Skip to content

Commit 5d4f653

Browse files
Suballocator refactor: Move it to the Vulkan device metadata
We now have one memory suballocator for each Vulkan device, allowing better support for esoteric scenarios with multiple devices. More importantly, we now have all the data we need for the bigger rewrite. We currently preallocate some space for all the traced objects on every device. This is incorrect, but we will fix this later.
1 parent c5b5826 commit 5d4f653

25 files changed

Lines changed: 229 additions & 175 deletions

scripts/lava.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def out(lst, str=''):
7777
out(targets_read, '#include "vulkan/vulkan.h"')
7878
out(targets_read, '#include "window.h"')
7979
out(targets_read, '#include "suballocator.h"')
80+
out(targets_read, '#include "memory.h"')
8081
out([uh,wrh], '#include <unordered_map>')
8182
out([uh,wrh], '#include "vulkan_feature_detect.h"')
8283
out([r], '#include "read_auto.h"')
@@ -383,11 +384,8 @@ def out(lst, str=''):
383384
out(targets_read_headers, 'trackable& object_trackable(VkObjectType type, uint64_t handle);')
384385
out(targets_read_headers)
385386

386-
out(targets_read, 'void retrace_init(lava_reader& replayer, const Json::Value& v, int heap_size, bool run)')
387+
out(targets_read, 'void retrace_init(lava_reader& replayer, const Json::Value& v)')
387388
out(targets_read, '{')
388-
out(targets_read, '\tint images = 0;')
389-
out(targets_read, '\tint buffers = 0;')
390-
out(targets_read, '\tint tensors = 0;')
391389
out(targets_read, '\tfor (const auto& p : v.getMemberNames())')
392390
out(targets_read, '\t{')
393391
for v in spec.root.findall('types/type'):
@@ -403,17 +401,10 @@ def out(lst, str=''):
403401
out(targets_read, '\t\t\tif (v[p].asInt() == 0) ELOG("No Vulkan instances recorded. Broken trace file!");')
404402
if v.find('name').text == 'VkSurfaceKHR':
405403
out(targets_read, '\t\t\twindow_preallocate(v[p].asInt());')
406-
if v.find('name').text == 'VkImage':
407-
out(targets_read, '\t\t\timages = v[p].asInt();')
408-
if v.find('name').text == 'VkBuffer':
409-
out(targets_read, '\t\t\tbuffers = v[p].asInt();')
410-
if v.find('name').text == 'VkTensorARM':
411-
out(targets_read, '\t\t\ttensors = v[p].asInt();')
412404
out(targets_read, '\t\t}')
413405
out(targets_read, '\t}')
414-
out(targets_read, '\treplayer.allocator.init(images, buffers, tensors, heap_size, !run);')
415406
out(targets_read, '}')
416-
out(targets_read_headers, 'void retrace_init(lava_reader& replayer, const Json::Value& v, int heap_size = -1, bool run = true);')
407+
out(targets_read_headers, 'void retrace_init(lava_reader& replayer, const Json::Value& v);')
417408

418409
out(targets_write)
419410
out(targets_write, 'Json::Value trace_limits(const lava_writer* instance)')

scripts/util.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,9 @@ def print_load(self, name, owner): # called for each parameter
573573

