Skip to content

Commit b7bc28f

Browse files
committed
[ET-VK][ez] Fix Vulkan Validation layer errors due to consecutive command buffer encoding
## Changes * In `VulkanBackend.cpp` do not call `encode_execute()` during model load if the model compile spec specifies `requires_dynamic_shapes` as true * In test files, do not call `encode_execute()` if `propagate_resize()` is subsequently called. ## Motivation Recently, it was discovered that a command buffer re-encode was required to update push constant values. This means that for dynamic shapes to work correctly, `encode_execute()` must be called after updating tensor sizes. As a result, `propagate_resize()` now calls `encode_execute()` internally. This results in scenarios where `encode_execute()` is called once during model load, then again right before the first inference during `propagate_resize()`, without actually executing the command buffer in-between. This causes Validation layer errors like ``` UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout(ERROR / SPEC): msgNum: 1303270965 - Validation Error: [ UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout ] Object 0: handle = 0x24086224ec0, type = VK_OBJECT_TYPE_COMMAND_BUFFER; Object 1: handle = 0x88d2b500000000e2, type = VK_OBJECT_TYPE_IMAGE; | MessageID = 0x4dae5635 | vkQueueSubmit(): pSubmits[0].pCommandBuffers[0] command buffer VkCommandBuffer 0x24086224ec0[] expects VkImage 0x88d2b500000000e2[] (subresource: aspectMask VK_IMAGE_ASPECT_COLOR_BIT array layer 0, mip level 0) to be in layout VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL--instead, current layout is VK_IMAGE_LAYOUT_UNDEFINED. Objects: 2 [0] 0x24086224ec0, type: 6, name: NULL [1] 0x88d2b500000000e2, type: 10, name: NULL UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout(ERROR / SPEC): msgNum: 1303270965 - Validation Error: [ UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout ] Object 0: handle = 0x24086224ec0, type = VK_OBJECT_TYPE_COMMAND_BUFFER; Object 1: handle = 0x6caffc00000000e3, type = VK_OBJECT_TYPE_IMAGE; | MessageID = 0x4dae5635 | vkQueueSubmit(): pSubmits[0].pCommandBuffers[0] command buffer VkCommandBuffer 0x24086224ec0[] expects VkImage 0x6caffc00000000e3[] (subresource: aspectMask VK_IMAGE_ASPECT_COLOR_BIT array layer 0, mip level 0) to be in layout VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL--instead, current layout is VK_IMAGE_LAYOUT_UNDEFINED. Objects: 2 [0] 0x24086224ec0, type: 6, name: NULL [1] 0x6caffc00000000e3, type: 10, name: NULL ``` because the last access information of image/buffer resources are inaccurate during the second command buffer encoding, since the first command buffer never executed. ## Perf Impact * Performance improvement for first inference of dynamic shape models if actual tensor sizes are much smaller than maximum possible sizes * No impact for non-dynamic shape models Differential Revision: [D76047203](https://our.internmc.facebook.com/intern/diff/D76047203/) ghstack-source-id: 288459856 Pull Request resolved: #11401
1 parent 4b8531c commit b7bc28f

File tree

7 files changed

+33
-11
lines changed

7 files changed

+33
-11
lines changed

backends/vulkan/runtime/VulkanBackend.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <type_traits>
3131
#include <vector>
3232

