Skip to content

Commit

Permalink
Track Vulkan object current state
Browse files Browse the repository at this point in the history
Implemented a very simple state machine for the Vulkan object
metadata. Add more asserts that are conditionally on the
current state (uninitialized, initialized, created, bound,
destroyed).
  • Loading branch information
per-mathisen-arm committed Dec 28, 2024
1 parent 7152a73 commit f1c0012
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 9 deletions.
23 changes: 19 additions & 4 deletions scripts/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,7 @@ def print_load(self, name, owner): # called for each parameter
z.do('const VkMemoryPropertyFlags special_flags = static_cast<VkMemoryPropertyFlags>(reader.read_uint32_t()); // fetch memory flags especially added')
z.do('const VkImageTiling tiling = static_cast<VkImageTiling>(reader.read_uint32_t()); // fetch tiling property especially added')
z.do('const VkDeviceSize min_size = static_cast<VkDeviceSize>(reader.read_uint64_t()); // fetch padded memory size')
z.do('trackedimage& image_data = VkImage_index.at(image_index);')
z.do('suballoc_location loc = suballoc_add_image(reader.thread_index(), device, %s, image_index, special_flags, tiling, min_size);' % varname)
elif self.funcname in ['vkBindBufferMemory', 'VkBindBufferMemoryInfo', 'VkBindBufferMemoryInfoKHR'] and self.name == 'buffer':
z.do('const VkMemoryPropertyFlags special_flags = static_cast<VkMemoryPropertyFlags>(reader.read_uint32_t()); // fetch memory flags especially added')
Expand Down Expand Up @@ -1045,9 +1046,8 @@ def print_save(self, name, owner): # called for each parameter
z.do('const auto* meminfo = writer.parent->records.VkDeviceMemory_index.at(%s);' % (owner + 'memory'))
z.do('writer.write_uint32_t(static_cast<uint32_t>(meminfo->propertyFlags)); // save memory flags')
if self.funcname in ['vkBindImageMemory', 'VkBindImageMemoryInfo', 'VkBindImageMemoryInfoKHR'] and self.name == 'image':
z.do('const auto* imageinfo = writer.parent->records.VkImage_index.at(%s);' % varname)
z.do('writer.write_uint32_t(static_cast<uint32_t>(imageinfo->tiling)); // save tiling info')
z.do('writer.write_uint64_t(static_cast<uint64_t>(imageinfo->size)); // save padded image size')
z.do('writer.write_uint32_t(static_cast<uint32_t>(image_data->tiling)); // save tiling info')
z.do('writer.write_uint64_t(static_cast<uint64_t>(image_data->size)); // save padded image size')
if self.funcname == 'vkAllocateMemory' and self.name == 'pAllocateInfo':
z.do('frame_mutex.lock();')
z.do('assert(real_memory_properties.memoryTypeCount > 0);')
Expand Down Expand Up @@ -1235,6 +1235,7 @@ def save_add_tracking(name):
z.do('add->buffer_index = writer.parent->records.VkBuffer_index.at(pCreateInfo->buffer)->index;')
z.do('add->size = pCreateInfo->size;')
z.do('DLOG2("insert %s into %s index %%u", (unsigned)add->index);' % (type, name))
z.do('add->enter_created();')
z.do('writer.write_handle(add);')
elif name in spec.functions_create: # multiple
(param, count, type) = get_create_params(name)
Expand All @@ -1260,6 +1261,7 @@ def save_add_tracking(name):
elif type == 'VkSwapchainKHR':
z.do('add->info = pCreateInfos[i];')
z.do('DLOG2("insert %s into %s index %%u", (unsigned)add->index);' % (type, name))
z.do('add->enter_created();')
z.do('writer.write_handle(add);')
z.brace_end()
elif name in spec.functions_destroy:
Expand All @@ -1276,7 +1278,7 @@ def save_add_tracking(name):
z.do('auto* meta = writer.parent->records.%s_index.unset(%s%s, writer.current);' % (type, param, '' if count == '1' else '[i]'))
z.do('DLOG2("removing %s from %s index %%u", (unsigned)meta->index);' % (type, name))
z.do('meta->destroyed = writer.current;')
z.do('meta->self_test();')
z.do('meta->enter_destroyed();')
if type == 'VkCommandBuffer':
z.do('commandpool_data->commandbuffers.erase(meta);')
z.brace_end()
Expand Down Expand Up @@ -1334,6 +1336,7 @@ def load_add_tracking(name):
z.do('data.format = pCreateInfo->format; // as above, might be missing in json')
elif type == 'VkDescriptorSet':
z.do('data.pool = pAllocateInfo->descriptorPool;')
z.do('data.enter_created();')
else: # multiple
z.do('for (unsigned i = 0; i < %s; i++)' % count)
z.brace_begin()
Expand All @@ -1354,6 +1357,7 @@ def load_add_tracking(name):
if type == 'VkSwapchainKHR':
z.do('data.info = pCreateInfos[i];')
z.do('data.index = indices[i];')
z.do('data.enter_created();')
z.brace_end()
elif name in spec.functions_destroy:
param = spec.functions_destroy[name][0]
Expand All @@ -1378,13 +1382,15 @@ def load_add_tracking(name):
z.do('wrap_vkDestroySemaphore(device, data.virtual_semaphore, nullptr);')
z.do('for (auto i : data.virtual_fences) wrap_vkDestroyFence(device, i, nullptr);')
z.brace_end()
z.do('data.enter_destroyed();')
z.do('index_to_%s.unset(%s);' % (type, toindex(type)))
elif name not in ignore_on_read:
z.do('if (indices[i] == CONTAINER_NULL_VALUE) continue;')
z.do('auto& data = %s_index.at(indices[i]);' % type)
z.do('assert(data.destroyed.frame == UINT32_MAX || data.destroyed.frame == reader.current.frame);')
z.do('data.destroyed = reader.current;')
z.do('data.last_modified = reader.current;')
z.do('data.enter_destroyed();')
z.do('index_to_%s.unset(indices[i]);' % type)
z.brace_end()
elif name == 'vkQueuePresentKHR':
Expand All @@ -1397,6 +1403,15 @@ def load_add_tracking(name):
z.do('if (p__debug_destination) fclose(p__debug_destination);')
z.do('return; // make sure we now do not run anything below this point')
z.brace_end()
elif name in ['vkBindImageMemory', 'VkBindImageMemoryInfoKHR', 'VkBindImageMemoryInfo']:
z.do('image_data.backing = memory;')
z.do('image_data.offset = memoryOffset;')
z.do('image_data.size = loc.size;')
z.do('image_data.enter_bound();')
elif name in ['vkBindBufferMemory', 'VkBindBufferMemoryInfo', 'VkBindBufferMemoryInfoKHR']:
z.do('buffer_data.backing = memory;')
z.do('buffer_data.offset = memoryOffset;')
z.do('buffer_data.enter_bound();')