574574
if self.funcname in ['vkBindImageMemory', 'vkBindBufferMemory', 'VkBindBufferMemoryInfo', 'VkBindBufferMemoryInfoKHR', 'VkBindImageMemoryInfoKHR', 'VkBindImageMemoryInfo', 'VkBindTensorMemoryInfoARM']:
575575
if self.name in ['image', 'buffer', 'tensor']:
576+
if self.funcname in ['VkBindBufferMemoryInfo', 'VkBindBufferMemoryInfoKHR', 'VkBindImageMemoryInfoKHR', 'VkBindImageMemoryInfo', 'VkBindTensorMemoryInfoARM']:
577+
z.do('const uint32_t device_index = index_to_VkDevice.index(reader.device);')
578+
z.do('%s& %s = VkDevice_index.at(%s);' % (vk.trackable_type_map_replay['VkDevice'], totrackable('VkDevice'), toindex('VkDevice')))
576579
z.do('%s& %s = %s_index.at(%s);' % (vk.trackable_type_map_replay[self.type], totrackable(self.type), self.type, toindex(self.type)))
577580
z.do('%s.memory_flags = static_cast<VkMemoryPropertyFlags>(reader.read_uint32_t()); // fetch memory flags especially added' % totrackable(self.type)) # TBD remove
578581
if self.funcname in ['vkBindImageMemory', 'VkBindImageMemoryInfoKHR', 'VkBindImageMemoryInfo'] and self.name == 'image': # TBD remove me
@@ -583,17 +586,22 @@ def print_load(self, name, owner): # called for each parameter
583586
z.do('memory_requirements reqs;')
584587
z.do('if (reader.run) reqs = get_%s_memory_requirements(reader.device, %s);' % (vk.trackable_type_map_replay[self.type], totrackable(self.type)))
585588
z.do('else reqs = get_fake_memory_requirements(reader.device, %s);' % totrackable(self.type))
586-
z.do('suballoc_location loc = reader.parent->allocator.add_trackedobject(reader.thread_index(), reader.device, reqs, (uint64_t)%s, %s);' % (varname, totrackable(self.type)))
589+
z.do('suballoc_location loc = device_data.allocator->add_trackedobject(reader.thread_index(), reqs, (uint64_t)%s, %s);' % (varname, totrackable(self.type)))
587590
if self.name == 'memory':
588591
z.do('assert(loc.memory != VK_NULL_HANDLE);')
589592
z.do('%s = loc.memory;' % varname) # relying on the order of arguments here; see case above
590593
elif self.name == 'memoryOffset':
591594
z.do('%s = loc.offset;' % varname) # relying on the order of arguments here; see case above
592595

596+
if self.funcname in ['vkDestroyBuffer', 'vkDestroyImage', 'vkDestroyTensorARM', 'vkDestroyDevice', 'VkCreateDevice'] and self.name == 'device': #['image', 'buffer', 'tensor', 'device']:
597+
z.do('%s& %s = %s_index.at(%s);' % (vk.trackable_type_map_replay['VkDevice'], totrackable('VkDevice'), self.type, toindex('VkDevice')))
598+
593599
if self.funcname == 'vkDestroyBuffer' and self.name == 'buffer':
594-
z.do('if (buffer_index != CONTAINER_INVALID_INDEX) reader.parent->allocator.free_buffer(buffer_index);')
600+
z.do('if (buffer_index != CONTAINER_INVALID_INDEX) device_data.allocator->free_buffer(buffer_index);')
595601
elif self.funcname == 'vkDestroyImage' and self.name == 'image':
596-
z.do('reader.parent->allocator.free_image(image_index);')
602+
z.do('device_data.allocator->free_image(image_index);')
603+
elif self.funcname == 'vkDestroyTensorARM' and self.name == 'tensor':
604+
z.do('device_data.allocator->free_tensor(tensorarm_index);')
597605
elif self.name == 'sType':
598606
orig = z.struct_last()
599607
stype = spec.type2sType[orig]
@@ -1205,7 +1213,13 @@ def load_add_pre(name):
12051213
count = spec.functions_destroy[name][1]
12061214
type = spec.functions_destroy[name][2]
12071215
if type == 'VkDevice':
1208-
z.do('reader.parent->allocator.destroy(device);')
1216+
z.do('device_data.allocator->self_test();')
1217+
z.do('device_data.allocator->destroy();')
1218+
z.do('suballoc_metrics sm = device_data.allocator->performance();')
1219+
z.do('ILOG("Suballocator used=%lu allocated=%lu heaps=%u objects=%u efficiency=%g", (unsigned long)sm.used, (unsigned long)sm.allocated, (unsigned)sm.heaps, (unsigned)sm.objects, sm.efficiency);')
1220+
z.do('assert(device_data.allocator->self_test() == 0);')
1221+
z.do('delete device_data.allocator;')
1222+
z.do('device_data.allocator = nullptr;')
12091223

