test(runtime): verify native generator budget consumption
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:
3 files changed
+79
-1
No files matched your search
@@ -1781,6 +1781,53 @@ internal static class BehaviorDistributorTickOracle {
|
|||||||
Marshal.ReadInt32(generator, 0x18) == expectedAmounts[i],
|
Marshal.ReadInt32(generator, 0x18) == expectedAmounts[i],
|
||||||
"native category priority updates tag 15 before the earlier stored generator");
|
"native category priority updates tag 15 before the earlier stored generator");
|
||||||
}
|
}
|
||||||
|
NativeControlResourceDeviceConstructor constructGenerator = (NativeControlResourceDeviceConstructor)
|
||||||
|
Marshal.GetDelegateForFunctionPointer(IntPtr.Add(module, 0x22980), typeof(NativeControlResourceDeviceConstructor));
|
||||||
|
IntPtr nativeGenerator = fixture.Allocate(0x94);
|
||||||
|
IntPtr nativeGeneratorRecord = fixture.Allocate(0xb4);
|
||||||
|
constructGenerator(nativeGenerator);
|
||||||
|
IntPtr nativeGeneratorVtable = Marshal.ReadIntPtr(nativeGenerator);
|
||||||
|
Expect(nativeGeneratorVtable == IntPtr.Add(module, 0x3c608) &&
|
||||||
|
Marshal.ReadIntPtr(nativeGeneratorVtable, 0x14) == IntPtr.Add(module, 0x229a0) &&
|
||||||
|
Marshal.ReadIntPtr(nativeGeneratorVtable, 0x18) == IntPtr.Add(module, 0x22a60),
|
||||||
|
"native tag-19 generator keeps its original rate and update methods");
|
||||||
|
Marshal.WriteIntPtr(nativeGenerator, 0x3c, owner);
|
||||||
|
Marshal.WriteIntPtr(nativeGenerator, 0x44, IntPtr.Add(owner, 4));
|
||||||
|
Marshal.WriteInt32(nativeGeneratorRecord, 0, 19);
|
||||||
|
Marshal.WriteInt32(nativeGeneratorRecord, 0x18, -1); // Preserve constructor's enabled word 0x20.
|
||||||
|
Marshal.WriteInt32(nativeGeneratorRecord, 0x20, FloatBits(0.5f));
|
||||||
|
Marshal.WriteIntPtr(devices, 0, nativeGenerator);
|
||||||
|
for (int capped = 0; capped < 2; capped++) {
|
||||||
|
Marshal.WriteInt32(nativeGeneratorRecord, 0x2c, FloatBits(capped == 0 ? 1.0f : 0.125f));
|
||||||
|
Expect(applyInto(owner, 0, nativeGeneratorRecord) == IntPtr.Add(nativeGeneratorRecord, 0xb4),
|
||||||
|
"native tag-19 common record initializer");
|
||||||
|
Expect(Marshal.ReadInt32(nativeGenerator, 0x50) == 0x20, "tag-19 constructor enable word is preserved");
|
||||||
|
Marshal.WriteInt32(nativeGenerator, 0x4c, FloatBits(1.0f));
|
||||||
|
Marshal.WriteInt32(providerRow, 8, FloatBits(40.0f));
|
||||||
|
Marshal.WriteInt32(providerRow, 0x0c, FloatBits(40.0f));
|
||||||
|
Marshal.WriteInt32(providerRow, 0x14, FloatBits(0.4f));
|
||||||
|
Marshal.WriteInt32(owner, 0x590, FloatBits(40.0f));
|
||||||
|
NativeControlResourceDeviceRate generatorRate = (NativeControlResourceDeviceRate)
|
||||||
|
Marshal.GetDelegateForFunctionPointer(IntPtr.Add(module, 0x229a0), typeof(NativeControlResourceDeviceRate));
|
||||||
|
float produced = generatorRate(nativeGenerator, 0.5f);
|
||||||
|
Expect(FloatBits(produced) == FloatBits(capped == 0 ? 0.1f : 0.05f),
|
||||||
|
"real generator rate applies provider ratio and optional coefficient cap");
|
||||||
|
update(owner, 0.5f);
|
||||||
|
uint amount = unchecked((uint)Marshal.ReadInt32(providerRow, 8));
|
||||||
|
uint fraction = unchecked((uint)Marshal.ReadInt32(providerRow, 0x14));
|
||||||
|
uint remaining = unchecked((uint)Marshal.ReadInt32(nativeGenerator, 0x4c));
|
||||||
|
Console.WriteLine("native-control-real-generator-budget: ctor=0x22980 rate=0x229a0 update=0x22a60" +
|
||||||
|
" cap=" + capped + " produced=" + Float(produced) + " amount=" + Hex(amount) +
|
||||||
|
" ratio=" + Hex(fraction) + " remaining=" + Hex(remaining));
|
||||||
|
Expect(amount == (capped == 0 ? 0x42206666u : 0x42203333u) &&
|
||||||
|
fraction == (capped == 0 ? 0x3ecd4fdfu : 0x3ecd0e56u) &&
|
||||||
|
remaining == unchecked((uint)FloatBits(capped == 0 ? 0.75f : 0.0f)),
|
||||||
|
"real generator and consumer update exact provider and reserve values in one budget pass");
|
||||||
|
GC.KeepAlive(generatorRate);
|
||||||
|
}
|
||||||
|
Marshal.WriteIntPtr(devices, 0, generator);
|
||||||
|
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.
|
||||||
// These host getters are mocks; the clock, deadline and budget code are native.
|
// These host getters are mocks; the clock, deadline and budget code are native.
|
||||||
IntPtr host = fixture.Allocate(4);
|
IntPtr host = fixture.Allocate(4);
|
||||||
|
|||||||
@@ -348,7 +348,10 @@ visibility transition `0x11920` с исходными AniMesh callbacks. Отд
|
|||||||
бюджетный вектор исполняет `0x2d340` → `0x2dca0` → tag 15 `0x22bb0` →
|
бюджетный вектор исполняет `0x2d340` → `0x2dca0` → tag 15 `0x22bb0` →
|
||||||
`0x10ba0`: подтверждены коэффициенты `0.5` и `1`, точные amount/ratio и
|
`0x10ba0`: подтверждены коэффициенты `0.5` и `1`, точные amount/ratio и
|
||||||
приоритет категории перед порядком устройств. Положительный вклад генератора
|
приоритет категории перед порядком устройств. Положительный вклад генератора
|
||||||
в этом векторе подставлен mock-ом; его собственная генерация не проверена. Эти сценарии
|
в этом векторе подставлен mock-ом. Отдельный вектор с исходным генератором
|
||||||
|
tag `19` (`0x22980`, `0x229a0`, `0x22a60`) проверяет полную и ограниченную
|
||||||
|
rate, изменение provider и собственного множителя генератора; отрицательные
|
||||||
|
параметры и нулевой знаменатель не проверены. Эти сценарии
|
||||||
используют синтетические NDP/Node38-строки; они подтверждают локальные переходы
|
используют синтетические NDP/Node38-строки; они подтверждают локальные переходы
|
||||||
и порядок вызовов, но не полную загрузку или выполнение Control на миссии. Вызовы
|
и порядок вызовов, но не полную загрузку или выполнение Control на миссии. Вызовы
|
||||||
`0x11220` используют `updateEffects = false`, что исключает ветвь Control FX и
|
`0x11220` используют `updateEffects = false`, что исключает ветвь Control FX и
|
||||||
|
|||||||
@@ -686,6 +686,34 @@ provider getter и изменение количества не подменен
|
|||||||
категории и выполнение миссии этим не проверены.
|
категории и выполнение миссии этим не проверены.
|
||||||
Rust пока не переносит этот изменяемый бюджетный цикл.
|
Rust пока не переносит этот изменяемый бюджетный цикл.
|
||||||
|
|
||||||
|
#### Генератор tag 19 в том же бюджетном проходе
|
||||||
|
|
||||||
|
Отдельный вектор заменяет mock-генератор исходным устройством tag `19`:
|
||||||
|
конструктор `Control.dll` RVA `0x22980`, rate `0x229a0`, update `0x22a60`,
|
||||||
|
vtable `0x3c608`. Конструктор выставляет слово включения `0x20`; descriptor
|
||||||
|
с `+0x18 = -1` сохраняет его. Оба устройства проходят через исходные
|
||||||
|
`0x2d340` и `0x2dca0`, их provider и изменение количества тоже исходные.
|
||||||
|
|
||||||
|
При положительных параметрах проверенного вектора выработка зависит от поля
|
||||||
|
descriptor `+0x20`, текущего множителя `+0x4c`, доли provider и `dt`. Если
|
||||||
|
коэффициент selector `0` положителен, rate ограничивается значением selector
|
||||||
|
`0x200`, умноженным на долю provider. Поэтому один и тот же источник может
|
||||||
|
вернуть полную выработку или её ограниченный запас.
|
||||||
|
|
||||||
|
| Коэффициент | Поле `+0x20` | Исходная rate | Amount после прохода | Доля provider | Множитель генератора после прохода |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| `1` | `0.5` | `0.1` | `0x42206666` | `0x3ecd4fdf` | `0x3f400000` (`0.75`) |
|
||||||
|
| `0.125` | `0.5` | `0.05` | `0x42203333` | `0x3ecd0e56` | `0` |
|
||||||
|
|
||||||
|
В обоих случаях исходные amount, максимум, доля и интервал равны `40`, `100`,
|
||||||
|
`0.4` и `0.5`, а исходный множитель генератора — `1`. Его update читает
|
||||||
|
provider уже после потребителя tag `15`, повторно вычисляет rate и уменьшает
|
||||||
|
собственный множитель. Это не запись коэффициента выделения в `+0x4c`, как у
|
||||||
|
tag `15`: смысл изменения этого поля зависит от фабричного класса.
|
||||||
|
Векторы проверяют конечные положительные значения при x87 CW `0x007f`;
|
||||||
|
нулевой знаменатель, отрицательные параметры и другие классы устройств ими
|
||||||
|
не покрыты. Перенос общего бюджетного цикла в Rust ещё не завершён.
|
||||||
|
|
||||||
#### Активация и начальные часы
|
#### Активация и начальные часы
|
||||||
|
|
||||||
Для Control класса `4` oracle вызывает исходный конструктор RVA `0x31490`
|
Для Control класса `4` oracle вызывает исходный конструктор RVA `0x31490`
|
||||||
|
|||||||
Reference in new issue
Block a user