| // Copyright 2019 The IREE Authors |
| // |
| // Licensed under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| #ifndef IREE_HAL_CTS_COMMAND_BUFFER_TEST_H_ |
| #define IREE_HAL_CTS_COMMAND_BUFFER_TEST_H_ |
| |
| #include <cstdint> |
| #include <vector> |
| |
| #include "iree/base/api.h" |
| #include "iree/hal/api.h" |
| #include "iree/hal/cts/cts_test_base.h" |
| #include "iree/testing/gtest.h" |
| #include "iree/testing/status_matchers.h" |
| |
| // TODO(scotttodd): split into several tests, for example: |
| // command_buffer_recording_test (recording/lifetime) |
| // command_buffer_dispatch_test |
| // command_buffer_fill_test (filling buffers) |
| // command_buffer_e2e_test (barriers, dispatches) |
| |
| namespace iree { |
| namespace hal { |
| namespace cts { |
| |
| using ::testing::ContainerEq; |
| |
| class command_buffer_test : public CtsTestBase { |
| protected: |
| std::vector<uint8_t> RunFillBufferTest(iree_device_size_t buffer_size, |
| iree_device_size_t target_offset, |
| iree_device_size_t fill_length, |
| const void* pattern, |
| iree_host_size_t pattern_length) { |
| iree_hal_command_buffer_t* command_buffer; |
| IREE_CHECK_OK(iree_hal_command_buffer_create( |
| device_, IREE_HAL_COMMAND_BUFFER_MODE_ONE_SHOT, |
| IREE_HAL_COMMAND_CATEGORY_ANY, IREE_HAL_QUEUE_AFFINITY_ANY, |
| &command_buffer)); |
| iree_hal_buffer_t* device_buffer; |
| IREE_CHECK_OK(iree_hal_allocator_allocate_buffer( |
| iree_hal_device_allocator(device_), |
| IREE_HAL_MEMORY_TYPE_DEVICE_LOCAL | IREE_HAL_MEMORY_TYPE_HOST_VISIBLE, |
| IREE_HAL_BUFFER_USAGE_ALL, buffer_size, iree_const_byte_span_empty(), |
| &device_buffer)); |
| |
| IREE_CHECK_OK(iree_hal_command_buffer_begin(command_buffer)); |
| // Start with a zero fill on the entire buffer... |
| uint8_t zero_val = 0x0; |
| IREE_CHECK_OK(iree_hal_command_buffer_fill_buffer( |
| command_buffer, device_buffer, /*target_offset=*/0, |
| /*length=*/buffer_size, &zero_val, |
| /*pattern_length=*/sizeof(zero_val))); |
| // (buffer barrier between the fill operations) |
| iree_hal_buffer_barrier_t buffer_barrier; |
| buffer_barrier.source_scope = IREE_HAL_ACCESS_SCOPE_TRANSFER_WRITE; |
| buffer_barrier.target_scope = IREE_HAL_ACCESS_SCOPE_TRANSFER_WRITE | |
| IREE_HAL_ACCESS_SCOPE_DISPATCH_WRITE; |
| buffer_barrier.buffer = device_buffer; |
| buffer_barrier.offset = 0; |
| buffer_barrier.length = buffer_size; |
| IREE_CHECK_OK(iree_hal_command_buffer_execution_barrier( |
| command_buffer, IREE_HAL_EXECUTION_STAGE_TRANSFER, |
| IREE_HAL_EXECUTION_STAGE_TRANSFER | IREE_HAL_EXECUTION_STAGE_DISPATCH, |
| IREE_HAL_EXECUTION_BARRIER_FLAG_NONE, /*memory_barrier_count=*/0, NULL, |
| /*buffer_barrier_count=*/1, &buffer_barrier)); |
| // ... then fill the pattern on top. |
| IREE_CHECK_OK(iree_hal_command_buffer_fill_buffer( |
| command_buffer, device_buffer, |
| /*target_offset=*/target_offset, /*length=*/fill_length, |
| /*pattern=*/pattern, |
| /*pattern_length=*/pattern_length)); |
| IREE_CHECK_OK(iree_hal_command_buffer_end(command_buffer)); |
| IREE_CHECK_OK(SubmitCommandBufferAndWait(IREE_HAL_COMMAND_CATEGORY_ANY, |
| command_buffer)); |
| |
| std::vector<uint8_t> actual_data(buffer_size); |
| IREE_CHECK_OK( |
| iree_hal_buffer_read_data(device_buffer, /*source_offset=*/0, |
| /*target_buffer=*/actual_data.data(), |
| /*data_length=*/buffer_size)); |
| |
| iree_hal_command_buffer_release(command_buffer); |
| iree_hal_buffer_release(device_buffer); |
| |
| return actual_data; |
| } |
| |
| static constexpr iree_device_size_t kBufferSize = 4096; |
| }; |
| |
| TEST_P(command_buffer_test, Create) { |
| iree_hal_command_buffer_t* command_buffer; |
| IREE_ASSERT_OK(iree_hal_command_buffer_create( |
| device_, IREE_HAL_COMMAND_BUFFER_MODE_ONE_SHOT, |
| IREE_HAL_COMMAND_CATEGORY_DISPATCH, IREE_HAL_QUEUE_AFFINITY_ANY, |
| &command_buffer)); |
| |
| EXPECT_TRUE((iree_hal_command_buffer_allowed_categories(command_buffer) & |
| IREE_HAL_COMMAND_CATEGORY_DISPATCH) == |
| IREE_HAL_COMMAND_CATEGORY_DISPATCH); |
| |
| iree_hal_command_buffer_release(command_buffer); |
| } |
| |
| TEST_P(command_buffer_test, BeginEnd) { |
| iree_hal_command_buffer_t* command_buffer; |
| IREE_ASSERT_OK(iree_hal_command_buffer_create( |
| device_, IREE_HAL_COMMAND_BUFFER_MODE_ONE_SHOT, |
| IREE_HAL_COMMAND_CATEGORY_DISPATCH, IREE_HAL_QUEUE_AFFINITY_ANY, |
| &command_buffer)); |
| |
| IREE_ASSERT_OK(iree_hal_command_buffer_begin(command_buffer)); |
| IREE_ASSERT_OK(iree_hal_command_buffer_end(command_buffer)); |
| |
| iree_hal_command_buffer_release(command_buffer); |
| } |
| |
| TEST_P(command_buffer_test, SubmitEmpty) { |
| iree_hal_command_buffer_t* command_buffer; |
| IREE_ASSERT_OK(iree_hal_command_buffer_create( |
| device_, IREE_HAL_COMMAND_BUFFER_MODE_ONE_SHOT, |
| IREE_HAL_COMMAND_CATEGORY_DISPATCH, IREE_HAL_QUEUE_AFFINITY_ANY, |
| &command_buffer)); |
| |
| IREE_ASSERT_OK(iree_hal_command_buffer_begin(command_buffer)); |
| IREE_ASSERT_OK(iree_hal_command_buffer_end(command_buffer)); |
| |
| IREE_ASSERT_OK(SubmitCommandBufferAndWait(IREE_HAL_COMMAND_CATEGORY_DISPATCH, |
| command_buffer)); |
| |
| iree_hal_command_buffer_release(command_buffer); |
| } |
| |
| TEST_P(command_buffer_test, CopyWholeBuffer) { |
| iree_hal_command_buffer_t* command_buffer; |
| IREE_ASSERT_OK(iree_hal_command_buffer_create( |
| device_, IREE_HAL_COMMAND_BUFFER_MODE_ONE_SHOT, |
| IREE_HAL_COMMAND_CATEGORY_TRANSFER, IREE_HAL_QUEUE_AFFINITY_ANY, |
| &command_buffer)); |
| |
| uint8_t i8_val = 0x54; |
| std::vector<uint8_t> reference_buffer(kBufferSize); |
| std::memset(reference_buffer.data(), i8_val, kBufferSize); |
| |
| // Create and fill a host buffer. |
| iree_hal_buffer_t* host_buffer; |
| IREE_ASSERT_OK(iree_hal_allocator_allocate_buffer( |
| device_allocator_, |
| IREE_HAL_MEMORY_TYPE_HOST_VISIBLE | IREE_HAL_MEMORY_TYPE_HOST_CACHED | |
| IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE, |
| IREE_HAL_BUFFER_USAGE_ALL, kBufferSize, |
| iree_make_const_byte_span(reference_buffer.data(), |
| reference_buffer.size()), |
| &host_buffer)); |
| |
| // Create a device buffer. |
| iree_hal_buffer_t* device_buffer; |
| IREE_ASSERT_OK(iree_hal_allocator_allocate_buffer( |
| device_allocator_, |
| IREE_HAL_MEMORY_TYPE_DEVICE_LOCAL | IREE_HAL_MEMORY_TYPE_HOST_VISIBLE, |
| IREE_HAL_BUFFER_USAGE_ALL, kBufferSize, iree_const_byte_span_empty(), |
| &device_buffer)); |
| |
| // Copy the host buffer to the device buffer. |
| IREE_ASSERT_OK(iree_hal_command_buffer_begin(command_buffer)); |
| IREE_ASSERT_OK(iree_hal_command_buffer_copy_buffer( |
| command_buffer, /*source_buffer=*/host_buffer, /*source_offset=*/0, |
| /*target_buffer=*/device_buffer, /*target_offset=*/0, |
| /*length=*/kBufferSize)); |
| IREE_ASSERT_OK(iree_hal_command_buffer_end(command_buffer)); |
| |
| IREE_ASSERT_OK(SubmitCommandBufferAndWait(IREE_HAL_COMMAND_CATEGORY_TRANSFER, |
| command_buffer)); |
| |
| // Read the device buffer and compare. |
| std::vector<uint8_t> actual_data(kBufferSize); |
| IREE_ASSERT_OK(iree_hal_buffer_read_data(device_buffer, /*source_offset=*/0, |
| /*target_buffer=*/actual_data.data(), |
| /*data_length=*/kBufferSize)); |
| EXPECT_THAT(actual_data, ContainerEq(reference_buffer)); |
| |
| // Must release the command buffer before resources used by it. |
| iree_hal_command_buffer_release(command_buffer); |
| iree_hal_buffer_release(device_buffer); |
| iree_hal_buffer_release(host_buffer); |
| } |
| |
| TEST_P(command_buffer_test, CopySubBuffer) { |
| iree_hal_command_buffer_t* command_buffer = NULL; |
| IREE_ASSERT_OK(iree_hal_command_buffer_create( |
| device_, IREE_HAL_COMMAND_BUFFER_MODE_ONE_SHOT, |
| IREE_HAL_COMMAND_CATEGORY_TRANSFER, IREE_HAL_QUEUE_AFFINITY_ANY, |
| &command_buffer)); |
| |
| iree_hal_buffer_t* device_buffer = NULL; |
| IREE_ASSERT_OK(iree_hal_allocator_allocate_buffer( |
| device_allocator_, |
| IREE_HAL_MEMORY_TYPE_DEVICE_LOCAL | IREE_HAL_MEMORY_TYPE_HOST_VISIBLE, |
| IREE_HAL_BUFFER_USAGE_ALL, kBufferSize, iree_const_byte_span_empty(), |
| &device_buffer)); |
| |
| uint8_t i8_val = 0x88; |
| std::vector<uint8_t> reference_buffer(kBufferSize); |
| std::memset(reference_buffer.data() + 8, i8_val, kBufferSize / 2 - 4); |
| |
| // Create another host buffer with a smaller size. |
| std::vector<uint8_t> host_buffer_data(kBufferSize, i8_val); |
| iree_hal_buffer_t* host_buffer = NULL; |
| IREE_ASSERT_OK(iree_hal_allocator_allocate_buffer( |
| device_allocator_, |
| IREE_HAL_MEMORY_TYPE_HOST_VISIBLE | IREE_HAL_MEMORY_TYPE_HOST_CACHED | |
| IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE, |
| IREE_HAL_BUFFER_USAGE_ALL, host_buffer_data.size() / 2, |
| iree_make_const_byte_span(host_buffer_data.data(), |
| host_buffer_data.size() / 2), |
| &host_buffer)); |
| |
| // Copy the host buffer to the device buffer; zero fill the untouched bytes. |
| uint8_t zero_val = 0x0; |
| IREE_ASSERT_OK(iree_hal_command_buffer_begin(command_buffer)); |
| IREE_ASSERT_OK(iree_hal_command_buffer_fill_buffer( |
| command_buffer, device_buffer, /*target_offset=*/0, /*length=*/8, |
| &zero_val, /*pattern_length=*/sizeof(zero_val))); |
| IREE_ASSERT_OK(iree_hal_command_buffer_copy_buffer( |
| command_buffer, /*source_buffer=*/host_buffer, /*source_offset=*/4, |
| /*target_buffer=*/device_buffer, /*target_offset=*/8, |
| /*length=*/kBufferSize / 2 - 4)); |
| IREE_ASSERT_OK(iree_hal_command_buffer_fill_buffer( |
| command_buffer, device_buffer, /*target_offset=*/8 + kBufferSize / 2 - 4, |
| /*length=*/kBufferSize - (8 + kBufferSize / 2 - 4), &zero_val, |
| /*pattern_length=*/sizeof(zero_val))); |
| IREE_ASSERT_OK(iree_hal_command_buffer_end(command_buffer)); |
| |
| IREE_ASSERT_OK(SubmitCommandBufferAndWait(IREE_HAL_COMMAND_CATEGORY_TRANSFER, |
| command_buffer)); |
| |
| // Read the device buffer and compare. |
| std::vector<uint8_t> actual_data(kBufferSize); |
| IREE_ASSERT_OK(iree_hal_buffer_read_data(device_buffer, /*source_offset=*/0, |
| /*target_buffer=*/actual_data.data(), |
| /*data_length=*/kBufferSize)); |
| EXPECT_THAT(actual_data, ContainerEq(reference_buffer)); |
| |
| // Must release the command buffer before resources used by it. |
| iree_hal_command_buffer_release(command_buffer); |
| iree_hal_buffer_release(device_buffer); |
| iree_hal_buffer_release(host_buffer); |
| } |
| |
| TEST_P(command_buffer_test, FillBuffer_pattern1_size1_offset0_length1) { |
| iree_device_size_t buffer_size = 1; |
| iree_device_size_t target_offset = 0; |
| iree_device_size_t fill_length = 1; |
| uint8_t pattern = 0x07; |
| std::vector<uint8_t> reference_buffer{0x07}; |
| std::vector<uint8_t> actual_buffer = |
| RunFillBufferTest(buffer_size, target_offset, fill_length, |
| (void*)&pattern, sizeof(pattern)); |
| EXPECT_THAT(actual_buffer, ContainerEq(reference_buffer)); |
| } |
| |
| TEST_P(command_buffer_test, FillBuffer_pattern1_size5_offset0_length5) { |
| iree_device_size_t buffer_size = 5; |
| iree_device_size_t target_offset = 0; |
| iree_device_size_t fill_length = 5; |
| uint8_t pattern = 0x07; |
| std::vector<uint8_t> reference_buffer{0x07, 0x07, 0x07, 0x07, // |
| 0x07}; |
| std::vector<uint8_t> actual_buffer = |
| RunFillBufferTest(buffer_size, target_offset, fill_length, |
| (void*)&pattern, sizeof(pattern)); |
| EXPECT_THAT(actual_buffer, ContainerEq(reference_buffer)); |
| } |
| |
| TEST_P(command_buffer_test, FillBuffer_pattern1_size16_offset0_length1) { |
| iree_device_size_t buffer_size = 16; |
| iree_device_size_t target_offset = 0; |
| iree_device_size_t fill_length = 1; |
| uint8_t pattern = 0x07; |
| std::vector<uint8_t> reference_buffer{0x07, 0x00, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00}; |
| std::vector<uint8_t> actual_buffer = |
| RunFillBufferTest(buffer_size, target_offset, fill_length, |
| (void*)&pattern, sizeof(pattern)); |
| EXPECT_THAT(actual_buffer, ContainerEq(reference_buffer)); |
| } |
| |
| TEST_P(command_buffer_test, FillBuffer_pattern1_size16_offset0_length3) { |
| iree_device_size_t buffer_size = 16; |
| iree_device_size_t target_offset = 0; |
| iree_device_size_t fill_length = 3; |
| uint8_t pattern = 0x07; |
| std::vector<uint8_t> reference_buffer{0x07, 0x07, 0x07, 0x00, // |
| 0x00, 0x00, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00}; |
| std::vector<uint8_t> actual_buffer = |
| RunFillBufferTest(buffer_size, target_offset, fill_length, |
| (void*)&pattern, sizeof(pattern)); |
| EXPECT_THAT(actual_buffer, ContainerEq(reference_buffer)); |
| } |
| |
| TEST_P(command_buffer_test, FillBuffer_pattern1_size16_offset0_length8) { |
| iree_device_size_t buffer_size = 16; |
| iree_device_size_t target_offset = 0; |
| iree_device_size_t fill_length = 8; |
| uint8_t pattern = 0x07; |
| std::vector<uint8_t> reference_buffer{0x07, 0x07, 0x07, 0x07, // |
| 0x07, 0x07, 0x07, 0x07, // |
| 0x00, 0x00, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00}; |
| std::vector<uint8_t> actual_buffer = |
| RunFillBufferTest(buffer_size, target_offset, fill_length, |
| (void*)&pattern, sizeof(pattern)); |
| EXPECT_THAT(actual_buffer, ContainerEq(reference_buffer)); |
| } |
| |
| TEST_P(command_buffer_test, FillBuffer_pattern1_size16_offset2_length8) { |
| iree_device_size_t buffer_size = 16; |
| iree_device_size_t target_offset = 2; |
| iree_device_size_t fill_length = 8; |
| uint8_t pattern = 0x07; |
| std::vector<uint8_t> reference_buffer{0x00, 0x00, 0x07, 0x07, // |
| 0x07, 0x07, 0x07, 0x07, // |
| 0x07, 0x07, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00}; |
| std::vector<uint8_t> actual_buffer = |
| RunFillBufferTest(buffer_size, target_offset, fill_length, |
| (void*)&pattern, sizeof(pattern)); |
| EXPECT_THAT(actual_buffer, ContainerEq(reference_buffer)); |
| } |
| |
| TEST_P(command_buffer_test, FillBuffer_pattern2_size2_offset0_length2) { |
| iree_device_size_t buffer_size = 2; |
| iree_device_size_t target_offset = 0; |
| iree_device_size_t fill_length = 2; |
| uint16_t pattern = 0xAB23; |
| std::vector<uint8_t> reference_buffer{0x23, 0xAB}; |
| std::vector<uint8_t> actual_buffer = |
| RunFillBufferTest(buffer_size, target_offset, fill_length, |
| (void*)&pattern, sizeof(pattern)); |
| EXPECT_THAT(actual_buffer, ContainerEq(reference_buffer)); |
| } |
| |
| TEST_P(command_buffer_test, FillBuffer_pattern2_size16_offset0_length8) { |
| iree_device_size_t buffer_size = 16; |
| iree_device_size_t target_offset = 0; |
| iree_device_size_t fill_length = 8; |
| uint16_t pattern = 0xAB23; |
| std::vector<uint8_t> reference_buffer{0x23, 0xAB, 0x23, 0xAB, // |
| 0x23, 0xAB, 0x23, 0xAB, // |
| 0x00, 0x00, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00}; |
| std::vector<uint8_t> actual_buffer = |
| RunFillBufferTest(buffer_size, target_offset, fill_length, |
| (void*)&pattern, sizeof(pattern)); |
| EXPECT_THAT(actual_buffer, ContainerEq(reference_buffer)); |
| } |
| |
| TEST_P(command_buffer_test, FillBuffer_pattern2_size16_offset0_length10) { |
| iree_device_size_t buffer_size = 16; |
| iree_device_size_t target_offset = 0; |
| iree_device_size_t fill_length = 10; |
| uint16_t pattern = 0xAB23; |
| std::vector<uint8_t> reference_buffer{0x23, 0xAB, 0x23, 0xAB, // |
| 0x23, 0xAB, 0x23, 0xAB, // |
| 0x23, 0xAB, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00}; |
| std::vector<uint8_t> actual_buffer = |
| RunFillBufferTest(buffer_size, target_offset, fill_length, |
| (void*)&pattern, sizeof(pattern)); |
| EXPECT_THAT(actual_buffer, ContainerEq(reference_buffer)); |
| } |
| |
| TEST_P(command_buffer_test, FillBuffer_pattern2_size16_offset2_length8) { |
| iree_device_size_t buffer_size = 16; |
| iree_device_size_t target_offset = 2; |
| iree_device_size_t fill_length = 8; |
| uint16_t pattern = 0xAB23; |
| std::vector<uint8_t> reference_buffer{0x00, 0x00, 0x23, 0xAB, // |
| 0x23, 0xAB, 0x23, 0xAB, // |
| 0x23, 0xAB, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00}; |
| std::vector<uint8_t> actual_buffer = |
| RunFillBufferTest(buffer_size, target_offset, fill_length, |
| (void*)&pattern, sizeof(pattern)); |
| EXPECT_THAT(actual_buffer, ContainerEq(reference_buffer)); |
| } |
| |
| TEST_P(command_buffer_test, FillBuffer_pattern4_size4_offset0_length4) { |
| iree_device_size_t buffer_size = 4; |
| iree_device_size_t target_offset = 0; |
| iree_device_size_t fill_length = 4; |
| uint32_t pattern = 0xAB23CD45; |
| std::vector<uint8_t> reference_buffer{0x45, 0xCD, 0x23, 0xAB}; |
| std::vector<uint8_t> actual_buffer = |
| RunFillBufferTest(buffer_size, target_offset, fill_length, |
| (void*)&pattern, sizeof(pattern)); |
| EXPECT_THAT(actual_buffer, ContainerEq(reference_buffer)); |
| } |
| |
| TEST_P(command_buffer_test, FillBuffer_pattern4_size16_offset0_length8) { |
| iree_device_size_t buffer_size = 16; |
| iree_device_size_t target_offset = 0; |
| iree_device_size_t fill_length = 8; |
| uint32_t pattern = 0xAB23CD45; |
| std::vector<uint8_t> reference_buffer{0x45, 0xCD, 0x23, 0xAB, // |
| 0x45, 0xCD, 0x23, 0xAB, // |
| 0x00, 0x00, 0x00, 0x00, // |
| 0x00, 0x00, 0x00, 0x00}; |
| std::vector<uint8_t> actual_buffer = |
| RunFillBufferTest(buffer_size, target_offset, fill_length, |
| (void*)&pattern, sizeof(pattern)); |
| EXPECT_THAT(actual_buffer, ContainerEq(reference_buffer)); |
| } |
| |
| } // namespace cts |
| } // namespace hal |
| } // namespace iree |
| |
| #endif // IREE_HAL_CTS_COMMAND_BUFFER_TEST_H_ |