12101224
if name == 'vkCreatePipelineCache': # cannot be autogenerated, need the index parameter
12111225
z.do('if (reader.run) replay_pre_vkCreatePipelineCache(reader, device, device_index, pCreateInfo, pipelinecache_index);')
@@ -1234,7 +1248,8 @@ def load_add_tracking(name):
12341248
z.do('data.device = device;')
12351249
elif type == 'VkDevice':
12361250
z.do('data.physicalDevice = physicalDevice; // track parentage')
1237-
z.do('reader.parent->allocator.setup(selected_physical_device);')
1251+
z.do('data.allocator = new suballocator();')
1252+
z.do('data.allocator->create(selected_physical_device, pDevice, VkImage_index, VkBuffer_index, VkTensorARM_index, reader.run);')
12381253
elif type == 'VkImage':
12391254
z.do('data.tiling = (lava_tiling)pCreateInfo->tiling;')
12401255
z.do('data.usage = pCreateInfo->usage;')

src/execute_commands.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
static bool run_spirv(lava_file_reader& reader, const trackedpipeline& pipeline_data, const shader_stage& stage, const std::vector<std::byte>& push_constants,
1+
static bool run_spirv(const trackeddevice& device_data, const trackedpipeline& pipeline_data, const shader_stage& stage, const std::vector<std::byte>& push_constants,
22
const std::unordered_map<uint32_t, std::unordered_map<uint32_t, buffer_access>>& descriptorsets)
33
{
4-
assert(!reader.run); // this code is only run when post-processing
54
assert(stage.module != VK_NULL_HANDLE);
65
const uint32_t shader_index = index_to_VkShaderModule.index(stage.module);
76
trackedshadermodule& shader_data = VkShaderModule_index.at(shader_index);
@@ -22,7 +21,7 @@ static bool run_spirv(lava_file_reader& reader, const trackedpipeline& pipeline_
2221
const buffer_access& access = binding_pair.second;
2322
if (!access.buffer_data) continue;
2423
const uint32_t buffer_index = access.buffer_data->index;
25-
suballoc_location loc = reader.parent->allocator.find_buffer_memory(buffer_index);
24+
suballoc_location loc = device_data.allocator->find_buffer_memory(buffer_index);
2625
std::byte* base = (std::byte*)loc.memory;
2726
std::byte* binding_ptr = base + access.offset;
2827
set_bindings[binding_pair.first] = binding_ptr;
@@ -73,8 +72,9 @@ static bool run_spirv(lava_file_reader& reader, const trackedpipeline& pipeline_
7372
return true;
7473
}
7574

76-
static bool execute_commands(lava_file_reader& reader, VkCommandBuffer commandBuffer)
75+
static bool execute_commands(lava_file_reader& reader, const trackeddevice& device_data, VkCommandBuffer commandBuffer)
7776
{
77+
assert(!reader.run); // this code is only run when post-processing
7878
std::vector<std::byte> push_constants; // current state of the push constants
7979
uint32_t compute_pipeline_bound = CONTAINER_INVALID_INDEX; // currently bound pipeline
8080
uint32_t graphics_pipeline_bound = CONTAINER_INVALID_INDEX; // currently bound pipeline
@@ -117,8 +117,8 @@ static bool execute_commands(lava_file_reader& reader, VkCommandBuffer commandBu
117117
break;
118118
case VKCMDCOPYBUFFER:
119119
{
120-
suballoc_location src = reader.parent->allocator.find_buffer_memory(c.data.copy_buffer.src_buffer_index);
121-
suballoc_location dst = reader.parent->allocator.find_buffer_memory(c.data.copy_buffer.dst_buffer_index);
120+
suballoc_location src = device_data.allocator->find_buffer_memory(c.data.copy_buffer.src_buffer_index);
121+
suballoc_location dst = device_data.allocator->find_buffer_memory(c.data.copy_buffer.dst_buffer_index);
122122
for (uint32_t i = 0; i < c.data.copy_buffer.regionCount; i++)
123123
{
124124
VkBufferCopy& r = c.data.copy_buffer.pRegions[i];
@@ -129,7 +129,7 @@ static bool execute_commands(lava_file_reader& reader, VkCommandBuffer commandBu
129129
break;
130130
case VKCMDUPDATEBUFFER:
131131
{
132-
suballoc_location sub = reader.parent->allocator.find_buffer_memory(c.data.update_buffer.buffer_index);
132+
suballoc_location sub = device_data.allocator->find_buffer_memory(c.data.update_buffer.buffer_index);
133133
memcpy((char*)sub.memory + c.data.update_buffer.offset, c.data.update_buffer.values, c.data.update_buffer.size);
134134
}
135135
free(c.data.update_buffer.values);
@@ -153,15 +153,15 @@ static bool execute_commands(lava_file_reader& reader, VkCommandBuffer commandBu
153153
const auto& pipeline_data = VkPipeline_index.at(compute_pipeline_bound);
154154
assert(pipeline_data.shader_stages.size() == 1);
155155
assert(pipeline_data.shader_stages[0].stage == VK_SHADER_STAGE_COMPUTE_BIT);
156-
run_spirv(reader, pipeline_data, pipeline_data.shader_stages[0], push_constants, descriptorsets);
156+
run_spirv(device_data, pipeline_data, pipeline_data.shader_stages[0], push_constants, descriptorsets);
157157
}
158158
break;
159159
case VKCMDDRAW: // proxy for all draw commands
160160
{
161161
const auto& pipeline_data = VkPipeline_index.at(graphics_pipeline_bound);
162162
for (const auto& stage : pipeline_data.shader_stages)
163163
{
164-
run_spirv(reader, pipeline_data, stage, push_constants, descriptorsets);
164+
run_spirv(device_data, pipeline_data, stage, push_constants, descriptorsets);
165165
}
166166
}
167167
break;

src/hardcode_read.cpp

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,14 +1138,15 @@ void retrace_vkSyncBufferTRACETOOLTEST(lava_file_reader& reader)
11381138
void retrace_vkAssertBufferARM(lava_file_reader& reader)
11391139
{
11401140
const uint32_t device_index = reader.read_handle(DEBUGPARAM("VkDevice"));
1141+
const auto& device_data = VkDevice_index.at(device_index);
11411142
const uint32_t buffer_index = reader.read_handle(DEBUGPARAM("VkBuffer"));
11421143
const VkDeviceSize offset = reader.read_uint64_t();
11431144
VkDeviceSize size = reader.read_uint64_t();
11441145
const char* comment = reader.read_string();
11451146
const uint32_t checksum = reader.read_uint32_t();
11461147
trackedobject& tbuf = VkBuffer_index.at(buffer_index);
11471148
VkDevice device = index_to_VkDevice.at(device_index);
1148-
suballoc_location loc = reader.parent->allocator.find_buffer_memory(buffer_index);
1149+
suballoc_location loc = device_data.allocator->find_buffer_memory(buffer_index);
11491150
if (size == VK_WHOLE_SIZE)
11501151
{
11511152
size = tbuf.size - offset; // set to remaining size
@@ -1436,11 +1437,16 @@ void replay_postprocess_vkCmdBindDescriptorSets2(lava_file_reader& reader, VkCom
14361437

14371438
void replay_postprocess_vkQueueSubmit2(lava_file_reader& reader, VkResult result, VkQueue queue, uint32_t submitCount, const VkSubmitInfo2* pSubmits, VkFence fence)
14381439
{
1440+
const uint32_t queue_index = index_to_VkQueue.index(queue);
1441+
auto& queue_data = VkQueue_index.at(queue_index);
1442+
const uint32_t device_index = queue_data.device_index;
1443+
assert(device_index != UINT32_MAX);
1444+
auto& device_data = VkDevice_index.at(device_index);
14391445
for (uint32_t i = 0; i < submitCount; i++)
14401446
{
14411447
for (uint32_t j = 0; j < pSubmits[i].commandBufferInfoCount; j++)
14421448
{
1443-
execute_commands(reader, pSubmits[i].pCommandBufferInfos[j].commandBuffer);
1449+
execute_commands(reader, device_data, pSubmits[i].pCommandBufferInfos[j].commandBuffer);
14441450
}
14451451
}
14461452
}
@@ -1452,11 +1458,16 @@ void replay_postprocess_vkQueueSubmit2KHR(lava_file_reader& reader, VkResult res
14521458

14531459
void replay_postprocess_vkQueueSubmit(lava_file_reader& reader, VkResult result, VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence)
14541460
{
1461+
const uint32_t queue_index = index_to_VkQueue.index(queue);
1462+
auto& queue_data = VkQueue_index.at(queue_index);
1463+
const uint32_t device_index = queue_data.device_index;
1464+
assert(device_index != UINT32_MAX);
1465+
auto& device_data = VkDevice_index.at(device_index);
14551466
for (uint32_t i = 0; i < submitCount; i++)
14561467
{
14571468
for (uint32_t j = 0; j < pSubmits[i].commandBufferCount; j++)
14581469
{
1459-
execute_commands(reader, pSubmits[i].pCommandBuffers[j]);
1470+
execute_commands(reader, device_data, pSubmits[i].pCommandBuffers[j]);
14601471
}
14611472
}
14621473
}
@@ -1908,6 +1919,7 @@ void retrace_vkGetSwapchainImagesKHR(lava_file_reader& reader)
19081919
const uint32_t device_index = reader.read_handle(DEBUGPARAM("VkDevice"));
19091920
const uint32_t swapchain_index = reader.read_handle(DEBUGPARAM("VkSwapchainKHR"));
19101921
VkDevice device = index_to_VkDevice.at(device_index);
1922+
auto& device_data = VkDevice_index.at(device_index);
19111923
const uint8_t do_call = reader.read_uint8_t();
19121924
const VkResult stored_retval = (VkResult)reader.read_uint32_t();
19131925
const uint32_t stored_image_count = reader.read_uint32_t();
@@ -1953,7 +1965,7 @@ void retrace_vkGetSwapchainImagesKHR(lava_file_reader& reader)
19531965
result = wrap_vkCreateImage(device, &pinfo, nullptr, &data.virtual_images[i]);
19541966
assert(result == VK_SUCCESS);
19551967
}
1956-
reader.parent->allocator.virtualswap_images(device, data.virtual_images);
1968+
device_data.allocator->virtualswap_images(data.virtual_images);
19571969

19581970
if (is_noscreen())
19591971
{
@@ -2458,18 +2470,20 @@ void retrace_vkGetPhysicalDeviceXlibPresentationSupportKHR(lava_file_reader& rea
24582470

24592471
void image_update(lava_file_reader& reader, uint32_t device_index, uint32_t image_index, uint64_t size, const VkBaseOutStructure* sptr)
24602472
{
2461-
suballoc_location loc = reader.parent->allocator.find_image_memory(image_index);
2473+
VkDevice device = index_to_VkDevice.at(device_index);
2474+
const auto& device_data = VkDevice_index.at(device_index);
2475+
suballoc_location loc = device_data.allocator->find_image_memory(image_index);
24622476
DLOG2("image update idx=%u flush=%s init=%s size=%lu", image_index, loc.needs_flush ? "yes" : "no", loc.needs_init ? "yes" : "no", (unsigned long)loc.size);
24632477
assert(sptr == nullptr);
2464-
VkDevice device = index_to_VkDevice.at(device_index);
24652478
char* ptr = mem_map(reader, device, loc);
24662479
int32_t changed = reader.read_patch(ptr, loc.size);
24672480
mem_unmap(reader, device, loc, nullptr, ptr);
24682481
}
24692482

24702483
void buffer_update(lava_file_reader& reader, uint32_t device_index, uint32_t buffer_index, uint64_t size, const VkBaseOutStructure* sptr)
24712484
{
2472-
suballoc_location loc = reader.parent->allocator.find_buffer_memory(buffer_index);
2485+
const auto& device_data = VkDevice_index.at(device_index);
2486+
suballoc_location loc = device_data.allocator->find_buffer_memory(buffer_index);
24732487
DLOG2("buffer update idx=%u flush=%s init=%s size=%lu", buffer_index, loc.needs_flush ? "yes" : "no", loc.needs_init ? "yes" : "no", (unsigned long)loc.size);
24742488
assert(sptr == nullptr || sptr->sType == VK_STRUCTURE_TYPE_MARKED_OFFSETS_ARM);
24752489
VkDevice device = index_to_VkDevice.at(device_index);
@@ -2486,7 +2500,8 @@ void buffer_update(lava_file_reader& reader, uint32_t device_index, uint32_t buf
24862500

24872501
void tensor_update(lava_file_reader& reader, uint32_t device_index, uint32_t tensor_index, uint64_t size, const VkBaseOutStructure* sptr)
24882502
{
2489-
suballoc_location loc = reader.parent->allocator.find_tensor_memory(tensor_index);
2503+
const auto& device_data = VkDevice_index.at(device_index);
2504+
suballoc_location loc = device_data.allocator->find_tensor_memory(tensor_index);
24902505
DLOG2("tensor update idx=%u flush=%s init=%s size=%lu", tensor_index, loc.needs_flush ? "yes" : "no", loc.needs_init ? "yes" : "no", (unsigned long)loc.size);
24912506
assert(sptr == nullptr);
24922507
VkDevice device = index_to_VkDevice.at(device_index);

src/hardcode_write.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2187,6 +2187,7 @@ VKAPI_ATTR void VKAPI_CALL trace_vkGetDeviceQueue2(VkDevice device, const VkDevi
21872187
queue_data->queueFamily = pQueueInfo->queueFamilyIndex;
21882188
queue_data->queueFlags = pQueueInfo->flags;
21892189
queue_data->device = device;
2190+
queue_data->device_index = device_data->index;
21902191
queue_data->realIndex = realIndex;
21912192
queue_data->realFamily = realFamily;
21922193
queue_data->realQueue = *pQueue;
@@ -2236,6 +2237,7 @@ VKAPI_ATTR void VKAPI_CALL trace_vkGetDeviceQueue(VkDevice device, uint32_t queu
22362237
queue_data->queueFamily = queueFamilyIndex;
22372238
queue_data->queueFlags = physicaldevice_data->queueFamilyProperties.at(queueFamilyIndex).queueFlags;
22382239
queue_data->device = device;
2240+
queue_data->device_index = device_data->index;
22392241
queue_data->realIndex = realIndex;
22402242
queue_data->realFamily = realFamily;
22412243
queue_data->realQueue = *pQueue;

src/json_helpers.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ Json::Value trackedimage_json(const trackedimage* t)
106106
v["samples"] = (unsigned)t->samples;
107107
v["mipLevels"] = (unsigned)t->mipLevels;
108108
v["arrayLayers"] = (unsigned)t->arrayLayers;
109+
if (t->is_swapchain_image) v["swapchain_image"] = true;
109110
return v;
110111
}
111112

@@ -154,6 +155,7 @@ Json::Value trackedqueue_json(const trackedqueue* t)
154155
v["queueFamily"] = (unsigned)t->queueFamily;
155156
v["queueIndex"] = (unsigned)t->queueIndex;
156157
v["queueFlags"] = (unsigned)t->queueFlags;
158+
v["parent_device_index"] = (unsigned)t->device_index;
157159
return v;
158160
}
159161

@@ -351,7 +353,7 @@ trackedimage trackedimage_json(const Json::Value& v)
351353
t.currentLayout = t.initialLayout;
352354
t.samples = (VkSampleCountFlagBits)(v.get("samples", 0).asUInt());
353355
t.mipLevels = (unsigned)v.get("mipLevels", 0).asUInt();
354-
t.arrayLayers = (unsigned)v.get("arrayLevels", 0).asUInt();
356+
t.arrayLayers = (unsigned)v.get("arrayLayers", 0).asUInt();
355357
t.format = (VkFormat)v.get("format", VK_FORMAT_MAX_ENUM).asUInt();
356358
if (v.isMember("extent"))
357359
{
@@ -365,6 +367,7 @@ trackedimage trackedimage_json(const Json::Value& v)
365367
t.alias_type = (VkObjectType)v["alias_type"].asUInt();
366368
}
367369
if (v.isMember("memory_flags")) t.memory_flags = (VkMemoryPropertyFlags)v["memory_flags"].asUInt();
370+
if (v.isMember("swapchain_image")) t.is_swapchain_image = v["swapchain_image"].asBool();
368371
t.object_type = VK_OBJECT_TYPE_IMAGE;
369372

370373
if (v.isMember("parent_device_index")) t.parent_device_index = v["parent_device_index"].asUInt();
@@ -456,6 +459,7 @@ trackedqueue trackedqueue_json(const Json::Value& v)
456459
{
457460
trackedqueue t;
458461
trackable_helper(t, v);
462+
t.device_index = v["parent_device_index"].asUInt();
459463
t.enter_initialized();
460464
return t;
461465
}

0 commit comments

Comments
 (0)