def func_common(name, node, read, target, header, guard_header=True):
proto = node.find('proto')
Expand Down
19 changes: 19 additions & 0 deletions src/hardcode_read.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1990,6 +1990,7 @@ void retrace_vkGetPhysicalDeviceXlibPresentationSupportKHR(lava_file_reader& rea

static void trackable_helper(trackable& t, const Json::Value& v)
{
t.index = v["index"].asUInt();
t.creation.frame = v["frame_created"].asUInt();
if (v.isMember("call_created")) t.creation.call = v["call_created"].asUInt();
if (v.isMember("thread_created")) t.creation.thread = v["thread_created"].asUInt();
Expand All @@ -2009,6 +2010,7 @@ static trackable trackable_json(const Json::Value& v)
{
trackable t;
trackable_helper(t, v);
t.enter_initialized();
return t;
}

Expand All @@ -2017,6 +2019,7 @@ static trackedfence trackedfence_json(const Json::Value& v)
trackedfence t;
trackable_helper(t, v);
t.flags = v["flags"].asInt();
t.enter_initialized();
return t;
}

Expand All @@ -2026,6 +2029,7 @@ static trackedpipeline trackedpipeline_json(const Json::Value& v)
trackable_helper(t, v);
t.flags = v["flags"].asUInt();
t.type = (VkPipelineBindPoint)v["type"].asUInt();
t.enter_initialized();
return t;
}

Expand All @@ -2036,6 +2040,7 @@ static trackedaccelerationstructure trackedaccelerationstructure_json(const Json
t.size = (VkDeviceSize)v["size"].asUInt64();
t.offset = (VkDeviceSize)v["offset"].asUInt64();
t.buffer_index = v["buffer_index"].asUInt();
t.enter_initialized();
return t;
}

Expand All @@ -2051,6 +2056,7 @@ static trackedbuffer trackedbuffer_json(const Json::Value& v)
t.req.alignment = v["req_alignment"].asUInt();
t.req.memoryTypeBits = 0;
t.type = VK_OBJECT_TYPE_BUFFER;
t.enter_initialized();
return t;
}

Expand Down Expand Up @@ -2079,6 +2085,7 @@ static trackedimage trackedimage_json(const Json::Value& v)
t.extent.depth = v["extent"][2].asUInt();
}
t.type = VK_OBJECT_TYPE_IMAGE;
t.enter_initialized();
return t;
}

