feat(runtime): run tag-15 and tag-19 Control budgets
Docs Deploy / Build and Deploy MkDocs (push) Successful in 36s
Docs Deploy / Build and Deploy MkDocs (push) Successful in 36s
This commit is contained in:
5 files changed
+1143
-30
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -1791,6 +1791,7 @@ internal static class BehaviorDistributorTickOracle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void RunNativeControlResourceBudgetTag15Case(IntPtr module) {
|
private static void RunNativeControlResourceBudgetTag15Case(IntPtr module) {
|
||||||
|
RunNativeControlResourceBudgetEmptyCases(module);
|
||||||
NativeControlOwnerConstructor constructOwner = (NativeControlOwnerConstructor)Marshal.GetDelegateForFunctionPointer(
|
NativeControlOwnerConstructor constructOwner = (NativeControlOwnerConstructor)Marshal.GetDelegateForFunctionPointer(
|
||||||
IntPtr.Add(module, ControlOwnerConstructorRva), typeof(NativeControlOwnerConstructor));
|
IntPtr.Add(module, ControlOwnerConstructorRva), typeof(NativeControlOwnerConstructor));
|
||||||
NativeControlProviderRowConstructor constructProviderRow = (NativeControlProviderRowConstructor)
|
NativeControlProviderRowConstructor constructProviderRow = (NativeControlProviderRowConstructor)
|
||||||
@@ -1889,6 +1890,7 @@ internal static class BehaviorDistributorTickOracle {
|
|||||||
Marshal.WriteInt32(generator, 0x1c, 0);
|
Marshal.WriteInt32(generator, 0x1c, 0);
|
||||||
Marshal.WriteInt32(providerRow, 8, FloatBits(40.0f));
|
Marshal.WriteInt32(providerRow, 8, FloatBits(40.0f));
|
||||||
Marshal.WriteInt32(providerRow, 0x0c, FloatBits(40.0f));
|
Marshal.WriteInt32(providerRow, 0x0c, FloatBits(40.0f));
|
||||||
|
Marshal.WriteInt32(providerRow, 0x10, 0);
|
||||||
Marshal.WriteInt32(providerRow, 0x14, FloatBits(0.4f));
|
Marshal.WriteInt32(providerRow, 0x14, FloatBits(0.4f));
|
||||||
Marshal.WriteInt32(owner, 0x590, FloatBits(40.0f));
|
Marshal.WriteInt32(owner, 0x590, FloatBits(40.0f));
|
||||||
const float dt = 0.5f;
|
const float dt = 0.5f;
|
||||||
@@ -1941,6 +1943,7 @@ internal static class BehaviorDistributorTickOracle {
|
|||||||
Marshal.WriteInt32(nativeGenerator, 0x4c, FloatBits(1.0f));
|
Marshal.WriteInt32(nativeGenerator, 0x4c, FloatBits(1.0f));
|
||||||
Marshal.WriteInt32(providerRow, 8, FloatBits(40.0f));
|
Marshal.WriteInt32(providerRow, 8, FloatBits(40.0f));
|
||||||
Marshal.WriteInt32(providerRow, 0x0c, FloatBits(40.0f));
|
Marshal.WriteInt32(providerRow, 0x0c, FloatBits(40.0f));
|
||||||
|
Marshal.WriteInt32(providerRow, 0x10, 0);
|
||||||
Marshal.WriteInt32(providerRow, 0x14, FloatBits(0.4f));
|
Marshal.WriteInt32(providerRow, 0x14, FloatBits(0.4f));
|
||||||
Marshal.WriteInt32(owner, 0x590, FloatBits(40.0f));
|
Marshal.WriteInt32(owner, 0x590, FloatBits(40.0f));
|
||||||
NativeControlResourceDeviceRate generatorRate = (NativeControlResourceDeviceRate)
|
NativeControlResourceDeviceRate generatorRate = (NativeControlResourceDeviceRate)
|
||||||
@@ -1951,17 +1954,74 @@ internal static class BehaviorDistributorTickOracle {
|
|||||||
update(owner, 0.5f);
|
update(owner, 0.5f);
|
||||||
uint amount = unchecked((uint)Marshal.ReadInt32(providerRow, 8));
|
uint amount = unchecked((uint)Marshal.ReadInt32(providerRow, 8));
|
||||||
uint fraction = unchecked((uint)Marshal.ReadInt32(providerRow, 0x14));
|
uint fraction = unchecked((uint)Marshal.ReadInt32(providerRow, 0x14));
|
||||||
|
uint accumulated = unchecked((uint)Marshal.ReadInt32(providerRow, 0x10));
|
||||||
uint remaining = unchecked((uint)Marshal.ReadInt32(nativeGenerator, 0x4c));
|
uint remaining = unchecked((uint)Marshal.ReadInt32(nativeGenerator, 0x4c));
|
||||||
Console.WriteLine("native-control-real-generator-budget: ctor=0x22980 rate=0x229a0 update=0x22a60" +
|
Console.WriteLine("native-control-real-generator-budget: ctor=0x22980 rate=0x229a0 update=0x22a60" +
|
||||||
" cap=" + capped + " produced=" + Float(produced) + " amount=" + Hex(amount) +
|
" cap=" + capped + " produced=" + Float(produced) + " amount=" + Hex(amount) +
|
||||||
" ratio=" + Hex(fraction) + " remaining=" + Hex(remaining));
|
" accumulated=" + Hex(accumulated) + " ratio=" + Hex(fraction) + " remaining=" + Hex(remaining));
|
||||||
Expect(amount == (capped == 0 ? 0x42206666u : 0x42203333u) &&
|
Expect(amount == (capped == 0 ? 0x42206666u : 0x42203333u) &&
|
||||||
fraction == (capped == 0 ? 0x3ecd4fdfu : 0x3ecd0e56u) &&
|
fraction == (capped == 0 ? 0x3ecd4fdfu : 0x3ecd0e56u) &&
|
||||||
|
accumulated == (capped == 0 ? 0x3dcccc00u : 0x3d4ccc00u) &&
|
||||||
remaining == unchecked((uint)FloatBits(capped == 0 ? 0.75f : 0.0f)),
|
remaining == unchecked((uint)FloatBits(capped == 0 ? 0.75f : 0.0f)),
|
||||||
"real generator and consumer update exact provider and reserve values in one budget pass");
|
"real generator and consumer update exact provider amount, delta, ratio, and reserve in one budget pass");
|
||||||
GC.KeepAlive(generatorRate);
|
GC.KeepAlive(generatorRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Marshal.WriteInt32(typedRecord, 0x20, FloatBits(0.125f));
|
||||||
|
Marshal.WriteInt32(typedRecord, 0x2c, FloatBits(0.75f));
|
||||||
|
Marshal.WriteInt32(typedRecord, 0x30, FloatBits(1.25f));
|
||||||
|
Marshal.WriteInt32(nativeGeneratorRecord, 0x20, FloatBits(1.0f));
|
||||||
|
Marshal.WriteInt32(nativeGeneratorRecord, 0x2c, FloatBits(0.25f));
|
||||||
|
Expect(applyInto(owner, 1, typedRecord) == IntPtr.Add(typedRecord, 0xb4) &&
|
||||||
|
applyInto(owner, 0, nativeGeneratorRecord) == IntPtr.Add(nativeGeneratorRecord, 0xb4),
|
||||||
|
"native non-degenerate tag-19/tag-15 descriptor initializers");
|
||||||
|
Marshal.WriteInt32(device, 0x4c, FloatBits(0.8f));
|
||||||
|
Marshal.WriteInt32(nativeGenerator, 0x4c, FloatBits(1.0f));
|
||||||
|
Marshal.WriteInt32(providerRow, 8, FloatBits(40.0f));
|
||||||
|
Marshal.WriteInt32(providerRow, 0x0c, FloatBits(40.0f));
|
||||||
|
Marshal.WriteInt32(providerRow, 0x10, 0);
|
||||||
|
Marshal.WriteInt32(providerRow, 0x14, FloatBits(0.4f));
|
||||||
|
Marshal.WriteInt32(owner, 0x590, FloatBits(40.0f));
|
||||||
|
NativeControlResourceDeviceRate nativeGeneratorRate = (NativeControlResourceDeviceRate)
|
||||||
|
Marshal.GetDelegateForFunctionPointer(IntPtr.Add(module, 0x229a0), typeof(NativeControlResourceDeviceRate));
|
||||||
|
float nonDegenerateProduced = nativeGeneratorRate(nativeGenerator, 0.375f);
|
||||||
|
float nonDegenerateDemand = rate(device, 0.375f);
|
||||||
|
update(owner, 0.375f);
|
||||||
|
uint nonDegenerateAmount = unchecked((uint)Marshal.ReadInt32(providerRow, 8));
|
||||||
|
uint nonDegenerateProcessed = unchecked((uint)Marshal.ReadInt32(providerRow, 0x0c));
|
||||||
|
uint nonDegenerateAccumulated = unchecked((uint)Marshal.ReadInt32(providerRow, 0x10));
|
||||||
|
uint nonDegenerateRatio = unchecked((uint)Marshal.ReadInt32(providerRow, 0x14));
|
||||||
|
uint nonDegenerateConsumerMultiplier = unchecked((uint)Marshal.ReadInt32(device, 0x4c));
|
||||||
|
uint nonDegenerateGeneratorMultiplier = unchecked((uint)Marshal.ReadInt32(nativeGenerator, 0x4c));
|
||||||
|
uint nonDegenerateRate5dc = unchecked((uint)Marshal.ReadInt32(owner, 0x5dc));
|
||||||
|
uint nonDegenerateRate5e0 = unchecked((uint)Marshal.ReadInt32(owner, 0x5e0));
|
||||||
|
Expect(FloatBits(nonDegenerateProduced) == 0x3dcccccd && FloatBits(nonDegenerateDemand) == unchecked((int)0xbe400000) &&
|
||||||
|
nonDegenerateAmount == 0x42202b85 && nonDegenerateProcessed == 0x42202b85 &&
|
||||||
|
nonDegenerateAccumulated == 0x3d2e1400 && nonDegenerateRatio == 0x3ecd0481 &&
|
||||||
|
nonDegenerateConsumerMultiplier == 0x3f088889 && nonDegenerateGeneratorMultiplier == 0 &&
|
||||||
|
nonDegenerateRate5dc == 0x3e88888a && nonDegenerateRate5e0 == 0x3e88888a,
|
||||||
|
"native non-degenerate tag-19/tag-15 budget vector matches amount, delta, rates, and multipliers");
|
||||||
|
Console.WriteLine("native-control-real-complex-budget: dt=0.375 produced=" + Hex(unchecked((uint)FloatBits(nonDegenerateProduced))) +
|
||||||
|
" demand=" + Hex(unchecked((uint)FloatBits(nonDegenerateDemand))) +
|
||||||
|
" amount=" + Hex(nonDegenerateAmount) + " processed=" + Hex(nonDegenerateProcessed) +
|
||||||
|
" accumulated=" + Hex(nonDegenerateAccumulated) + " ratio=" + Hex(nonDegenerateRatio) +
|
||||||
|
" multipliers=" + Hex(nonDegenerateConsumerMultiplier) + "," + Hex(nonDegenerateGeneratorMultiplier) +
|
||||||
|
" rates=" + Hex(nonDegenerateRate5dc) + "," + Hex(nonDegenerateRate5e0));
|
||||||
|
GC.KeepAlive(nativeGeneratorRate);
|
||||||
|
|
||||||
|
// Restore the fixture that the subsequent native clock test uses.
|
||||||
|
Marshal.WriteInt32(typedRecord, 0x20, FloatBits(0.0f));
|
||||||
|
Marshal.WriteInt32(typedRecord, 0x2c, FloatBits(1.0f));
|
||||||
|
Marshal.WriteInt32(typedRecord, 0x30, FloatBits(1.0f));
|
||||||
|
Expect(applyInto(owner, 1, typedRecord) == IntPtr.Add(typedRecord, 0xb4),
|
||||||
|
"restore native tag-15 clock-test descriptor");
|
||||||
|
Marshal.WriteInt32(device, 0x4c, FloatBits(1.0f));
|
||||||
Marshal.WriteIntPtr(devices, 0, generator);
|
Marshal.WriteIntPtr(devices, 0, generator);
|
||||||
|
Marshal.WriteInt32(providerRow, 8, FloatBits(40.0f));
|
||||||
|
Marshal.WriteInt32(providerRow, 0x0c, FloatBits(40.0f));
|
||||||
|
Marshal.WriteInt32(providerRow, 0x10, 0);
|
||||||
|
Marshal.WriteInt32(providerRow, 0x14, FloatBits(0.4f));
|
||||||
|
Marshal.WriteInt32(owner, 0x590, FloatBits(40.0f));
|
||||||
GC.KeepAlive(constructGenerator);
|
GC.KeepAlive(constructGenerator);
|
||||||
|
|
||||||
// Bounded active class-10 host: no motion object, no deferred packets.
|
// Bounded active class-10 host: no motion object, no deferred packets.
|
||||||
@@ -2081,6 +2141,27 @@ internal static class BehaviorDistributorTickOracle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void RunNativeControlResourceBudgetEmptyCases(IntPtr module) {
|
||||||
|
NativeControlOwnerConstructor constructOwner = (NativeControlOwnerConstructor)
|
||||||
|
Marshal.GetDelegateForFunctionPointer(IntPtr.Add(module, ControlOwnerConstructorRva),
|
||||||
|
typeof(NativeControlOwnerConstructor));
|
||||||
|
NativeControlResourceBudgetUpdate update = (NativeControlResourceBudgetUpdate)
|
||||||
|
Marshal.GetDelegateForFunctionPointer(IntPtr.Add(module, ControlResourceBudgetUpdateRva),
|
||||||
|
typeof(NativeControlResourceBudgetUpdate));
|
||||||
|
using (Fixture fixture = NewFixture()) {
|
||||||
|
IntPtr owner = fixture.Allocate(0x664);
|
||||||
|
Expect(constructOwner(owner, IntPtr.Zero, 4) == owner, "native empty-budget owner constructor");
|
||||||
|
foreach (float dt in new[] { 0.0f, 0.5f }) {
|
||||||
|
update(owner, dt);
|
||||||
|
Expect(Marshal.ReadInt32(owner, 0x5dc) == unchecked((int)0x80000000) &&
|
||||||
|
Marshal.ReadInt32(owner, 0x5e0) == unchecked((int)0x80000000),
|
||||||
|
"native empty budget writes negative-zero output rates at dt=" + Float(dt));
|
||||||
|
}
|
||||||
|
Console.WriteLine("native-control-empty-budget: dt=0/0.5 rates=0x80000000,0x80000000");
|
||||||
|
}
|
||||||
|
GC.KeepAlive(update);
|
||||||
|
}
|
||||||
|
|
||||||
private static void RunNativeControlActivationCase(IntPtr module) {
|
private static void RunNativeControlActivationCase(IntPtr module) {
|
||||||
NativeControlOwnerConstructor construct = (NativeControlOwnerConstructor)Marshal.GetDelegateForFunctionPointer(
|
NativeControlOwnerConstructor construct = (NativeControlOwnerConstructor)Marshal.GetDelegateForFunctionPointer(
|
||||||
IntPtr.Add(module, 0x31490), typeof(NativeControlOwnerConstructor));
|
IntPtr.Add(module, 0x31490), typeof(NativeControlOwnerConstructor));
|
||||||
|
|||||||
@@ -358,10 +358,18 @@ API `distribute_loaded_mission_control_provider_amount` переносит
|
|||||||
`+0x10` получает фактическую разницу amount. После распределения обязателен
|
`+0x10` получает фактическую разницу amount. После распределения обязателен
|
||||||
`0x11130(root, true)`.
|
`0x11130(root, true)`.
|
||||||
|
|
||||||
Оба API работают только с CPU-проекцией и не подключены к GPU/window path или
|
Три API работают только с CPU-проекцией и не подключены к GPU/window path или
|
||||||
игровому Control clock. Gate `+0x618` изначально выключен; событие `7` задаёт его
|
игровому Control clock. Отдельный
|
||||||
по правилу `gate = (arg == 2)` и вызывает `0x2dc20`, только если gate изменился.
|
`advance_loaded_mission_control_budget` переносит `0x2d340` для factory
|
||||||
События Control в Rust пока не исполняются.
|
callbacks `15/19` и current descriptor tags `15/19` (категории `0/1`); ставки сохраняются
|
||||||
|
в текущих полях `+0x5dc/+0x5e0`. Он выполняется транзакционно, а неизвестный
|
||||||
|
callback, provider или диапазон числа возвращает ошибку без частичного
|
||||||
|
изменения. Тест с нулевым знаменателем генератора подтверждает откат после уже
|
||||||
|
выполненного изменения потребителем. Пустая таблица всё равно записывает в оба поля отрицательный ноль;
|
||||||
|
неактивный callback не требует provider lookup. Gate `+0x618` изначально
|
||||||
|
выключен; событие `7` задаёт его по правилу `gate = (arg == 2)` и вызывает
|
||||||
|
`0x2dc20`, только если gate изменился. События Control в Rust пока не
|
||||||
|
исполняются, поэтому ресурсный шаг вызывается явно и не подключён к часам.
|
||||||
Capacity aggregate выведен при подтверждённых единичных множителях Unit DAT;
|
Capacity aggregate выведен при подтверждённых единичных множителях Unit DAT;
|
||||||
изменение масштабов `0x9ee0`/`0x9f90` не реализовано. Из-за округления `+0x590`
|
изменение масштабов `0x9ee0`/`0x9f90` не реализовано. Из-за округления `+0x590`
|
||||||
может стать больше сохранённого `+0x58c`; распределение сохраняет этот signed
|
может стать больше сохранённого `+0x58c`; распределение сохраняет этот signed
|
||||||
@@ -394,8 +402,8 @@ Rust query обращается к загруженному объекту и п
|
|||||||
текущего descriptor tag `26` с factory tag `26`; другие активные typed tags и
|
текущего descriptor tag `26` с factory tag `26`; другие активные typed tags и
|
||||||
factory paths остаются явными границами. Запрос читает текущее type-1 значение.
|
factory paths остаются явными границами. Запрос читает текущее type-1 значение.
|
||||||
Licensed `min_l_n1.dat` подтверждает изменение `0xe00` и Behavior `0x1002` с
|
Licensed `min_l_n1.dat` подтверждает изменение `0xe00` и Behavior `0x1002` с
|
||||||
`1.0` до `0.5` после явного уменьшения количества вдвое; это ещё не
|
`1.0` до `0.5` после явного уменьшения количества вдвое; чтение получает
|
||||||
автоматический бюджетный цикл.
|
текущее значение provider, который также меняет явный бюджетный шаг.
|
||||||
Native oracle проверил изолированные пути распределения, обновления класса `4`,
|
Native oracle проверил изолированные пути распределения, обновления класса `4`,
|
||||||
стадийную арифметику `0x11220`, обход дочерних provider-строк `0x11130` на
|
стадийную арифметику `0x11220`, обход дочерних provider-строк `0x11130` на
|
||||||
interleaved parents `[-1, 0, 0, 1, 3]` с callback-порядком `1, 3, 4, 2` и
|
interleaved parents `[-1, 0, 0, 1, 3]` с callback-порядком `1, 3, 4, 2` и
|
||||||
@@ -406,15 +414,28 @@ visibility transition `0x11920` с исходными AniMesh callbacks. Отд
|
|||||||
в этом векторе подставлен mock-ом. Отдельный вектор с исходным генератором
|
в этом векторе подставлен mock-ом. Отдельный вектор с исходным генератором
|
||||||
tag `19` (`0x22980`, `0x229a0`, `0x22a60`) проверяет полную и ограниченную
|
tag `19` (`0x22980`, `0x229a0`, `0x22a60`) проверяет полную и ограниченную
|
||||||
rate, изменение provider и собственного множителя генератора; отрицательные
|
rate, изменение provider и собственного множителя генератора; отрицательные
|
||||||
параметры и нулевой знаменатель не проверены. Эти сценарии
|
параметры и нулевой знаменатель не проверены. Native-векторы бюджетного шага
|
||||||
используют синтетические NDP/Node38-строки; они подтверждают локальные переходы
|
подтверждают текущие ставки `+0x5dc/+0x5e0`, включая значения `0.2`/`0.4` у
|
||||||
и порядок вызовов, но не полную загрузку или выполнение Control на миссии. Вызовы
|
потребителя tag 15, пустой результат `-0.0` и невырожденную пару исходных
|
||||||
`0x11220` используют `updateEffects = false`, что исключает ветвь Control FX и
|
устройств tag 19/tag 15 при `dt = 0.375`; Rust-тест повторяет поля этой пары
|
||||||
|
на синтетической таблице. Реализация принимает только описанные factory и
|
||||||
|
category tags; остальные callbacks и состояния остаются явными ошибками.
|
||||||
|
Лицензированный `min_l_n1.dat` содержит исходные factory/current tags
|
||||||
|
`[19, 19, 9, 15, 12, 12, 13, 13, 26, 25, 21]`. Публичный API оставляет всю
|
||||||
|
таблицу без изменений при не конечном интервале и при первом неподдержанном
|
||||||
|
current tag `9`; полный бюджетный проход для этой реальной таблицы поэтому не
|
||||||
|
проверен. Успешные бюджетные native-векторы используют синтетические
|
||||||
|
NDP/Node38-строки и подтверждают локальные переходы и порядок вызовов, а не
|
||||||
|
полный Control tick на миссии. Прямые oracle-вызовы `0x11220` для одиночной
|
||||||
|
строки используют `updateEffects = false`, что исключает ветвь Control FX и
|
||||||
`0x11af0`; в отдельном сценарии класса `9` `0xfca0` возвращается сразу после
|
`0x11af0`; в отдельном сценарии класса `9` `0xfca0` возвращается сразу после
|
||||||
смены видимости, поэтому связанное с ним движение не проверено. Хотя у строки
|
смены видимости, поэтому связанное с ним движение не проверено. Хотя у строки
|
||||||
Control выставлен flag `0x102`, слово флагов ANI `+0x14` и производные байты
|
Control выставлен flag `0x102`, слово флагов ANI `+0x14` и производные байты
|
||||||
`+0x111..+0x113` изначально равны нулю. Вызовы AniMesh `0x93c0` и `0xa2a0` обновляют производную геометрию и
|
`+0x111..+0x113` изначально равны нулю. Вызовы AniMesh `0x93c0` и `0xa2a0` обновляют производную геометрию и
|
||||||
видимость, это отдельная непроверенная граница, а не FX callbacks. Бюджетная
|
видимость, это отдельная непроверенная граница, а не FX callbacks. Распределение
|
||||||
|
и бюджетный шаг вызывают обязательный `0x11130(root, true)`, но их проверенные
|
||||||
|
векторы имеют одну строку без смены стадии; они не доказывают исполнение FX или
|
||||||
|
`0x11af0`. Бюджетная
|
||||||
часть активного `0xbcf0` исполнена на синтетическом хосте класса `10`:
|
часть активного `0xbcf0` исполнена на синтетическом хосте класса `10`:
|
||||||
при периоде `500` и нулевом RNG проверены пропуск до deadline и на равенстве
|
при периоде `500` и нулевом RNG проверены пропуск до deadline и на равенстве
|
||||||
`437.5`, затем ресурсный `dt = 0.5` при времени `500`. На том же ограниченном
|
`437.5`, затем ресурсный `dt = 0.5` при времени `500`. На том же ограниченном
|
||||||
|
|||||||
@@ -752,8 +752,8 @@ amount, accumulator, aggregate или stage изменений.
|
|||||||
Поле `+0x618` изначально выключено; обработчик события `7` задаёт gate по
|
Поле `+0x618` изначально выключено; обработчик события `7` задаёт gate по
|
||||||
правилу `gate = (arg == 2)` и вызывает пересчёт `0x2dc20`, только если gate
|
правилу `gate = (arg == 2)` и вызывает пересчёт `0x2dc20`, только если gate
|
||||||
изменился. Проекция сохраняет начальное состояние и уважает установленную
|
изменился. Проекция сохраняет начальное состояние и уважает установленную
|
||||||
suspension-метку. События Control пока не исполняются,
|
suspension-метку. События Control пока не исполняются, а ресурсный budget API
|
||||||
поэтому это API остаётся явным CPU-шагом и не подключено к игровому clock.
|
вызывается явно и пока не подключён к игровому clock.
|
||||||
Тесты Rust повторяют пропорциональный вектор, накопитель `+0x10`, округление
|
Тесты Rust повторяют пропорциональный вектор, накопитель `+0x10`, округление
|
||||||
`2^24`, sequential capacity boundary, достижимый negative capacity и откат при
|
`2^24`, sequential capacity boundary, достижимый negative capacity и откат при
|
||||||
unsupported FX/lifecycle.
|
unsupported FX/lifecycle.
|
||||||
@@ -776,18 +776,37 @@ descriptor `+0x20`. Исходная rate-функция возвращает `-
|
|||||||
вклад `0.1` или `0.5` и записывает аргументы своего update. Функции tag 15,
|
вклад `0.1` или `0.5` и записывает аргументы своего update. Функции tag 15,
|
||||||
provider getter и изменение количества не подменены.
|
provider getter и изменение количества не подменены.
|
||||||
|
|
||||||
| Бюджет | Коэффициент tag 15 | Amount / processed / total, биты | Доля, биты |
|
| Бюджет | Коэффициент tag 15 | Amount / processed / total, биты | Доля, биты | `+0x5dc` / `+0x5e0`, биты |
|
||||||
|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| `0.1` | `0.5` | `0x42206666` (`40.1`) | `0x3ecd4fdf` |
|
| `0.1` | `0.5` | `0x42206666` (`40.1`) | `0x3ecd4fdf` | `0x3e4ccccd` / `0x3e4ccccd` (`0.2`) |
|
||||||
| `0.5` | `1` | `0x4220cccd` (`40.2`) | `0x3ecdd2f2` |
|
| `0.5` | `1` | `0x4220cccd` (`40.2`) | `0x3ecdd2f2` | `0x3ecccccd` / `0x3ecccccd` (`0.4`) |
|
||||||
|
|
||||||
Сравнение идёт по битам при x87 control word `0x007f`. Реальный update
|
Сравнение идёт по битам при x87 control word `0x007f`. Реальный update
|
||||||
вызывает RVA `0x10ba0` и публикует новую долю одним callback selector `0x200`.
|
вызывает RVA `0x10ba0` и публикует новую долю одним callback selector `0x200`.
|
||||||
Хотя генератор стоит первым в массиве, его update уже видит изменённое
|
Хотя генератор стоит первым в массиве, его update уже видит изменённое
|
||||||
количество: категория tag 15 обслуживается раньше. Проверены также интервал
|
количество: категория tag 15 обслуживается раньше. Проверены также интервал
|
||||||
и коэффициент, переданные mock-генератору. Его собственная генерация, другие
|
и коэффициент, переданные mock-генератору. Его собственная генерация, другие
|
||||||
категории и выполнение миссии этим не проверены.
|
категории и выполнение миссии этим не проверены. Исходные выходные ставки
|
||||||
Rust пока не переносит этот изменяемый бюджетный цикл.
|
`+0x5dc` и `+0x5e0` также записываются на каждом вызове; при пустой таблице
|
||||||
|
оба поля получают битовый отрицательный ноль.
|
||||||
|
|
||||||
|
Rust предоставляет явный CPU-вызов
|
||||||
|
`advance_loaded_mission_control_budget(engine, handle, dt)`. Он переносит
|
||||||
|
диспетчер и callbacks исходных factory tags `15` и `19`, если текущий
|
||||||
|
descriptor tag равен `15` или `19` (native categories `0` или `1`). Rates `+0x5dc/+0x5e0`
|
||||||
|
сохраняются отдельно. Вызов работает на копии полного состояния: неизвестный
|
||||||
|
descriptor, factory callback, provider или численный диапазон возвращает
|
||||||
|
ошибку без публикации частичных изменений. Вызов ещё не включён в Control clock;
|
||||||
|
нативный oracle также проверяет пустую таблицу с отрицательным нулём в обеих
|
||||||
|
выходных ставках и невырожденную пару исходных устройств tag 19/tag 15 при
|
||||||
|
`dt = 0.375`. Этот парный вектор проверяет изменения amount, processed amount,
|
||||||
|
накопителя, доли, обоих множителей и ставок `+0x5dc/+0x5e0` по битам. Rust-тест
|
||||||
|
повторяет его на синтетической provider-таблице. Для лицензированного
|
||||||
|
`min_l_n1.dat` отдельный тест подтверждает состав factory/current tags и то, что
|
||||||
|
первый неподдержанный current tag `9` отклоняет весь шаг без изменений; полного
|
||||||
|
успешного бюджетного прохода для реальной миссионной таблицы пока нет.
|
||||||
|
Rust также отклоняет нулевой знаменатель обновления tag 19; тест вызывает эту
|
||||||
|
ошибку после изменения provider потребителем и подтверждает откат всей таблицы.
|
||||||
|
|
||||||
#### Генератор tag 19 в том же бюджетном проходе
|
#### Генератор tag 19 в том же бюджетном проходе
|
||||||
|
|
||||||
@@ -814,8 +833,18 @@ provider уже после потребителя tag `15`, повторно в
|
|||||||
собственный множитель. Это не запись коэффициента выделения в `+0x4c`, как у
|
собственный множитель. Это не запись коэффициента выделения в `+0x4c`, как у
|
||||||
tag `15`: смысл изменения этого поля зависит от фабричного класса.
|
tag `15`: смысл изменения этого поля зависит от фабричного класса.
|
||||||
Векторы проверяют конечные положительные значения при x87 CW `0x007f`;
|
Векторы проверяют конечные положительные значения при x87 CW `0x007f`;
|
||||||
нулевой знаменатель, отрицательные параметры и другие классы устройств ими
|
нативный oracle дополнительно фиксирует невырожденный случай, в котором
|
||||||
не покрыты. Перенос общего бюджетного цикла в Rust ещё не завершён.
|
коэффициенты различаются и поле `+0x20` ненулевое. Нулевой знаменатель,
|
||||||
|
отрицательные параметры и другие классы устройств остаются непокрытыми.
|
||||||
|
В этом дополнительном native-векторе `dt = 0.375`, provider `40/100`, tag 19
|
||||||
|
имеет `B = 1`, `C0 = 0.25`, а tag 15 — `B = 0.125`, `C0 = 0.75`, `C1 = 1.25`
|
||||||
|
и начальный множитель `0.8`. Получены rate генератора `0x3dcccccd`, rate
|
||||||
|
потребителя `0xbe400000`, amount `0x42202b85`, ratio `0x3ecd0481`, накопитель
|
||||||
|
`+0x10 = 0x3d2e1400`, множитель tag 15 `0x3f088889`, множитель tag 19 `0`, а
|
||||||
|
обе выходные ставки равны `0x3e88888a`. Это проверяет раздельное чтение
|
||||||
|
коэффициента `C1` и множителя `+0x4c`, а также ненулевую базовую стоимость.
|
||||||
|
Поддержанные Rust-векторы не означают, что любой Control descriptor или
|
||||||
|
полный миссионный цикл уже работает.
|
||||||
|
|
||||||
#### Активация и начальные часы
|
#### Активация и начальные часы
|
||||||
|
|
||||||
|
|||||||
@@ -1051,6 +1051,26 @@ Rust переносит этот порядок, сохраняет native на
|
|||||||
реализованные FX или lifecycle-вызовы, таблица остаётся без изменений. Эти
|
реализованные FX или lifecycle-вызовы, таблица остаётся без изменений. Эти
|
||||||
операции обновляют только CPU-снимок и пока не подключены к игровым часам Control.
|
операции обновляют только CPU-снимок и пока не подключены к игровым часам Control.
|
||||||
|
|
||||||
|
Распределение по бюджету тоже доступно как отдельный CPU-вызов
|
||||||
|
`advance_loaded_mission_control_budget`. Он сначала собирает положительные
|
||||||
|
ставки, затем обслуживает категории в исходном порядке и передаёт каждому
|
||||||
|
устройству его долю. Например, при запасе `40/100`, доле `0.4` и интервале
|
||||||
|
`0.5` секунды tag 19 может принести `0.1`, а tag 15 запросить `0.2`.
|
||||||
|
Потребитель получает половину запроса: запас становится `40.1`, доля — `0.401`,
|
||||||
|
а множитель генератора уменьшается до `0.75`. Хотя генератор может стоять раньше
|
||||||
|
в массиве, его обновление перечитывает уже изменённую долю после обслуживания
|
||||||
|
категории tag 15. Этот явный вызов хранит также две выходные ставки `+0x5dc` и
|
||||||
|
`+0x5e0`, но сам по себе ещё не запускается игровыми часами.
|
||||||
|
Поддержаны factory callbacks `15/19` и текущие descriptor tags `15/19`
|
||||||
|
(категории `0/1`); неизвестное устройство,
|
||||||
|
недоступные данные provider или нулевой знаменатель останавливают весь шаг без
|
||||||
|
частичных изменений.
|
||||||
|
Другие категории и полный миссионный цикл пока не подтверждены. В лицензированном
|
||||||
|
`min_l_n1.dat` рядом с тегами `19` и `15` есть также factory tags `9`, `12`,
|
||||||
|
`13`, `21`, `25` и `26`; поэтому его полный проход пока явно отклоняется на
|
||||||
|
первой неподдержанной категории `9`. Проверка сохраняет всю таблицу без
|
||||||
|
изменений и не доказывает успешный бюджетный шаг на реальной миссии.
|
||||||
|
|
||||||
Отдельный путь объекта класса `4` умножает интервал на контактный коэффициент и
|
Отдельный путь объекта класса `4` умножает интервал на контактный коэффициент и
|
||||||
передаёт изменение строке. В проверенном примере коэффициент `1`, интервал `2`,
|
передаёт изменение строке. В проверенном примере коэффициент `1`, интервал `2`,
|
||||||
и значение строки уменьшается на `2`; этот коэффициент не является ставкой
|
и значение строки уменьшается на `2`; этот коэффициент не является ставкой
|
||||||
@@ -1064,10 +1084,10 @@ Control заново разыгрывает срок обновления, да
|
|||||||
исходная DLL пропустила времена `500` и `501`, затем выполнила ресурсный шаг
|
исходная DLL пропустила времена `500` и `501`, затем выполнила ресурсный шаг
|
||||||
на `502`. Выбрать случайный срок один раз и ждать его было бы неверно.
|
на `502`. Выбрать случайный срок один раз и ждать его было бы неверно.
|
||||||
Принудительный шаг тоже расходует RNG; неактивный объект синхронизирует часы
|
Принудительный шаг тоже расходует RNG; неактивный объект синхронизирует часы
|
||||||
без такого расхода. Это локальные native проверки, а не работающий Rust
|
без такого расхода. Это локальные native проверки, а не автоматический Rust
|
||||||
Control: запросы Rust `0xe00` и `0x1002` читают текущее состояние строк,
|
Control tick: запросы Rust `0xe00` и `0x1002` читают текущее состояние строк,
|
||||||
которое меняют явные API одной строки и распределения, а `0x1005` — загруженную
|
которое меняют явные API одной строки, распределения и бюджетного шага, а
|
||||||
ставку профиля.
|
`0x1005` — загруженную ставку профиля.
|
||||||
Точные нативные векторы и открытые границы описаны в [приложении](../appendices/script-vm.md#изменение-provider-состояния-control).
|
Точные нативные векторы и открытые границы описаны в [приложении](../appendices/script-vm.md#изменение-provider-состояния-control).
|
||||||
|
|
||||||
На x87 control word `0x007f` каждое умножение округляет мантиссу до 24 бит,
|
На x87 control word `0x007f` каждое умножение округляет мантиссу до 24 бит,
|
||||||
|
|||||||
Reference in new issue
Block a user