layers: Validate Flush/Invalidate ranges.
Merge Request 53
Conflicts:
layers/mem_tracker.cpp
layers/mem_tracker.h
diff --git a/layers/mem_tracker.cpp b/layers/mem_tracker.cpp
index 232e8f0..0701240 100644
--- a/layers/mem_tracker.cpp
+++ b/layers/mem_tracker.cpp
@@ -57,7 +57,7 @@
std::vector<VkDbgMsgCallback> logging_callback;
VkLayerDispatchTable* device_dispatch_table;
VkLayerInstanceDispatchTable* instance_dispatch_table;
- bool wsi_enabled;
+ VkBool32 wsi_enabled;
uint64_t currentFenceId;
// Maps for tracking key structs related to MemTracker state
unordered_map<VkCommandBuffer, MT_CB_INFO> cbMap;
@@ -74,7 +74,7 @@
report_data(nullptr),
device_dispatch_table(nullptr),
instance_dispatch_table(nullptr),
- wsi_enabled(false),
+ wsi_enabled(VK_FALSE),
currentFenceId(1)
{};
};
@@ -544,7 +544,7 @@
static VkBool32 freeMemObjInfo(layer_data* my_data,
void* object,
VkDeviceMemory mem,
- bool internal)
+ VkBool32 internal)
{
VkBool32 skipCall = VK_FALSE;
// Parse global list to find info w/ mem
@@ -988,7 +988,7 @@
pDisp->GetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR) gpa(device, "vkGetSwapchainImagesKHR");
pDisp->AcquireNextImageKHR = (PFN_vkAcquireNextImageKHR) gpa(device, "vkAcquireNextImageKHR");
pDisp->QueuePresentKHR = (PFN_vkQueuePresentKHR) gpa(device, "vkQueuePresentKHR");
- my_device_data->wsi_enabled = false;
+ my_device_data->wsi_enabled = VK_FALSE;
for (uint32_t i = 0; i < pCreateInfo->enabledExtensionNameCount; i++) {
if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_EXT_KHR_DEVICE_SWAPCHAIN_EXTENSION_NAME) == 0)
my_device_data->wsi_enabled = true;
@@ -1236,31 +1236,87 @@
* all API objects referencing it and that it is not referenced by any queued command buffers
*/
loader_platform_thread_lock_mutex(&globalLock);
- freeMemObjInfo(my_data, device, mem, false);
+ freeMemObjInfo(my_data, device, mem, VK_FALSE);
print_mem_list(my_data, device);
printCBList(my_data, device);
loader_platform_thread_unlock_mutex(&globalLock);
my_data->device_dispatch_table->FreeMemory(device, mem, pAllocator);
}
-bool validateMemRange(
- VkDevice device,
+VkBool32 validateMemRange(
+ layer_data *my_data,
VkDeviceMemory mem,
VkDeviceSize offset,
VkDeviceSize size)
{
- layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
- bool skip_call = false;
+ VkBool32 skipCall = VK_FALSE;
auto mem_element = my_data->memObjMap.find(mem);
if (mem_element != my_data->memObjMap.end()) {
if ((offset + size) > mem_element->second.allocInfo.allocationSize) {
- skip_call = log_msg(my_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t) mem, 0,
- MEMTRACK_INVALID_MAP, "MEM", "Mapping Memory from %" PRIu64 " to %" PRIu64 " with total array size %" PRIu64,
- offset, size + offset, mem_element->second.allocInfo.allocationSize);
+ skipCall = log_msg(my_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)mem, 0,
+ MEMTRACK_INVALID_MAP, "MEM", "Mapping Memory from %" PRIu64 " to %" PRIu64 " with total array size %" PRIu64,
+ offset, size + offset, mem_element->second.allocInfo.allocationSize);
}
}
- return skip_call;
+ return skipCall;
+}
+
+void storeMemRanges(
+ layer_data *my_data,
+ VkDeviceMemory mem,
+ VkDeviceSize offset,
+ VkDeviceSize size)
+ {
+ auto mem_element = my_data->memObjMap.find(mem);
+ if (mem_element != my_data->memObjMap.end()) {
+ MemRange new_range;
+ new_range.offset = offset;
+ new_range.size = size;
+ mem_element->second.memRange = new_range;
+ }
+}
+
+VkBool32 deleteMemRanges(
+ layer_data *my_data,
+ VkDeviceMemory mem)
+{
+ VkBool32 skipCall = VK_FALSE;
+ auto mem_element = my_data->memObjMap.find(mem);
+ if (mem_element != my_data->memObjMap.end()) {
+ if (!mem_element->second.memRange.size) {
+ skipCall = log_msg(my_data->report_data, VK_DBG_REPORT_WARN_BIT, VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)mem, 0, MEMTRACK_INVALID_MAP, "MEM",
+ "Unmapping Memory without memory being mapped: mem obj %#" PRIxLEAST64, mem);
+ }
+ mem_element->second.memRange.size = 0;
+ if (mem_element->second.pData) {
+ free(mem_element->second.pData);
+ mem_element->second.pData = 0;
+ }
+ }
+ return skipCall;
+}
+
+static char NoncoherentMemoryFillValue = 0xb;
+
+void initializeAndTrackMemory(
+ layer_data *my_data,
+ VkDeviceMemory mem,
+ VkDeviceSize size,
+ void **ppData)
+{
+ auto mem_element = my_data->memObjMap.find(mem);
+ if (mem_element != my_data->memObjMap.end()) {
+ mem_element->second.pDriverData = *ppData;
+ uint32_t index = mem_element->second.allocInfo.memoryTypeIndex;
+ if (memProps.memoryTypes[index].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) {
+ mem_element->second.pData = 0;
+ } else {
+ mem_element->second.pData = malloc(2 * size);
+ memset(mem_element->second.pData, NoncoherentMemoryFillValue, 2 * size);
+ *ppData = static_cast<char*>(mem_element->second.pData) + (size / 2);
+ }
+ }
}
VK_LAYER_EXPORT VkResult VKAPI vkMapMemory(
@@ -1272,9 +1328,8 @@
void **ppData)
{
layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
- // TODO : Track when memory is mapped
- VkBool32 skipCall = VK_FALSE;
- VkResult result = VK_ERROR_VALIDATION_FAILED;
+ VkBool32 skipCall = VK_FALSE;
+ VkResult result = VK_ERROR_VALIDATION_FAILED;
loader_platform_thread_lock_mutex(&globalLock);
MT_MEM_OBJ_INFO *pMemObj = get_mem_obj_info(my_data, mem);
if ((memProps.memoryTypes[pMemObj->allocInfo.memoryTypeIndex].propertyFlags &
@@ -1282,10 +1337,12 @@
skipCall = log_msg(my_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t) mem, 0, MEMTRACK_INVALID_STATE, "MEM",
"Mapping Memory without VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT set: mem obj %#" PRIxLEAST64, (uint64_t) mem);
}
- skipCall |= validateMemRange(device, mem, offset, size);
+ skipCall |= validateMemRange(my_data, mem, offset, size);
+ storeMemRanges(my_data, mem, offset, size);
loader_platform_thread_unlock_mutex(&globalLock);
if (VK_FALSE == skipCall) {
result = my_data->device_dispatch_table->MapMemory(device, mem, offset, size, flags, ppData);
+ initializeAndTrackMemory(my_data, mem, size, ppData);
}
return result;
}
@@ -1295,9 +1352,90 @@
VkDeviceMemory mem)
{
layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
- // TODO : Track as memory gets unmapped, do we want to check what changed following map?
- // Make sure that memory was ever mapped to begin with
- my_data->device_dispatch_table->UnmapMemory(device, mem);
+ VkBool32 skipCall = VK_FALSE;
+
+ loader_platform_thread_lock_mutex(&globalLock);
+ skipCall |= deleteMemRanges(my_data, mem);
+ loader_platform_thread_unlock_mutex(&globalLock);
+ if (VK_FALSE == skipCall) {
+ my_data->device_dispatch_table->UnmapMemory(device, mem);
+ }
+}
+
+VkBool32 validateMemoryIsMapped(layer_data *my_data, uint32_t memRangeCount, const VkMappedMemoryRange* pMemRanges) {
+ VkBool32 skipCall = VK_FALSE;
+ for (uint32_t i = 0; i < memRangeCount; ++i) {
+ auto mem_element = my_data->memObjMap.find(pMemRanges[i].memory);
+ if (mem_element != my_data->memObjMap.end()) {
+ if (mem_element->second.memRange.offset > pMemRanges[i].offset ||
+ (mem_element->second.memRange.offset + mem_element->second.memRange.size) < (pMemRanges[i].offset + pMemRanges[i].size)) {
+ skipCall |= log_msg(my_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)pMemRanges[i].memory,
+ 0, MEMTRACK_INVALID_MAP, "MEM", "Memory must be mapped before it can be flushed or invalidated.");
+ }
+ }
+ }
+ return skipCall;
+}
+
+VkBool32 validateAndCopyNoncoherentMemoryToDriver(layer_data *my_data, uint32_t memRangeCount, const VkMappedMemoryRange* pMemRanges) {
+ VkBool32 skipCall = VK_FALSE;
+ for (uint32_t i = 0; i < memRangeCount; ++i) {
+ auto mem_element = my_data->memObjMap.find(pMemRanges[i].memory);
+ if (mem_element != my_data->memObjMap.end()) {
+ if (mem_element->second.pData) {
+ VkDeviceSize size = mem_element->second.memRange.size;
+ VkDeviceSize half_size = (size / 2);
+ char* data = static_cast<char*>(mem_element->second.pData);
+ for (auto j = 0; j < half_size; ++j) {
+ if (data[j] != NoncoherentMemoryFillValue) {
+ skipCall |= log_msg(my_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)pMemRanges[i].memory,
+ 0, MEMTRACK_INVALID_MAP, "MEM", "Memory overflow was detected on mem obj %" PRIxLEAST64, pMemRanges[i].memory);
+ }
+ }
+ for (auto j = size + half_size; j < 2 * size; ++j) {
+ if (data[j] != NoncoherentMemoryFillValue) {
+ skipCall |= log_msg(my_data->report_data, VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)pMemRanges[i].memory,
+ 0, MEMTRACK_INVALID_MAP, "MEM", "Memory overflow was detected on mem obj %" PRIxLEAST64, pMemRanges[i].memory);
+ }
+ }
+ memcpy(mem_element->second.pDriverData, static_cast<void*>(data + half_size), size);
+ }
+ }
+ }
+ return skipCall;
+}
+
+VK_LAYER_EXPORT VkResult vkFlushMappedMemoryRanges(
+ VkDevice device,
+ uint32_t memRangeCount,
+ const VkMappedMemoryRange* pMemRanges)
+{
+ VkResult result = VK_ERROR_VALIDATION_FAILED;
+ VkBool32 skipCall = VK_FALSE;
+ layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+
+ skipCall |= validateAndCopyNoncoherentMemoryToDriver(my_data, memRangeCount, pMemRanges);
+ skipCall |= validateMemoryIsMapped(my_data, memRangeCount, pMemRanges);
+ if (VK_FALSE == skipCall ) {
+ result = my_data->device_dispatch_table->FlushMappedMemoryRanges(device, memRangeCount, pMemRanges);
+ }
+ return result;
+}
+
+VK_LAYER_EXPORT VkResult vkInvalidateMappedMemoryRanges(
+ VkDevice device,
+ uint32_t memRangeCount,
+ const VkMappedMemoryRange* pMemRanges)
+{
+ VkResult result = VK_ERROR_VALIDATION_FAILED;
+ VkBool32 skipCall = VK_FALSE;
+ layer_data *my_data = get_my_data_ptr(get_dispatch_key(device), layer_data_map);
+
+ skipCall |= validateMemoryIsMapped(my_data, memRangeCount, pMemRanges);
+ if (VK_FALSE == skipCall) {
+ result = my_data->device_dispatch_table->InvalidateMappedMemoryRanges(device, memRangeCount, pMemRanges);
+ }
+ return result;
}
VK_LAYER_EXPORT void VKAPI vkDestroyFence(VkDevice device, VkFence fence, const VkAllocationCallbacks* pAllocator)
@@ -1643,7 +1781,7 @@
// Validate that img has correct usage flags set
validate_image_usage_flags(my_data, device, pCreateInfo->image,
VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
- false, "vkCreateImageView()", "VK_IMAGE_USAGE_[SAMPLED|STORAGE|COLOR_ATTACHMENT]_BIT");
+ VK_FALSE, "vkCreateImageView()", "VK_IMAGE_USAGE_[SAMPLED|STORAGE|COLOR_ATTACHMENT]_BIT");
loader_platform_thread_unlock_mutex(&globalLock);
}
return result;
@@ -1663,7 +1801,7 @@
// following flags: UNIFORM_TEXEL_BUFFER_BIT or STORAGE_TEXEL_BUFFER_BIT
validate_buffer_usage_flags(my_data, device, pCreateInfo->buffer,
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT,
- false, "vkCreateBufferView()", "VK_BUFFER_USAGE_[STORAGE|UNIFORM]_TEXEL_BUFFER_BIT");
+ VK_FALSE, "vkCreateBufferView()", "VK_BUFFER_USAGE_[STORAGE|UNIFORM]_TEXEL_BUFFER_BIT");
loader_platform_thread_unlock_mutex(&globalLock);
}
return result;
@@ -2425,7 +2563,7 @@
} else {
const size_t count = *pCount;
MT_SWAP_CHAIN_INFO *pInfo = my_data->swapchainMap[swapchain];
- const bool mismatch = (pInfo->images.size() != count ||
+ const VkBool32 mismatch = (pInfo->images.size() != count ||
memcmp(&pInfo->images[0], pSwapchainImages, sizeof(pInfo->images[0]) * count));
if (mismatch) {
@@ -2526,6 +2664,10 @@
return (PFN_vkVoidFunction) vkMapMemory;
if (!strcmp(funcName, "vkUnmapMemory"))
return (PFN_vkVoidFunction) vkUnmapMemory;
+ if (!strcmp(funcName, "vkFlushMappedMemoryRanges"))
+ return (PFN_vkVoidFunction) vkFlushMappedMemoryRanges;
+ if (!strcmp(funcName, "vkInvalidateMappedMemoryRanges"))
+ return (PFN_vkVoidFunction) vkInvalidateMappedMemoryRanges;
if (!strcmp(funcName, "vkDestroyFence"))
return (PFN_vkVoidFunction) vkDestroyFence;
if (!strcmp(funcName, "vkDestroyBuffer"))
diff --git a/layers/mem_tracker.h b/layers/mem_tracker.h
index 7fb03bc..8cfcb0c 100644
--- a/layers/mem_tracker.h
+++ b/layers/mem_tracker.h
@@ -1,6 +1,7 @@
/*
*
* Copyright (C) 2015 Valve Corporation
+ * Copyright (C) 2015 Google, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -60,6 +61,11 @@
MEMTRACK_SEMAPHORE_STATE_WAIT, // Semaphore is in wait state
} MtSemaphoreState;
+struct MemRange {
+ VkDeviceSize offset;
+ VkDeviceSize size;
+};
+
/*
* Data Structure overview
* There are 4 global STL(' maps
@@ -98,12 +104,15 @@
// Data struct for tracking memory object
struct MT_MEM_OBJ_INFO {
- void* object; // Dispatchable object used to create this memory (device of swapchain)
- uint32_t refCount; // Count of references (obj bindings or CB use)
+ void* object; // Dispatchable object used to create this memory (device of swapchain)
+ uint32_t refCount; // Count of references (obj bindings or CB use)
VkDeviceMemory mem;
- VkMemoryAllocateInfo allocInfo;
- list<MT_OBJ_HANDLE_TYPE> pObjBindings; // list container of objects bound to this memory
- list<VkCommandBuffer> pCommandBufferBindings; // list container of cmd buffers that reference this mem object
+ VkMemoryAllocateInfo allocInfo;
+ list<MT_OBJ_HANDLE_TYPE> pObjBindings; // list container of objects bound to this memory
+ list<VkCommandBuffer> pCommandBufferBindings; // list container of cmd buffers that reference this mem object
+ list<VkCommandBuffer> pCmdBufferBindings; // list container of cmd buffers that reference this mem object
+ MemRange memRange;
+ void *pData, *pDriverData;
};
// This only applies to Buffers and Images, which can have memory bound to them