Expand All @@ -2091,6 +2098,7 @@ static trackedswapchain_replay trackedswapchain_replay_json(const Json::Value& v
t.info.imageExtent.width = v["width"].asUInt();
t.info.imageExtent.height = v["height"].asUInt();
t.info.imageSharingMode = (VkSharingMode)v["imageSharingMode"].asUInt();
t.enter_initialized();
return t;
}

Expand All @@ -2099,6 +2107,7 @@ static trackedcmdbuffer_replay trackedcmdbuffer_replay_json(const Json::Value& v
trackedcmdbuffer_replay t;
trackable_helper(t, v);
t.pool_index = v["pool"].asUInt();
t.enter_initialized();
return t;
}

Expand All @@ -2107,6 +2116,7 @@ static trackedimageview trackedimageview_json(const Json::Value& v)
trackedimageview t;
trackable_helper(t, v);
t.image_index = v["image"].asUInt();
t.enter_initialized();
return t;
}

Expand All @@ -2115,6 +2125,7 @@ static trackedbufferview trackedbufferview_json(const Json::Value& v)
trackedbufferview t;
trackable_helper(t, v);
t.buffer_index = v["buffer"].asUInt();
t.enter_initialized();
return t;
}

Expand All @@ -2123,34 +2134,39 @@ static trackeddescriptorset trackeddescriptorset_json(const Json::Value& v)
trackeddescriptorset t;
trackable_helper(t, v);
t.pool_index = v["pool"].asUInt();
t.enter_initialized();
return t;
}

static trackedqueue trackedqueue_json(const Json::Value& v)
{
trackedqueue t;
trackable_helper(t, v);
t.enter_initialized();
return t;
}

static trackeddevice trackeddevice_json(const Json::Value& v)
{
trackeddevice t;
trackable_helper(t, v);
t.enter_initialized();
return t;
}

static trackedphysicaldevice trackedphysicaldevice_json(const Json::Value& v)
{
trackedphysicaldevice t;
trackable_helper(t, v);
t.enter_initialized();
return t;
}

static trackedframebuffer trackedframebuffer_json(const Json::Value& v)
{
trackedframebuffer t;
trackable_helper(t, v);
t.enter_initialized();
return t;
}

Expand All @@ -2160,13 +2176,15 @@ static trackedshadermodule trackedshadermodule_json(const Json::Value& v)
trackable_helper(t, v);
if (v.isMember("size")) t.name = v["size"].asInt();
if (v.isMember("enables_device_address")) t.enables_device_address = v["enables_device_address"].asBool();
t.enter_initialized();
return t;
}

static trackedrenderpass trackedrenderpass_json(const Json::Value& v)
{
trackedrenderpass t;
trackable_helper(t, v);
t.enter_initialized();
return t;
}

Expand All @@ -2175,6 +2193,7 @@ static trackedpipelinelayout trackedpipelinelayout_json(const Json::Value& v)
trackedpipelinelayout t;
trackable_helper(t, v);
if (v.isMember("push_constant_space_used")) t.push_constant_space_used = v["push_constant_space_used"].asUInt();
t.enter_initialized();
return t;
}

Expand Down
5 changes: 5 additions & 0 deletions src/hardcode_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ static void trace_post_vkBindImageMemory(lava_file_writer& writer, VkResult resu
}
image_data->size = image_data->req.size; // we do not try to second guess this for images
image_data->accessible = ((image_data->tiling != VK_IMAGE_TILING_OPTIMAL) && (memory_data->propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
image_data->enter_bound();
writer.parent->memory_mutex.unlock();
}

Expand All @@ -400,6 +401,7 @@ static void trace_post_vkBindBufferMemory(lava_file_writer& writer, VkResult res
}
// we pass in size recorded from vkCreateBuffer, which is the actually used size, rather than required allocation size
buffer_data->accessible = (memory_data->propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
buffer_data->enter_bound();
writer.parent->memory_mutex.unlock();
}

Expand Down Expand Up @@ -1227,6 +1229,7 @@ static void trace_post_vkCreateInstance(lava_file_writer& writer, VkResult resul
wrap_vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[cur_dev], &count, nullptr);
add->queueFamilyProperties.resize(count);
wrap_vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[cur_dev], &count, add->queueFamilyProperties.data());
add->enter_initialized();
}