33+
#include <iostream>
34+
3335
namespace executorch {
3436
namespace backends {
3537
namespace vulkan {
@@ -140,6 +142,14 @@ GraphConfig get_graph_config(ArrayRef<CompileSpec>& compile_specs) {
140142

141143
config.set_memory_layout_override(memory_layout);
142144
}
145+
if (strcmp(spec.key, "require_dynamic_shapes") == 0) {
146+
ET_CHECK_MSG(value_size == sizeof(uint8_t), "Unexpected value size!");
147+
bool value = getBool(value_data);
148+
149+
if (value) {
150+
config.expect_dynamic_shapes = true;
151+
}
152+
}
143153
}
144154
#ifdef ET_EVENT_TRACER_ENABLED
145155
config.enable_querypool = true;
@@ -500,9 +510,12 @@ class VulkanBackend final : public ::executorch::runtime::BackendInterface {
500510
compute_graph->encode_prepack();
501511
compute_graph->prepack();
502512

503-
// TODO(ssjia): remove this once we can batch compile compute pipelines
504-
// during prepare().
505-
compute_graph->encode_execute();
513+
// If dynamic shapes are not expected, then the command buffer only needs to
514+
// be encoded once. Otherwise, wait until the first inference to encode the
515+
// the command buffer, when actual input shapes are known.
516+
if (!compute_graph->graphconfig().expect_dynamic_shapes) {
517+
compute_graph->encode_execute();
518+
}
506519

507520
return Error::Ok;
508521
}
@@ -574,7 +587,9 @@ class VulkanBackend final : public ::executorch::runtime::BackendInterface {
574587
// constants are updated and DynamicDispatchNode can update the compute
575588
// shader, global workgroup size, and local workgroup size to perform the
576589
// model inference.
577-
if (should_propagate_resize) {
590+
if (should_propagate_resize ||
591+
(compute_graph->graphconfig().expect_dynamic_shapes &&
592+
compute_graph->execute_count() == 0u)) {
578593
compute_graph->propagate_resize();
579594
}
580595

backends/vulkan/runtime/VulkanDelegateHeader.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ uint32_t getUInt16LE(const uint8_t* data) {
6060
return (uint32_t)data[0] | ((uint32_t)data[1] << 8);
6161
}
6262

63+
bool getBool(const uint8_t* data) {
64+
return data[0] != 0;
65+
}
66+
6367
bool VulkanDelegateHeader::is_valid() const {
6468
if (header_size < kExpectedSize) {
6569
return false;

backends/vulkan/runtime/VulkanDelegateHeader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ uint64_t getUInt64LE(const uint8_t* data);
1919
uint32_t getUInt32LE(const uint8_t* data);
2020
uint32_t getUInt16LE(const uint8_t* data);
2121

22+
// Bool is serialized as a single byte
23+
bool getBool(const uint8_t* data);
24+
2225
struct VulkanDelegateHeader {
2326
bool is_valid() const;
2427

backends/vulkan/runtime/graph/GraphConfig.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ GraphConfig::GraphConfig() {
6363

6464
enable_local_wg_size_override = false;
6565
local_wg_size_override = {};
66+
67+
expect_dynamic_shapes = false;
6668
}
6769

6870
void GraphConfig::set_storage_type_override(utils::StorageType storage_type) {

backends/vulkan/runtime/graph/GraphConfig.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ struct GraphConfig final {
3333
bool enable_local_wg_size_override;
3434
utils::uvec3 local_wg_size_override;
3535

36+
// Whether or not the ComputeGraph should expect input shapes to be dynamic
37+
bool expect_dynamic_shapes;
38+
3639
// Generate a default graph config with pre-configured settings
3740
explicit GraphConfig();
3841

backends/vulkan/test/op_tests/utils/gen_computegraph.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,8 @@ def gen_graph_build_code(self, include_declarations: bool = True) -> str:
618618
graph_build += f"{self.graph}{self.dot}prepare();\n"
619619
graph_build += f"{self.graph}{self.dot}encode_prepack();\n"
620620
graph_build += f"{self.graph}{self.dot}prepack();\n"
621-
graph_build += f"{self.graph}{self.dot}encode_execute();\n"
621+
if not self.include_io:
622+
graph_build += f"{self.graph}{self.dot}encode_execute();\n"
622623

623624
graph_build += "\n"
624625
return graph_build

backends/vulkan/test/vulkan_compute_api_test.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,7 +1536,6 @@ TEST(VulkanComputeGraphTest, test_simple_shared_objects_with_resize) {
15361536
EXPECT_EQ(get_vma_allocation_count(), expected_vma_allocation_count);
15371537

15381538
graph.prepare();
1539-
graph.encode_execute();
15401539

15411540
// +3: shared memory allocations for tensors
15421541
expected_vma_allocation_count += 3;
@@ -1743,7 +1742,6 @@ TEST(VulkanComputeGraphTest, test_large_graph) {
17431742
out.staging = graph.set_output_tensor(out.value);
17441743

17451744
graph.prepare();
1746-
graph.encode_execute();
17471745

17481746
auto build_end_time = std::chrono::system_clock::now();
17491747

@@ -1820,7 +1818,6 @@ void test_clone(
18201818
out.staging = graph.set_output_tensor(out.value);
18211819

18221820
graph.prepare();
1823-
graph.encode_execute();
18241821

18251822
fill_vtensor(graph, a, 0.0f, /*iota = */ true);
18261823

@@ -2955,7 +2952,6 @@ void test_grid_priors(
29552952
graph.prepare();
29562953
graph.encode_prepack();
29572954
graph.prepack();
2958-
graph.encode_execute();
29592955

29602956
vTensorPtr t_in = graph.get_tensor(in.value);
29612957
vTensorPtr t_out = graph.get_tensor(out.value);
@@ -3058,7 +3054,6 @@ void test_transpose_view_mm(
30583054
graph.prepare();
30593055
graph.encode_prepack();
30603056
graph.prepack();
3061-
graph.encode_execute();
30623057

30633058
for (int i = 1; i < 4; i++) {
30643059
float val_mat1 = i;
@@ -3125,7 +3120,6 @@ void test_to_copy() {
31253120
graph.prepare();
31263121
graph.encode_prepack();
31273122
graph.prepack();
3128-
graph.encode_execute();
31293123
graph.propagate_resize();
31303124
graph.execute();
31313125

0 commit comments

Comments
 (0)