| # Copyright (c) Meta Platforms, Inc. and affiliates. |
| # All rights reserved. |
| # |
| # This source code is licensed under the BSD-style license found in the |
| # LICENSE file in the root directory of this source tree. |
| |
| import ctypes |
| import random |
| import unittest |
| from typing import List |
| |
| import torch |
| |
| from executorch.backends.vulkan.serialization.vulkan_graph_schema import VkGraph |
| |
| from executorch.backends.vulkan.serialization.vulkan_graph_serialize import ( |
| serialize_vulkan_graph, |
| VulkanDelegateHeader, |
| ) |
| |
| |
| class TestSerialization(unittest.TestCase): |
| def _generate_random_const_tensors(self, num_tensors: int) -> List[torch.Tensor]: |
| """ |
| Helper function to generate `num_tensor` buffers of random sizes and random contents, |
| we return a tuple of (list_of_buffers, list_of_mem_sizes), |
| """ |
| tensors = [] |
| for _ in range(num_tensors): |
| width = random.randint(4, 100) |
| height = random.randint(4, 100) |
| channels = random.randint(2, 8) |
| |
| tensor = torch.randn(channels, width, height) |
| tensors.append(tensor) |
| |
| return tensors |
| |
| def test_serialize_vulkan_binary(self): |
| vk_graph = VkGraph( |
| version="0", |
| chain=[], |
| values=[], |
| input_ids=[], |
| output_ids=[], |
| constants=[], |
| shaders=[], |
| ) |
| const_tensors = self._generate_random_const_tensors(5) |
| |
| serialized_binary = serialize_vulkan_graph(vk_graph, const_tensors, []) |
| |
| # Check header |
| self.assertEqual(serialized_binary[0:4], b"\x00\x00\x00\x00") |
| self.assertEqual(serialized_binary[VulkanDelegateHeader.MAGIC_IX], b"VH00") |
| flatbuffer_offset = int.from_bytes( |
| serialized_binary[VulkanDelegateHeader.FLATBUFFER_OFFSET_IX], |
| byteorder="little", |
| ) |
| constants_offset = int.from_bytes( |
| serialized_binary[VulkanDelegateHeader.BYTES_OFFSET_IX], |
| byteorder="little", |
| ) |
| constants_size = int.from_bytes( |
| serialized_binary[VulkanDelegateHeader.BYTES_SIZE_IX], |
| byteorder="little", |
| ) |
| |
| # Flatbuffer magic should be in the same spot as the Header's magic |
| self.assertEqual( |
| serialized_binary[flatbuffer_offset:][VulkanDelegateHeader.MAGIC_IX], |
| b"VK00", |
| ) |
| |
| constant_data_payload = serialized_binary[ |
| constants_offset : constants_offset + constants_size |
| ] |
| |
| # We check that constant data indexes stored in the vk_graph correctly index |
| # into the correct buffer in the constant data section |
| self.assertEqual(len(vk_graph.constants), len(const_tensors)) |
| for bytes_range, tensor in zip(vk_graph.constants, const_tensors): |
| offset = bytes_range.offset |
| length = bytes_range.length |
| |
| constant_data_bytes = constant_data_payload[offset : offset + length] |
| |
| array_type = ctypes.c_char * tensor.untyped_storage().nbytes() |
| array = ctypes.cast( |
| tensor.untyped_storage().data_ptr(), |
| ctypes.POINTER(array_type), |
| ).contents |
| |
| tensor_bytes = bytes(array) |
| self.assertEqual(constant_data_bytes, tensor_bytes) |