frame_mutex.unlock();
Expand Down Expand Up @@ -2062,6 +2065,7 @@ VKAPI_ATTR void VKAPI_CALL trace_vkGetDeviceQueue2(VkDevice device, const VkDevi
queue_data->realFamily = realFamily;
queue_data->realQueue = *pQueue;
queue_data->physicalDevice = device_data->physicalDevice;
queue_data->enter_initialized();
}
auto* queue_data = writer.parent->records.VkQueue_index.at(*pQueue);
assert(queue_data);
Expand Down Expand Up @@ -2109,6 +2113,7 @@ VKAPI_ATTR void VKAPI_CALL trace_vkGetDeviceQueue(VkDevice device, uint32_t queu
queue_data->realFamily = realFamily;
queue_data->realQueue = *pQueue;
queue_data->physicalDevice = device_data->physicalDevice;
queue_data->enter_initialized();
}
auto* queue_data = writer.parent->records.VkQueue_index.at(*pQueue);
assert(queue_data);
Expand Down
58 changes: 53 additions & 5 deletions src/lavatube.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,51 @@ enum

struct trackable
{
uintptr_t magic = ICD_LOADER_MAGIC; // in case we want to pass this around as a vulkan object
uint32_t index = 0;
uintptr_t magic = ICD_LOADER_MAGIC; // in case we want to pass this around as a vulkan object; must be first
uint32_t index = UINT32_MAX;
enum class states { uninitialized, initialized, created, destroyed };
uint8_t state = (uint8_t)states::uninitialized;
std::string name;
trackable() {}
change_source creation;
change_source last_modified;
change_source destroyed;

bool is_state(states s) const { return (uint8_t)s == state; }
void set_state(states s) { state = (uint8_t)s; }

void enter_initialized() // call after initialized to transition state and verify contents
{
assert(is_state(states::uninitialized));
set_state(states::initialized);
self_test();
}

void enter_created() // tracer initializes and creates at same time, replayer does it in two steps
{
assert(is_state(states::initialized) || is_state(states::uninitialized));
set_state(states::created);
self_test();
}

void enter_destroyed()
{
assert(is_state(states::created));
set_state(states::destroyed);
self_test();
}

void self_test() const
{
// We assume child classes put their fields after parent classes. This is not guaranteed
// by the standard. If this is not happening, we're in trouble, so assert on it.
static_assert(offsetof(trackable, magic) == 0, "ICD loader magic must be at offset zero!");
static_assert(std::is_standard_layout_v<trackable> == true); // only applies to base class

creation.self_test();
last_modified.self_test();
assert(is_state(states::uninitialized) != (index != UINT32_MAX));
if (is_state(states::created)) creation.self_test();
if (is_state(states::created)) last_modified.self_test();
if (is_state(states::destroyed)) destroyed.self_test();
}
};

Expand Down Expand Up @@ -125,8 +153,9 @@ struct trackeddevice : trackable

struct trackedobject : trackable
{
enum class states : uint8_t { uninitialized, initialized, created, destroyed, bound }; // must add at end
using trackable::trackable; // inherit constructor
VkDeviceMemory backing = (VkDeviceMemory)0;
VkDeviceMemory backing = VK_NULL_HANDLE;
VkDeviceSize size = 0;
VkDeviceSize offset = 0; // our offset into our backing memory
VkMemoryRequirements req = {};
Expand All @@ -136,9 +165,27 @@ struct trackedobject : trackable
bool accessible = false; // whether our backing memory is host visible and understandable
int source = 0; // code line that is the last source for us to be scanned, only for debugging

bool is_state(states s) const { return (uint8_t)s == state; }
void set_state(states s) { state = (uint8_t)s; }

void enter_bound()
{
assert(is_state(states::created));
set_state(states::bound);
self_test();
}

void enter_destroyed() // must override to allow exit from bound
{
assert(is_state(states::created) || is_state(states::bound));
set_state(states::destroyed);
self_test();
}

void self_test() const
{
static_assert(offsetof(trackedobject, magic) == 0, "ICD loader magic must be at offset zero!"); \
if (is_state(states::bound)) assert(backing != VK_NULL_HANDLE);
assert(type != VK_OBJECT_TYPE_UNKNOWN);
assert(size != VK_WHOLE_SIZE);
trackable::self_test();
Expand Down Expand Up @@ -173,6 +220,7 @@ struct trackedbuffer : trackedmemoryobject
assert(sharingMode != VK_SHARING_MODE_MAX_ENUM);
assert(usage != VK_BUFFER_USAGE_FLAG_BITS_MAX_ENUM);
assert(type == VK_OBJECT_TYPE_BUFFER);
if (is_state(states::bound)) assert(size != 0);
trackedobject::self_test();
}
};
Expand Down
1 change: 1 addition & 0 deletions src/write.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ class lava_file_writer : public file_writer
{
if (t)
{
assert(!t->is_state(trackable::states::uninitialized) && !t->is_state(trackable::states::destroyed));
write_uint32_t(t->index);
write_int8_t(t->last_modified.thread);
write_uint16_t(t->last_modified.call);
Expand Down

0 comments on commit f1c0012

Please sign in to comment.