Skip to content

Commit

Permalink
object_tracker: Split Device and Instance
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremyg-lunarg committed Jan 21, 2025
1 parent 14ab507 commit e416961
Show file tree
Hide file tree
Showing 13 changed files with 2,271 additions and 2,275 deletions.
3 changes: 2 additions & 1 deletion BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@ vvl_sources = [
"layers/vulkan/generated/instrumentation_post_process_descriptor_index_comp.cpp",
"layers/vulkan/generated/instrumentation_post_process_descriptor_index_comp.h",
"layers/vulkan/generated/object_tracker.cpp",
"layers/vulkan/generated/object_tracker.h",
"layers/vulkan/generated/object_tracker_device_methods.h",
"layers/vulkan/generated/object_tracker_instance_methods.h",
"layers/vulkan/generated/pnext_chain_extraction.cpp",
"layers/vulkan/generated/pnext_chain_extraction.h",
"layers/vulkan/generated/spirv_grammar_helper.cpp",
Expand Down
3 changes: 2 additions & 1 deletion layers/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ target_sources(vvl PRIVATE
${API_TYPE}/generated/dispatch_object.cpp
${API_TYPE}/generated/validation_object.cpp
${API_TYPE}/generated/object_tracker.cpp
${API_TYPE}/generated/object_tracker.h
${API_TYPE}/generated/object_tracker_device_methods.h
${API_TYPE}/generated/object_tracker_instance_methods.h
${API_TYPE}/generated/spirv_grammar_helper.cpp
${API_TYPE}/generated/spirv_validation_helper.cpp
${API_TYPE}/generated/stateless_validation_helper.cpp
Expand Down
258 changes: 168 additions & 90 deletions layers/object_tracker/object_lifetime_validation.h

Large diffs are not rendered by default.

727 changes: 363 additions & 364 deletions layers/object_tracker/object_tracker_utils.cpp

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions layers/vulkan/generated/dispatch_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ void Instance::InitValidationObjects() {
object_dispatch.emplace_back(new StatelessValidation(this));
}
if (!settings.disabled[object_tracking]) {
object_dispatch.emplace_back(new ObjectLifetimes(this));
object_dispatch.emplace_back(new object_lifetimes::Instance(this));
}
if (!settings.disabled[core_checks]) {
object_dispatch.emplace_back(new CoreChecks(this));
Expand Down Expand Up @@ -79,8 +79,8 @@ void Device::InitValidationObjects() {
this, static_cast<StatelessValidation*>(dispatch_instance->GetValidationObject(LayerObjectTypeParameterValidation))));
}
if (!settings.disabled[object_tracking]) {
object_dispatch.emplace_back(new ObjectLifetimes(
this, static_cast<ObjectLifetimes*>(dispatch_instance->GetValidationObject(LayerObjectTypeObjectTracker))));
object_dispatch.emplace_back(new object_lifetimes::Device(
this, static_cast<object_lifetimes::Instance*>(dispatch_instance->GetValidationObject(LayerObjectTypeObjectTracker))));
}
if (!settings.disabled[core_checks]) {
object_dispatch.emplace_back(
Expand Down
9 changes: 5 additions & 4 deletions layers/vulkan/generated/dispatch_vector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ namespace vvl {
namespace dispatch {

void Device::InitObjectDispatchVectors() {
#define BUILD_DISPATCH_VECTOR(name) \
init_object_dispatch_vector(InterceptId##name, typeid(&ValidationObject::name), typeid(&threadsafety::Device::name), \
typeid(&StatelessValidation::name), typeid(&ObjectLifetimes::name), typeid(&CoreChecks::name), \
typeid(&BestPractices::name), typeid(&gpuav::Validator::name), typeid(&SyncValidator::name));
#define BUILD_DISPATCH_VECTOR(name) \
init_object_dispatch_vector(InterceptId##name, typeid(&ValidationObject::name), typeid(&threadsafety::Device::name), \
typeid(&StatelessValidation::name), typeid(&object_lifetimes::Device::name), \
typeid(&CoreChecks::name), typeid(&BestPractices::name), typeid(&gpuav::Validator::name), \
typeid(&SyncValidator::name));

auto init_object_dispatch_vector = [this](InterceptId id, const std::type_info& vo_typeid, const std::type_info& tt_typeid,
const std::type_info& tpv_typeid, const std::type_info& tot_typeid,
Expand Down
2,991 changes: 1,425 additions & 1,566 deletions layers/vulkan/generated/object_tracker.cpp

Large diffs are not rendered by default.

Large diffs are not rendered by default.

250 changes: 250 additions & 0 deletions layers/vulkan/generated/object_tracker_instance_methods.h

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion scripts/generate_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,12 @@ def RunGenerators(api: str, registry: str, grammar: str, directory: str, styleFi
'generator' : ValidFlagValuesOutputGenerator,
'genCombined': True,
},
'object_tracker.h' : {
'object_tracker_device_methods.h' : {
'generator' : ObjectTrackerOutputGenerator,
'genCombined': True,
'options' : [valid_usage_file],
},
'object_tracker_instance_methods.h' : {
'generator' : ObjectTrackerOutputGenerator,
'genCombined': True,
'options' : [valid_usage_file],
Expand Down
4 changes: 2 additions & 2 deletions scripts/generators/dispatch_object_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ def getValidationLayerList(targetApiName: str) -> list[dict[str, str]]:
},
{
'include': 'object_tracker/object_lifetime_validation.h',
'device': 'ObjectLifetimes',
'instance': 'ObjectLifetimes',
'device': 'object_lifetimes::Device',
'instance': 'object_lifetimes::Instance',
'type': 'LayerObjectTypeObjectTracker',
'enabled': '!settings.disabled[object_tracking]'
},
Expand Down
2 changes: 1 addition & 1 deletion scripts/generators/dispatch_vector_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def genInitObjectDispatchVectorSource(targetApiName: str) -> str:
typeid(&ValidationObject::name), \\
typeid(&threadsafety::Device::name), \\
typeid(&StatelessValidation::name), \\
typeid(&ObjectLifetimes::name), \\
typeid(&object_lifetimes::Device::name), \\
typeid(&CoreChecks::name), \\
typeid(&BestPractices::name), \\
typeid(&gpuav::Validator::name), \\
Expand Down
66 changes: 44 additions & 22 deletions scripts/generators/object_tracker_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ def __init__(self,
# Union of pointers
'vkGetDescriptorEXT',
'vkCreateIndirectExecutionSetEXT',
'vkDestroyPipeline',
]

# Commands which source are not autogenerated for PreCallRecord
Expand Down Expand Up @@ -300,8 +301,10 @@ def generate(self):
****************************************************************************/\n''')
self.write('// NOLINTBEGIN') # Wrap for clang-tidy to ignore

if self.filename == 'object_tracker.h':
self.generateHeader()
if self.filename == 'object_tracker_device_methods.h':
self.generateDeviceHeader()
elif self.filename == 'object_tracker_instance_methods.h':
self.generateInstanceHeader()
elif self.filename == 'object_tracker.cpp':
self.generateSource()
else:
Expand All @@ -310,10 +313,12 @@ def generate(self):
self.write('// NOLINTEND') # Wrap for clang-tidy to ignore


def generateHeader(self):
def generateHeader(self, want_instance):
out = []
guard_helper = PlatformGuardHelper()
for command in self.vk.commands.values():
if command.instance != want_instance:
continue
out.extend(guard_helper.add_guard(command.protect))
(pre_call_validate, pre_call_record, post_call_record) = self.generateFunctionBody(command)

Expand All @@ -340,15 +345,27 @@ def generateHeader(self):
out.append(f'void PostCallRecord{prototype}{terminator}')

out.extend(guard_helper.add_guard(None))
self.write("".join(out))

def generateDeviceHeader(self):
self.generateHeader(False)
out = []
# These are Post/Pre call that normally would not be created but we need have manual object tracking logic for them
out.append('''
void PostCallRecordDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator, const RecordObject& record_obj) override;
void PreCallRecordResetDescriptorPool(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags, const RecordObject& record_obj) override;
void PostCallRecordGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties *pQueueFamilyProperties, const RecordObject& record_obj) override;
void PreCallRecordFreeCommandBuffers(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer *pCommandBuffers, const RecordObject& record_obj) override;
void PreCallRecordFreeDescriptorSets(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets, const RecordObject& record_obj) override;
''')
self.write("".join(out))

def generateInstanceHeader(self):
self.generateHeader(True)
out = []
# These are Post/Pre call that normally would not be created but we need have manual object tracking logic for them
out.append('''
void PostCallRecordDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator, const RecordObject& record_obj) override;
void PostCallRecordGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties *pQueueFamilyProperties, const RecordObject& record_obj) override;
void PostCallRecordGetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties2 *pQueueFamilyProperties, const RecordObject& record_obj) override;
void PostCallRecordGetPhysicalDeviceQueueFamilyProperties2KHR(VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties2 *pQueueFamilyProperties, const RecordObject& record_obj) override;
void PostCallRecordGetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPlanePropertiesKHR* pProperties, const RecordObject& record_obj) override;
Expand All @@ -358,60 +375,62 @@ def generateHeader(self):

def generateSource(self):
out = []
out.append('// clang-format off')
out.append('''
#include "object_tracker/object_lifetime_validation.h"
ReadLockGuard ObjectLifetimes::ReadLock() const { return ReadLockGuard(validation_object_mutex, std::defer_lock); }
WriteLockGuard ObjectLifetimes::WriteLock() { return WriteLockGuard(validation_object_mutex, std::defer_lock); }
namespace object_lifetimes {
ReadLockGuard Device::ReadLock() const { return ReadLockGuard(validation_object_mutex, std::defer_lock); }
WriteLockGuard Device::WriteLock() { return WriteLockGuard(validation_object_mutex, std::defer_lock); }
// ObjectTracker undestroyed objects validation function
bool ObjectLifetimes::ReportUndestroyedInstanceObjects(VkInstance instance, const Location& loc) const {
bool Instance::ReportUndestroyedObjects(const Location& loc) const {
bool skip = false;
const std::string error_code = "VUID-vkDestroyInstance-instance-00629";
''')
for handle in [x for x in self.vk.handles.values() if not x.dispatchable and not self.isParentDevice(x)]:
comment_prefix = ''
if APISpecific.IsImplicitlyDestroyed(self.targetApiName, handle.name):
comment_prefix = '// No destroy API or implicitly freed/destroyed -- do not report: '
out.append(f' {comment_prefix}skip |= ReportLeakedInstanceObjects(instance, kVulkanObjectType{handle.name[2:]}, error_code, loc);\n')
out.append(f' {comment_prefix}skip |= ReportLeakedObjects(kVulkanObjectType{handle.name[2:]}, error_code, loc);\n')
out.append(' return skip;\n')
out.append('}\n')

out.append('''
bool ObjectLifetimes::ReportUndestroyedDeviceObjects(VkDevice device, const Location& loc) const {
bool Device::ReportUndestroyedObjects(const Location& loc) const {
bool skip = false;
const std::string error_code = "VUID-vkDestroyDevice-device-05137";
''')

comment_prefix = ''
if APISpecific.IsImplicitlyDestroyed(self.targetApiName, 'VkCommandBuffer'):
comment_prefix = '// No destroy API or implicitly freed/destroyed -- do not report: '
out.append(f' {comment_prefix}skip |= ReportLeakedDeviceObjects(device, kVulkanObjectTypeCommandBuffer, error_code, loc);\n')
out.append(f' {comment_prefix}skip |= ReportLeakedObjects(kVulkanObjectTypeCommandBuffer, error_code, loc);\n')

for handle in [x for x in self.vk.handles.values() if not x.dispatchable and self.isParentDevice(x)]:
comment_prefix = ''
if APISpecific.IsImplicitlyDestroyed(self.targetApiName, handle.name):
comment_prefix = '// No destroy API or implicitly freed/destroyed -- do not report: '
out.append(f' {comment_prefix}skip |= ReportLeakedDeviceObjects(device, kVulkanObjectType{handle.name[2:]}, error_code, loc);\n')
out.append(f' {comment_prefix}skip |= ReportLeakedObjects(kVulkanObjectType{handle.name[2:]}, error_code, loc);\n')
out.append(' return skip;\n')
out.append('}\n')

out.append('\nvoid ObjectLifetimes::DestroyLeakedInstanceObjects() {\n')
out.append('\nvoid Instance::DestroyLeakedObjects() {\n')
for handle in [x for x in self.vk.handles.values() if not x.dispatchable and not self.isParentDevice(x)]:
out.append(f' DestroyUndestroyedObjects(kVulkanObjectType{handle.name[2:]});\n')
out.append('}\n')

out.append('\nvoid ObjectLifetimes::DestroyLeakedDeviceObjects() {\n')
out.append('\nvoid Device::DestroyLeakedObjects() {\n')
out.append(' DestroyUndestroyedObjects(kVulkanObjectTypeCommandBuffer);\n')
for handle in [x for x in self.vk.handles.values() if not x.dispatchable and self.isParentDevice(x)]:
out.append(f' DestroyUndestroyedObjects(kVulkanObjectType{handle.name[2:]});\n')
out.append('}\n')

out.append('// clang-format on')
guard_helper = PlatformGuardHelper()
for command in [x for x in self.vk.commands.values() if x.name not in self.no_autogen_list]:
out.extend(guard_helper.add_guard(command.protect))

class_name = "Device" if not command.instance else "Instance"

# Generate object handling code
(pre_call_validate, pre_call_record, post_call_record) = self.generateFunctionBody(command)

Expand All @@ -431,13 +450,13 @@ def generateSource(self):
paramList.append('error_obj')
params = ', '.join(paramList)
out.append(f'''
bool ObjectLifetimes::PreCallValidate{prePrototype} const {{
bool {class_name}::PreCallValidate{prePrototype} const {{
return PreCallValidate{command.alias[2:]}({params});
}}
''')
else:
out.append(f'''
bool ObjectLifetimes::PreCallValidate{prePrototype} const {{
bool {class_name}::PreCallValidate{prePrototype} const {{
bool skip = false;
{pre_call_validate}
return skip;
Expand All @@ -448,15 +467,15 @@ def generateSource(self):
if pre_call_record and command.name not in self.no_pre_record_autogen_list:
postPrototype = prototype.replace(')', ', const RecordObject& record_obj)')
out.append(f'''
void ObjectLifetimes::PreCallRecord{postPrototype} {{
void {class_name}::PreCallRecord{postPrototype} {{
{pre_call_record}
}}
''')

# Output PostCallRecordAPI function if necessary
if post_call_record and command.name not in self.no_post_record_autogen_list:
out.append('\n')
postPrototype = f'void ObjectLifetimes::PostCallRecord{prototype} {{\n'
postPrototype = f'void {class_name}::PostCallRecord{prototype} {{\n'
postPrototype = postPrototype.replace(')', ', const RecordObject& record_obj)')
if command.returnType == 'VkResult':
# The two createpipelines APIs may create on failure -- skip the success result check
Expand All @@ -468,6 +487,9 @@ def generateSource(self):
out.append('}\n')

out.extend(guard_helper.add_guard(None))
out.append('''
} // namespace object_lifetimes
''')

self.write("".join(out))

Expand Down Expand Up @@ -945,7 +967,7 @@ def validateObjects(self, members: list[Member], prefix: str, arrayIndex: int, p
location = f'{errorLoc}.dot(Field::{member.name})'
if self.vk.commands[topCommand].device and self.vk.handles[member.type].instance:
# Use case when for device-level API call we should use instance-level validation object
pre_call_validate += 'auto instance_object_lifetimes = static_cast<ObjectLifetimes*>(dispatch_instance_->GetValidationObject(LayerObjectTypeObjectTracker));\n'
pre_call_validate += 'auto instance_object_lifetimes = static_cast<Instance*>(dispatch_instance_->GetValidationObject(container_type));\n'
pre_call_validate += f'skip |= instance_object_lifetimes->ValidateObject({prefix}{member.name}, kVulkanObjectType{member.type[2:]}, {nullAllowed}, {param_vuid}, {parent_vuid}, {location}{parent_object_type});\n'
else:
pre_call_validate += f'skip |= ValidateObject({prefix}{member.name}, kVulkanObjectType{member.type[2:]}, {nullAllowed}, {param_vuid}, {parent_vuid}, {location}{parent_object_type});\n'
Expand Down

0 comments on commit e416961

Please sign in to comment.