Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions layersvt/device_memory_report/device_memory_report.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,14 @@ void DeviceMemoryReport::UpdateAllocationUnboundCounter(uint64_t memory_handle)
uint64_t new_unbound = (allocation.total_size > bound_size) ? (allocation.total_size - bound_size) : 0;

std::string track_name = "unbound_memory";
auto res_it = resources_.find(allocation.object_handle);
// If the memory object has an associated resource with a specific usage, use it as the track name.
if (res_it != resources_.end()) {
track_name = res_it->second.GetCluster(allocation.mem_flags);
if (allocation.is_driver &&
(allocation.object_type == VK_OBJECT_TYPE_IMAGE ||
allocation.object_type == VK_OBJECT_TYPE_BUFFER)) {
auto res_it = resources_.find(allocation.object_handle);
Comment thread
jimblacklercorp marked this conversation as resolved.
// If the memory object has an associated resource with a specific usage, use it as the track name.
if (res_it != resources_.end()) {
track_name = res_it->second.GetCluster(allocation.mem_flags);
}
}
std::string new_unbound_track = GetUsageTrackName(allocation.is_driver, track_name);

Expand Down Expand Up @@ -312,13 +316,19 @@ VkDeviceSize DeviceMemoryReport::GetRecordedResourceSize(uint64_t resource_handl
return it != resources_.end() ? it->second.size : 0;
}

uint64_t DeviceMemoryReport::GetUsageCounterBytes(const std::string& track) {
std::lock_guard<std::mutex> lock(counter_mutex_);
auto it = usage_memory_bytes_.find(track);
return it != usage_memory_bytes_.end() ? it->second : 0;
}

void DeviceMemoryReport::OnCreateImage(uint64_t image_handle, VkImageUsageFlags usage) {
std::lock_guard<std::mutex> lock(counter_mutex_);
auto& res = resources_[image_handle];
res.is_image = true;
res.image_usage = usage;
for (const auto& pair : memory_allocations_) {
if (pair.second.object_handle == image_handle) {
if (pair.second.is_driver && pair.second.object_type == VK_OBJECT_TYPE_IMAGE && pair.second.object_handle == image_handle) {
UpdateAllocationUnboundCounter(pair.first);
}
}
Expand All @@ -331,7 +341,7 @@ void DeviceMemoryReport::OnCreateBuffer(uint64_t buffer_handle, VkBufferUsageFla
res.buffer_usage = usage;
res.size = size;
for (const auto& pair : memory_allocations_) {
if (pair.second.object_handle == buffer_handle) {
if (pair.second.is_driver && pair.second.object_type == VK_OBJECT_TYPE_BUFFER && pair.second.object_handle == buffer_handle) {
UpdateAllocationUnboundCounter(pair.first);
}
}
Expand All @@ -355,6 +365,7 @@ void DeviceMemoryReport::OnMemoryReportEvent(const VkDeviceMemoryReportCallbackD
auto& allocation = memory_allocations_[key];
allocation.total_size = pCallbackData->size;
allocation.is_driver = (pCallbackData->flags & VK_DEVICE_MEMORY_REPORT_FLAG_INTERNAL_OBJECT_BIT_EXT) != 0;
allocation.object_type = pCallbackData->objectType;
allocation.object_handle = pCallbackData->objectHandle;
UpdateAllocationUnboundCounter(key);
} else if (pCallbackData->type == VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_FREE_EXT ||
Expand Down
8 changes: 8 additions & 0 deletions layersvt/device_memory_report/device_memory_report.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,13 @@ class DeviceMemoryReport {
*/
VkDeviceSize GetRecordedResourceSize(uint64_t resource_handle);

/**
* @brief Retrieves the current total memory bytes recorded on a usage track (for testing).
* @param track The full usage track name (e.g. "vulkan.mem.app.usage.unbound_memory").
* @return Number of bytes currently recorded on the track, or 0 if the track has no counter.
*/
Comment thread
jimblacklercorp marked this conversation as resolved.
uint64_t GetUsageCounterBytes(const std::string& track);

/**
* @brief Tracks creation of a Vulkan image and its usage flags.
* @param image_handle The 64-bit handle of the Vulkan image.
Expand Down Expand Up @@ -209,6 +216,7 @@ class DeviceMemoryReport {
VkDeviceSize total_size = 0;
VkDeviceSize applied_unbound_bytes = 0;
bool is_driver = false;
VkObjectType object_type = VK_OBJECT_TYPE_UNKNOWN;
VkMemoryPropertyFlags mem_flags = 0;
std::vector<SubAllocation> sub_allocations;
std::string unbound_usage_track;
Expand Down
86 changes: 86 additions & 0 deletions layersvt/test/test_devicememoryreport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ TEST_F(DeviceMemoryReportTests, EmitEventsAndSubCounters) {
cb_data.size = 2048;
DeviceMemoryReport::MemoryReportCallback(&cb_data, nullptr);

// Free the driver allocation
cb_data.type = VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_FREE_EXT;
DeviceMemoryReport::MemoryReportCallback(&cb_data, nullptr);
Comment thread
jimblacklercorp marked this conversation as resolved.
EXPECT_EQ(DeviceMemoryReport::Get().GetUsageCounterBytes("vulkan.mem.driver.usage.unbound_memory"), 0u);

// Test direct allocate/free fallbacks
VkDevice dummy_device = reinterpret_cast<VkDevice>(0x1234);
VkDeviceMemory dummy_memory = reinterpret_cast<VkDeviceMemory>(0x5678);
Expand Down Expand Up @@ -422,6 +427,87 @@ TEST_F(DeviceMemoryReportTests, StaticCounterTrackLookup) {
EXPECT_EQ(dynamic_track_again.Serialize().counter().unit(), perfetto::protos::gen::CounterDescriptor::UNIT_SIZE_BYTES);
}

TEST_F(DeviceMemoryReportTests, DriverVsAppUnboundMemoryAttribution) {
TEST_DESCRIPTION("Test that application unbound memory is not misclassified when object handle matches an existing resource handle");

InitializeDeviceMemoryReportPerfetto();

uint64_t shared_handle = 0xF001;

// Register a virtual image resource with handle shared_handle
DeviceMemoryReport::Get().OnCreateImage(shared_handle, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
DeviceMemoryReport::Get().OnRecordResourceSize(shared_handle, 4096);

// Case 1: Driver allocation where objectHandle is the virtual resource handle
VkDeviceMemoryReportCallbackDataEXT driver_callback_data = {};
driver_callback_data.sType = VK_STRUCTURE_TYPE_DEVICE_MEMORY_REPORT_CALLBACK_DATA_EXT;
driver_callback_data.flags = VK_DEVICE_MEMORY_REPORT_FLAG_INTERNAL_OBJECT_BIT_EXT; // Driver allocation
driver_callback_data.type = VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATE_EXT;
driver_callback_data.memoryObjectId = 0x8001;
driver_callback_data.size = 4096;
driver_callback_data.objectType = VK_OBJECT_TYPE_IMAGE;
driver_callback_data.objectHandle = shared_handle;
DeviceMemoryReport::MemoryReportCallback(&driver_callback_data, nullptr);

// Driver allocation with image handle should be attributed to the color_render_target track
EXPECT_EQ(DeviceMemoryReport::Get().GetUsageCounterBytes("vulkan.mem.driver.usage.color_render_target"), 4096u);
EXPECT_EQ(DeviceMemoryReport::Get().GetUsageCounterBytes("vulkan.mem.driver.usage.unbound_memory"), 0u);

// Clean up driver allocation
driver_callback_data.type = VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_FREE_EXT;
DeviceMemoryReport::MemoryReportCallback(&driver_callback_data, nullptr);
EXPECT_EQ(DeviceMemoryReport::Get().GetUsageCounterBytes("vulkan.mem.driver.usage.color_render_target"), 0u);

// Case 2: Application allocation where VkDeviceMemory handle happens to have the same integer value as shared_handle
VkDeviceMemoryReportCallbackDataEXT application_callback_data = {};
application_callback_data.sType = VK_STRUCTURE_TYPE_DEVICE_MEMORY_REPORT_CALLBACK_DATA_EXT;
application_callback_data.flags = 0; // Application allocation
application_callback_data.type = VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATE_EXT;
application_callback_data.memoryObjectId = 0x8002;
application_callback_data.size = 8192;
application_callback_data.objectType = VK_OBJECT_TYPE_DEVICE_MEMORY;
application_callback_data.objectHandle = shared_handle;
DeviceMemoryReport::MemoryReportCallback(&application_callback_data, nullptr);

// Application allocation should be classified as unbound_memory, NOT color_render_target
EXPECT_EQ(DeviceMemoryReport::Get().GetUsageCounterBytes("vulkan.mem.app.usage.unbound_memory"), 8192u);
EXPECT_EQ(DeviceMemoryReport::Get().GetUsageCounterBytes("vulkan.mem.app.usage.color_render_target"), 0u);

// Clean up application allocation and resource
application_callback_data.type = VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_FREE_EXT;
DeviceMemoryReport::MemoryReportCallback(&application_callback_data, nullptr);
EXPECT_EQ(DeviceMemoryReport::Get().GetUsageCounterBytes("vulkan.mem.app.usage.unbound_memory"), 0u);

DeviceMemoryReport::Get().OnDestroyObject(shared_handle);

// Case 3: Driver allocation arrives before OnCreateBuffer (tests re-attribution)
uint64_t buffer_handle = 0xF002;
VkDeviceMemoryReportCallbackDataEXT buffer_callback_data = {};
buffer_callback_data.sType = VK_STRUCTURE_TYPE_DEVICE_MEMORY_REPORT_CALLBACK_DATA_EXT;
buffer_callback_data.flags = VK_DEVICE_MEMORY_REPORT_FLAG_INTERNAL_OBJECT_BIT_EXT;
buffer_callback_data.type = VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATE_EXT;
buffer_callback_data.memoryObjectId = 0x8003;
buffer_callback_data.size = 4096;
buffer_callback_data.objectType = VK_OBJECT_TYPE_BUFFER;
buffer_callback_data.objectHandle = buffer_handle;
DeviceMemoryReport::MemoryReportCallback(&buffer_callback_data, nullptr);

// Prior to OnCreateBuffer, the driver allocation is counted under unbound_memory
EXPECT_EQ(DeviceMemoryReport::Get().GetUsageCounterBytes("vulkan.mem.driver.usage.unbound_memory"), 4096u);
EXPECT_EQ(DeviceMemoryReport::Get().GetUsageCounterBytes("vulkan.mem.driver.usage.geometry_mesh"), 0u);

// When the buffer is created, the allocation is re-attributed to the geometry_mesh cluster
DeviceMemoryReport::Get().OnCreateBuffer(buffer_handle, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 4096);
EXPECT_EQ(DeviceMemoryReport::Get().GetUsageCounterBytes("vulkan.mem.driver.usage.unbound_memory"), 0u);
EXPECT_EQ(DeviceMemoryReport::Get().GetUsageCounterBytes("vulkan.mem.driver.usage.geometry_mesh"), 4096u);

// Clean up
buffer_callback_data.type = VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_FREE_EXT;
DeviceMemoryReport::MemoryReportCallback(&buffer_callback_data, nullptr);
EXPECT_EQ(DeviceMemoryReport::Get().GetUsageCounterBytes("vulkan.mem.driver.usage.geometry_mesh"), 0u);
DeviceMemoryReport::Get().OnDestroyObject(buffer_handle);
}

Comment thread
olehkuznetsov marked this conversation as resolved.
TEST_F(DeviceMemoryReportTests, ProactiveMemoryRequirementsQuery) {
TEST_DESCRIPTION("Test that the layer proactively queries memory requirements when creating images and buffers");

Expand Down
Loading