Skip to content

Commit 185128d

Browse files
layers: Handle partial bound image/buffer
1 parent b495e81 commit 185128d

File tree

8 files changed

+127
-27
lines changed

8 files changed

+127
-27
lines changed

layers/best_practices/best_practices_validation.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,11 @@ class BestPractices : public ValidationStateTracker {
742742

743743
void ManualPostCallRecordEndCommandBuffer(VkCommandBuffer commandBuffer, const RecordObject& record_obj);
744744

745+
void ManualPostCallRecordBindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos,
746+
const RecordObject& record_obj);
747+
void ManualPostCallRecordBindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos,
748+
const RecordObject& record_obj);
749+
745750
void ManualPostCallRecordCreateComputePipelines(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount,
746751
const VkComputePipelineCreateInfo* pCreateInfos,
747752
const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines,
@@ -834,24 +839,23 @@ class BestPractices : public ValidationStateTracker {
834839
// Include code-generated functions
835840
#include "generated/best_practices.h"
836841
protected:
837-
std::shared_ptr<vvl::CommandBuffer> CreateCmdBufferState(VkCommandBuffer cb, const VkCommandBufferAllocateInfo* create_info,
842+
std::shared_ptr<vvl::CommandBuffer> CreateCmdBufferState(VkCommandBuffer handle, const VkCommandBufferAllocateInfo* create_info,
838843
const vvl::CommandPool* pool) final;
839844

840-
std::shared_ptr<vvl::Swapchain> CreateSwapchainState(const VkSwapchainCreateInfoKHR* create_info,
841-
VkSwapchainKHR swapchain) final;
845+
std::shared_ptr<vvl::Swapchain> CreateSwapchainState(const VkSwapchainCreateInfoKHR* create_info, VkSwapchainKHR handle) final;
842846

843-
std::shared_ptr<vvl::PhysicalDevice> CreatePhysicalDeviceState(VkPhysicalDevice phys_dev) final;
847+
std::shared_ptr<vvl::PhysicalDevice> CreatePhysicalDeviceState(VkPhysicalDevice handle) final;
844848

845-
std::shared_ptr<vvl::Image> CreateImageState(VkImage img, const VkImageCreateInfo* pCreateInfo,
849+
std::shared_ptr<vvl::Image> CreateImageState(VkImage handle, const VkImageCreateInfo* pCreateInfo,
846850
VkFormatFeatureFlags2KHR features) final;
847851

848-
std::shared_ptr<vvl::Image> CreateImageState(VkImage img, const VkImageCreateInfo* pCreateInfo, VkSwapchainKHR swapchain,
852+
std::shared_ptr<vvl::Image> CreateImageState(VkImage handle, const VkImageCreateInfo* pCreateInfo, VkSwapchainKHR swapchain,
849853
uint32_t swapchain_index, VkFormatFeatureFlags2KHR features) final;
850854

851-
std::shared_ptr<vvl::DescriptorPool> CreateDescriptorPoolState(VkDescriptorPool pool,
855+
std::shared_ptr<vvl::DescriptorPool> CreateDescriptorPoolState(VkDescriptorPool handle,
852856
const VkDescriptorPoolCreateInfo* pCreateInfo) final;
853857

854-
std::shared_ptr<vvl::DeviceMemory> CreateDeviceMemoryState(VkDeviceMemory mem, const VkMemoryAllocateInfo* p_alloc_info,
858+
std::shared_ptr<vvl::DeviceMemory> CreateDeviceMemoryState(VkDeviceMemory handle, const VkMemoryAllocateInfo* p_alloc_info,
855859
uint64_t fake_address, const VkMemoryType& memory_type,
856860
const VkMemoryHeap& memory_heap,
857861
std::optional<vvl::DedicatedBinding>&& dedicated_binding,

layers/best_practices/bp_device_memory.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,26 @@ std::shared_ptr<vvl::DeviceMemory> BestPractices::CreateDeviceMemoryState(
311311
return std::static_pointer_cast<vvl::DeviceMemory>(std::make_shared<bp_state::DeviceMemory>(
312312
handle, pAllocateInfo, fake_address, memory_type, memory_heap, std::move(dedicated_binding), physical_device_count));
313313
}
314+
315+
void BestPractices::ManualPostCallRecordBindBufferMemory2(VkDevice device, uint32_t bindInfoCount,
316+
const VkBindBufferMemoryInfo* pBindInfos,
317+
const RecordObject& record_obj) {
318+
if (record_obj.result != VK_SUCCESS && bindInfoCount > 1) {
319+
// Details of check found in https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/5527
320+
LogWarning(
321+
"BestPractices-Partial-Bound-Buffer", device, record_obj.location,
322+
"all buffer are now in an indeterminate state because the call failed to return VK_SUCCESS. The best action to take "
323+
"is to destroy the buffers instead of trying to rebind");
324+
}
325+
}
326+
327+
void BestPractices::ManualPostCallRecordBindImageMemory2(VkDevice device, uint32_t bindInfoCount,
328+
const VkBindImageMemoryInfo* pBindInfos, const RecordObject& record_obj) {
329+
if (record_obj.result != VK_SUCCESS && bindInfoCount > 1) {
330+
// Details of check found in https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/5527
331+
LogWarning(
332+
"BestPractices-Partial-Bound-Image", device, record_obj.location,
333+
"all image are now in an indeterminate state because the call failed to return VK_SUCCESS. The best action to take is "
334+
"to destroy the images instead of trying to rebind");
335+
}
336+
}

layers/core_checks/cc_device_memory.cpp

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -288,19 +288,17 @@ bool CoreChecks::ValidateSetMemBinding(const vvl::DeviceMemory &memory_state, co
288288

289289
const bool bind_2 = (loc.function != Func::vkBindBufferMemory) && (loc.function != Func::vkBindImageMemory);
290290
auto typed_handle = mem_binding.Handle();
291+
const bool is_buffer = typed_handle.type == kVulkanObjectTypeBuffer;
291292

292293
if (mem_binding.sparse) {
293-
const char *vuid = nullptr;
294-
const char *handle_type = nullptr;
295-
if (typed_handle.type == kVulkanObjectTypeBuffer) {
296-
handle_type = "BUFFER";
294+
const char *vuid = kVUIDUndefined;
295+
const char *handle_type = is_buffer ? "BUFFER" : "IMAGE";
296+
if (is_buffer) {
297297
vuid = bind_2 ? "VUID-VkBindBufferMemoryInfo-buffer-01030" : "VUID-vkBindBufferMemory-buffer-01030";
298-
} else if (typed_handle.type == kVulkanObjectTypeImage) {
299-
handle_type = "IMAGE";
300-
vuid = bind_2 ? "VUID-VkBindImageMemoryInfo-image-01045" : "VUID-vkBindImageMemory-image-01045";
301298
} else {
302-
assert(false); // Unsupported object type
299+
vuid = bind_2 ? "VUID-VkBindImageMemoryInfo-image-01045" : "VUID-vkBindImageMemory-image-01045";
303300
}
301+
304302
const LogObjectList objlist(memory_state.Handle(), typed_handle);
305303
skip |= LogError(vuid, objlist, loc,
306304
"attempting to bind %s to %s which was created with sparse memory flags "
@@ -309,19 +307,30 @@ bool CoreChecks::ValidateSetMemBinding(const vvl::DeviceMemory &memory_state, co
309307
}
310308

311309
const auto *prev_binding = mem_binding.MemState();
312-
if (prev_binding) {
313-
const char *vuid = nullptr;
314-
if (typed_handle.type == kVulkanObjectTypeBuffer) {
310+
if (prev_binding || mem_binding.partial_bound) {
311+
const char *vuid = kVUIDUndefined;
312+
if (is_buffer) {
315313
vuid = bind_2 ? "VUID-VkBindBufferMemoryInfo-buffer-07459" : "VUID-vkBindBufferMemory-buffer-07459";
316-
} else if (typed_handle.type == kVulkanObjectTypeImage) {
314+
} else {
317315
vuid = bind_2 ? "VUID-VkBindImageMemoryInfo-image-07460" : "VUID-vkBindImageMemory-image-07460";
316+
}
317+
318+
if (mem_binding.partial_bound) {
319+
Func bind_call = is_buffer ? Func::vkBindBufferMemory2 : Func::vkBindImageMemory2;
320+
const char *handle_type = is_buffer ? "buffer" : "image";
321+
const LogObjectList objlist(memory_state.Handle(), typed_handle);
322+
skip |= LogError(
323+
vuid, objlist, loc,
324+
"attempting to bind %s to %s which is in an indeterminate (possibly bound) state. A previous call to %s failed and "
325+
"we have to assume the %s was bound (but best advise is to handle the case and recreate the %s).",
326+
FormatHandle(memory_state.Handle()).c_str(), FormatHandle(typed_handle).c_str(), String(bind_call), handle_type,
327+
handle_type);
318328
} else {
319-
assert(false); // Unsupported object type
329+
const LogObjectList objlist(memory_state.Handle(), typed_handle, prev_binding->Handle());
330+
skip |= LogError(vuid, objlist, loc, "attempting to bind %s to %s which has already been bound to %s.",
331+
FormatHandle(memory_state.Handle()).c_str(), FormatHandle(typed_handle).c_str(),
332+
FormatHandle(prev_binding->Handle()).c_str());
320333
}
321-
const LogObjectList objlist(memory_state.Handle(), typed_handle, prev_binding->Handle());
322-
skip |= LogError(vuid, objlist, loc, "attempting to bind %s to %s which has already been bound to %s.",
323-
FormatHandle(memory_state.Handle()).c_str(), FormatHandle(typed_handle).c_str(),
324-
FormatHandle(prev_binding->Handle()).c_str());
325334
}
326335
return skip;
327336
}
@@ -1063,7 +1072,6 @@ bool CoreChecks::PreCallValidateBindBufferMemory(VkDevice device, VkBuffer buffe
10631072
bool CoreChecks::PreCallValidateBindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo *pBindInfos,
10641073
const ErrorObject &error_obj) const {
10651074
bool skip = false;
1066-
10671075
for (uint32_t i = 0; i < bindInfoCount; i++) {
10681076
const Location loc = error_obj.location.dot(Field::pBindInfos, i);
10691077
skip |= ValidateBindBufferMemory(pBindInfos[i].buffer, pBindInfos[i].memory, pBindInfos[i].memoryOffset,
@@ -2061,7 +2069,16 @@ bool CoreChecks::PreCallValidateBindImageMemory2(VkDevice device, uint32_t bindI
20612069

20622070
void CoreChecks::PostCallRecordBindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo *pBindInfos,
20632071
const RecordObject &record_obj) {
2064-
if (VK_SUCCESS != record_obj.result) return;
2072+
if (VK_SUCCESS != record_obj.result) {
2073+
if (bindInfoCount > 1) {
2074+
for (uint32_t i = 0; i < bindInfoCount; i++) {
2075+
if (auto image_state = Get<vvl::Image>(pBindInfos[i].image)) {
2076+
image_state->partial_bound = true;
2077+
}
2078+
}
2079+
}
2080+
return;
2081+
}
20652082
StateTracker::PostCallRecordBindImageMemory2(device, bindInfoCount, pBindInfos, record_obj);
20662083

20672084
for (uint32_t i = 0; i < bindInfoCount; i++) {

layers/state_tracker/device_memory_state.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,10 @@ class Bindable : public StateObject {
314314
const VkExternalMemoryHandleTypeFlags external_memory_handle_types;
315315
const bool sparse; // Is this object being bound with sparse memory or not?
316316
const bool unprotected; // can't be used for protected memory
317+
318+
// For when an array of binds don't succeed and the object is in an indeterminate state
319+
bool partial_bound = false;
320+
317321
private:
318322
mutable bool need_to_recache_invalid_memory_ = false;
319323
BindableMemoryTracker *memory_tracker_;

layers/state_tracker/state_tracker.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,6 +1614,17 @@ void ValidationStateTracker::PostCallRecordBindBufferMemory(VkDevice device, VkB
16141614
void ValidationStateTracker::PostCallRecordBindBufferMemory2(VkDevice device, uint32_t bindInfoCount,
16151615
const VkBindBufferMemoryInfo *pBindInfos,
16161616
const RecordObject &record_obj) {
1617+
if (VK_SUCCESS != record_obj.result) {
1618+
if (bindInfoCount > 1) {
1619+
for (uint32_t i = 0; i < bindInfoCount; i++) {
1620+
if (auto buffer_state = Get<vvl::Buffer>(pBindInfos[i].buffer)) {
1621+
buffer_state->partial_bound = true;
1622+
}
1623+
}
1624+
}
1625+
return;
1626+
}
1627+
16171628
for (uint32_t i = 0; i < bindInfoCount; i++) {
16181629
UpdateBindBufferMemoryState(pBindInfos[i].buffer, pBindInfos[i].memory, pBindInfos[i].memoryOffset);
16191630
}

layers/vulkan/generated/best_practices.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,7 @@ void BestPractices::PostCallRecordResetCommandBuffer(VkCommandBuffer commandBuff
747747
void BestPractices::PostCallRecordBindBufferMemory2(VkDevice device, uint32_t bindInfoCount,
748748
const VkBindBufferMemoryInfo* pBindInfos, const RecordObject& record_obj) {
749749
ValidationStateTracker::PostCallRecordBindBufferMemory2(device, bindInfoCount, pBindInfos, record_obj);
750+
ManualPostCallRecordBindBufferMemory2(device, bindInfoCount, pBindInfos, record_obj);
750751

751752
if (record_obj.result < VK_SUCCESS) {
752753
LogErrorCode(record_obj);
@@ -756,6 +757,7 @@ void BestPractices::PostCallRecordBindBufferMemory2(VkDevice device, uint32_t bi
756757
void BestPractices::PostCallRecordBindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos,
757758
const RecordObject& record_obj) {
758759
ValidationStateTracker::PostCallRecordBindImageMemory2(device, bindInfoCount, pBindInfos, record_obj);
760+
ManualPostCallRecordBindImageMemory2(device, bindInfoCount, pBindInfos, record_obj);
759761

760762
if (record_obj.result < VK_SUCCESS) {
761763
LogErrorCode(record_obj);

scripts/generators/best_practices_generator.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ def __init__(self):
6666
'vkGetPhysicalDeviceSurfaceFormats2KHR',
6767
'vkGetPhysicalDeviceDisplayPlanePropertiesKHR',
6868
'vkGetSwapchainImagesKHR',
69+
'vkBindBufferMemory2',
70+
'vkBindImageMemory2',
6971
# AMD tracked
7072
'vkCreateComputePipelines',
7173
'vkCmdPipelineBarrier',

tests/unit/memory.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2298,4 +2298,41 @@ TEST_F(NegativeMemory, BadMemoryBindMemory2) {
22982298
m_errorMonitor->SetDesiredError("VUID-VkBindImageMemoryInfo-pNext-01632");
22992299
vk::BindImageMemory2KHR(device(), 1, &image_bind_info);
23002300
m_errorMonitor->VerifyFound();
2301+
}
2302+
2303+
// TODO - Need a way to trigger Test ICD to fail call
2304+
TEST_F(NegativeMemory, DISABLED_PartialBoundBuffer) {
2305+
TEST_DESCRIPTION("https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/5527");
2306+
AddRequiredExtensions(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
2307+
RETURN_IF_SKIP(Init());
2308+
2309+
if (!IsPlatformMockICD()) {
2310+
GTEST_SKIP() << "Test needs to force a failure";
2311+
}
2312+
2313+
VkBufferCreateInfo buffer_create_info = vku::InitStructHelper();
2314+
buffer_create_info.size = 1024;
2315+
buffer_create_info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
2316+
vkt::Buffer buffer_0(*m_device, buffer_create_info, vkt::no_mem);
2317+
vkt::Buffer buffer_1(*m_device, buffer_create_info, vkt::no_mem);
2318+
2319+
VkMemoryAllocateInfo alloc_info = vkt::DeviceMemory::get_resource_alloc_info(*m_device, buffer_0.memory_requirements(), 0);
2320+
vkt::DeviceMemory buffer_memory(*m_device, alloc_info);
2321+
2322+
VkBindBufferMemoryInfo bind_buffer_infos[2];
2323+
bind_buffer_infos[0] = vku::InitStructHelper();
2324+
bind_buffer_infos[0].buffer = buffer_0.handle();
2325+
bind_buffer_infos[0].memory = buffer_memory.handle();
2326+
bind_buffer_infos[1] = vku::InitStructHelper();
2327+
bind_buffer_infos[1].buffer = buffer_1.handle();
2328+
bind_buffer_infos[1].memory = buffer_memory.handle();
2329+
2330+
VkResult result = vk::BindBufferMemory2KHR(device(), 2, bind_buffer_infos);
2331+
if (result == VK_SUCCESS) {
2332+
GTEST_SKIP() << "Test needs a to fail call";
2333+
}
2334+
2335+
m_errorMonitor->SetDesiredError("VUID-vkBindBufferMemory-buffer-07459");
2336+
vk::BindBufferMemory(device(), buffer_0, buffer_memory, 0);
2337+
m_errorMonitor->VerifyFound();
23012338
}

0 commit comments

Comments
 (0)