// Copyright (C) 2018 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "VulkanStream.h" #include "IOStream.h" #include "common/goldfish_vk_deepcopy.h" #include "common/goldfish_vk_extension_structs.h" #include "common/goldfish_vk_marshaling.h" #include "common/goldfish_vk_reserved_marshaling.h" #include "common/goldfish_vk_testing.h" #include "android/base/ArraySize.h" #include "android/base/BumpPool.h" #include #include #include using android::base::arraySize; namespace goldfish_vk { class TestStream : public IOStream { public: static constexpr size_t kBufSize = 1024; TestStream() : IOStream(kBufSize) { } protected: void* getDmaForReading(uint64_t guest_paddr) override { return nullptr; } void unlockDma(uint64_t guest_paddr) override { } // VulkanStream should never use these functions. void* allocBuffer(size_t minSize) override { fprintf(stderr, "%s: FATAL: not intended for use!\n", __func__); abort(); } int commitBuffer(size_t size) override { fprintf(stderr, "%s: FATAL: not intended for use!\n", __func__); abort(); } const unsigned char *readRaw(void *buf, size_t *inout_len) override { fprintf(stderr, "%s: FATAL: not intended for use!\n", __func__); abort(); } void onSave(android::base::Stream*) override { fprintf(stderr, "%s: FATAL: not intended for use!\n", __func__); abort(); } virtual unsigned char* onLoad(android::base::Stream*) override { fprintf(stderr, "%s: FATAL: not intended for use!\n", __func__); abort(); } int writeFully(const void* buffer, size_t size) override { if (mBuffer.size() < mWriteCursor + size) { mBuffer.resize(mWriteCursor + size); } memcpy(mBuffer.data() + mWriteCursor, buffer, size); mWriteCursor += size; if (mReadCursor == mWriteCursor) { clear(); } return 0; } const unsigned char* readFully(void* buf, size_t len) override { EXPECT_LE(mReadCursor + len, mBuffer.size()); memcpy(buf, mBuffer.data() + mReadCursor, len); mReadCursor += len; if (mReadCursor == mWriteCursor) { clear(); } return (unsigned char*)buf; } private: void clear() { mBuffer.clear(); mReadCursor = 0; mWriteCursor = 0; } size_t mReadCursor = 0; size_t mWriteCursor = 0; std::vector mBuffer; }; // Just see whether the test class is OK TEST(VulkanStream, Basic) { TestStream testStream; VulkanStream stream(&testStream); const uint32_t testInt = 6; stream.putBe32(testInt); EXPECT_EQ(testInt, stream.getBe32()); const std::string testString = "Hello World"; stream.putString(testString); EXPECT_EQ(testString, stream.getString()); } // Try a "basic" Vulkan struct (VkInstanceCreateInfo) TEST(VulkanStream, testMarshalVulkanStruct) { TestStream testStream; VulkanStream stream(&testStream); VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO, 0, // pNext "VulkanStreamTest", // application name 6, // application version "VulkanStreamTestEngine", //engine name 4, // engine version, VK_API_VERSION_1_0, }; const char* const layerNames[] = { "layer0", "layer1: test layer", }; const char* const extensionNames[] = { "VK_KHR_8bit_storage", "VK_KHR_android_surface", "VK_MVK_macos_surface", }; VkInstanceCreateInfo forMarshaling = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 0, // pNext 0, // flags, &appInfo, // pApplicationInfo, arraySize(layerNames), layerNames, arraySize(extensionNames), extensionNames }; marshal_VkInstanceCreateInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forMarshaling); VkInstanceCreateInfo forUnmarshaling; memset(&forUnmarshaling, 0x0, sizeof(VkInstanceCreateInfo)); // Before unmarshaling, these structs should be different. // Test that the generated comparator can detect inequality. int inequalities = 0; checkEqual_VkInstanceCreateInfo( &forMarshaling, &forUnmarshaling, [&inequalities](const char* errMsg) { (void)errMsg; ++inequalities; }); EXPECT_GT(inequalities, 0); unmarshal_VkInstanceCreateInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forUnmarshaling); // Check that the strings are equal as well. EXPECT_STREQ( forMarshaling.pApplicationInfo->pApplicationName, forUnmarshaling.pApplicationInfo->pApplicationName); EXPECT_STREQ( forMarshaling.pApplicationInfo->pEngineName, forUnmarshaling.pApplicationInfo->pEngineName); for (size_t i = 0; i < arraySize(layerNames); ++i) { EXPECT_STREQ( forMarshaling.ppEnabledLayerNames[i], forUnmarshaling.ppEnabledLayerNames[i]); } for (size_t i = 0; i < arraySize(extensionNames); ++i) { EXPECT_STREQ( forMarshaling.ppEnabledExtensionNames[i], forUnmarshaling.ppEnabledExtensionNames[i]); } EXPECT_EQ(forMarshaling.sType, forUnmarshaling.sType); EXPECT_EQ(forMarshaling.pNext, forUnmarshaling.pNext); EXPECT_EQ(forMarshaling.flags, forUnmarshaling.flags); EXPECT_EQ(forMarshaling.pApplicationInfo->sType, forUnmarshaling.pApplicationInfo->sType); EXPECT_EQ(forMarshaling.pApplicationInfo->apiVersion, forUnmarshaling.pApplicationInfo->apiVersion); checkEqual_VkInstanceCreateInfo( &forMarshaling, &forUnmarshaling, [](const char* errMsg) { EXPECT_TRUE(false) << errMsg; }); } // Try a Vulkan struct that has non-ptr structs in it TEST(VulkanStream, testMarshalVulkanStructWithNonPtrStruct) { TestStream testStream; VulkanStream stream(&testStream); VkPhysicalDeviceProperties forMarshaling = { VK_API_VERSION_1_0, 0, 0x8086, 0x7800, VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, "Intel740", "123456789abcdef", { 0x00, // maxImageDimension1D; 0x01, // maxImageDimension2D; 0x02, // maxImageDimension3D; 0x03, // maxImageDimensionCube; 0x04, // maxImageArrayLayers; 0x05, // maxTexelBufferElements; 0x06, // maxUniformBufferRange; 0x07, // maxStorageBufferRange; 0x08, // maxPushConstantsSize; 0x09, // maxMemoryAllocationCount; 0x0a, // maxSamplerAllocationCount; 0x0b, // bufferImageGranularity; 0x0c, // sparseAddressSpaceSize; 0x0d, // maxBoundDescriptorSets; 0x0e, // maxPerStageDescriptorSamplers; 0x0f, // maxPerStageDescriptorUniformBuffers; 0x10, // maxPerStageDescriptorStorageBuffers; 0x11, // maxPerStageDescriptorSampledImages; 0x12, // maxPerStageDescriptorStorageImages; 0x13, // maxPerStageDescriptorInputAttachments; 0x14, // maxPerStageResources; 0x15, // maxDescriptorSetSamplers; 0x16, // maxDescriptorSetUniformBuffers; 0x17, // maxDescriptorSetUniformBuffersDynamic; 0x18, // maxDescriptorSetStorageBuffers; 0x19, // maxDescriptorSetStorageBuffersDynamic; 0x1a, // maxDescriptorSetSampledImages; 0x1b, // maxDescriptorSetStorageImages; 0x1c, // maxDescriptorSetInputAttachments; 0x1d, // maxVertexInputAttributes; 0x1e, // maxVertexInputBindings; 0x1f, // maxVertexInputAttributeOffset; 0x20, // maxVertexInputBindingStride; 0x21, // maxVertexOutputComponents; 0x22, // maxTessellationGenerationLevel; 0x23, // maxTessellationPatchSize; 0x24, // maxTessellationControlPerVertexInputComponents; 0x25, // maxTessellationControlPerVertexOutputComponents; 0x26, // maxTessellationControlPerPatchOutputComponents; 0x27, // maxTessellationControlTotalOutputComponents; 0x28, // maxTessellationEvaluationInputComponents; 0x29, // maxTessellationEvaluationOutputComponents; 0x2a, // maxGeometryShaderInvocations; 0x2b, // maxGeometryInputComponents; 0x2c, // maxGeometryOutputComponents; 0x2d, // maxGeometryOutputVertices; 0x2e, // maxGeometryTotalOutputComponents; 0x2f, // maxFragmentInputComponents; 0x30, // maxFragmentOutputAttachments; 0x31, // maxFragmentDualSrcAttachments; 0x32, // maxFragmentCombinedOutputResources; 0x33, // maxComputeSharedMemorySize; { 0x1, 0x2, 0x3 }, // maxComputeWorkGroupCount[3]; 0x35, // maxComputeWorkGroupInvocations; { 0x4, 0x5, 0x6 }, // maxComputeWorkGroupSize[3]; 0x37, // subPixelPrecisionBits; 0x38, // subTexelPrecisionBits; 0x39, // mipmapPrecisionBits; 0x3a, // maxDrawIndexedIndexValue; 0x3b, // maxDrawIndirectCount; 1.0f, // maxSamplerLodBias; 1.0f, // maxSamplerAnisotropy; 0x3e, // maxViewports; { 0x7, 0x8 }, // maxViewportDimensions[2]; { 0.4f, 0.5f }, // viewportBoundsRange[2]; 0x41, // viewportSubPixelBits; 0x42, // minMemoryMapAlignment; 0x43, // minTexelBufferOffsetAlignment; 0x44, // minUniformBufferOffsetAlignment; 0x45, // minStorageBufferOffsetAlignment; 0x46, // minTexelOffset; 0x47, // maxTexelOffset; 0x48, // minTexelGatherOffset; 0x49, // maxTexelGatherOffset; 10.0f, // minInterpolationOffset; 11.0f, // maxInterpolationOffset; 0x4c, // subPixelInterpolationOffsetBits; 0x4d, // maxFramebufferWidth; 0x4e, // maxFramebufferHeight; 0x4f, // maxFramebufferLayers; 0x50, // framebufferColorSampleCounts; 0x51, // framebufferDepthSampleCounts; 0x52, // framebufferStencilSampleCounts; 0x53, // framebufferNoAttachmentsSampleCounts; 0x54, // maxColorAttachments; 0x55, // sampledImageColorSampleCounts; 0x56, // sampledImageIntegerSampleCounts; 0x57, // sampledImageDepthSampleCounts; 0x58, // sampledImageStencilSampleCounts; 0x59, // storageImageSampleCounts; 0x5a, // maxSampleMaskWords; 0x5b, // timestampComputeAndGraphics; 100.0f, // timestampPeriod; 0x5d, // maxClipDistances; 0x5e, // maxCullDistances; 0x5f, // maxCombinedClipAndCullDistances; 0x60, // discreteQueuePriorities; { 0.0f, 1.0f }, // pointSizeRange[2]; { 1.0f, 2.0f }, // lineWidthRange[2]; 3.0f, // pointSizeGranularity; 4.0f, // lineWidthGranularity; 0x65, // strictLines; 0x66, // standardSampleLocations; 0x67, // optimalBufferCopyOffsetAlignment; 0x68, // optimalBufferCopyRowPitchAlignment; 0x69, // nonCoherentAtomSize; }, { 0xff, // residencyStandard2DBlockShape; 0x00, // residencyStandard2DMultisampleBlockShape; 0x11, // residencyStandard3DBlockShape; 0x22, // residencyAlignedMipSize; 0x33, // residencyNonResidentStrict; }, }; marshal_VkPhysicalDeviceProperties(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forMarshaling); VkPhysicalDeviceProperties forUnmarshaling; memset(&forUnmarshaling, 0x0, sizeof(VkPhysicalDeviceLimits)); // Test the autogenerated testing code int inequalities = 0; checkEqual_VkPhysicalDeviceProperties( &forMarshaling, &forUnmarshaling, [&inequalities](const char* errMsg) { (void)errMsg; ++inequalities; }); EXPECT_GT(inequalities, 0); unmarshal_VkPhysicalDeviceProperties(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forUnmarshaling); // Test the autogenerated testing code EXPECT_EQ(VK_API_VERSION_1_0, forUnmarshaling.apiVersion); EXPECT_EQ(VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, forUnmarshaling.deviceType); EXPECT_EQ(2.0f, forUnmarshaling.limits.lineWidthRange[1]); EXPECT_EQ(11.0f, forUnmarshaling.limits.maxInterpolationOffset); checkEqual_VkPhysicalDeviceProperties( &forMarshaling, &forUnmarshaling, [](const char* errMsg) { EXPECT_TRUE(false) << errMsg; }); } // Try a Vulkan struct that has ptr fields with count (dynamic arrays) TEST(VulkanStream, testMarshalVulkanStructWithPtrFields) { TestStream testStream; VulkanStream stream(&testStream); const uint32_t bindCount = 14; std::vector sparseBinds; for (uint32_t i = 0; i < bindCount; i++) { VkSparseImageMemoryBind sparseBind = { // VkImageSubresource subresource { VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT, i, i * 2, }, // VkOffset3D offset { 1, 2 + (int32_t)i, 3}, // VkExtent3D extent { 10, 20 * i, 30}, // VkDeviceMemory memory (VkDeviceMemory)(uintptr_t)(0xff - i), // VkDeviceSize memoryOffset 0x12345678 + i, // VkSparseMemoryBindFlags flags VK_SPARSE_MEMORY_BIND_METADATA_BIT, }; sparseBinds.push_back(sparseBind); } VkSparseImageMemoryBindInfo forMarshaling = { (VkImage)(uintptr_t)54, bindCount, sparseBinds.data(), }; marshal_VkSparseImageMemoryBindInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forMarshaling); VkSparseImageMemoryBindInfo forUnmarshaling = { 0, 0, nullptr, }; unmarshal_VkSparseImageMemoryBindInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forUnmarshaling); EXPECT_EQ(bindCount, forUnmarshaling.bindCount); EXPECT_EQ(forMarshaling.image, forUnmarshaling.image); // Test some values in there so we know the autogenerated // compare code works. for (uint32_t i = 0; i < bindCount; i++) { EXPECT_EQ(forMarshaling.pBinds[i].memoryOffset, forUnmarshaling.pBinds[i].memoryOffset); EXPECT_EQ(forMarshaling.pBinds[i].memoryOffset, forUnmarshaling.pBinds[i].memoryOffset); EXPECT_EQ(forMarshaling.pBinds[i].subresource.arrayLayer, forUnmarshaling.pBinds[i].subresource.arrayLayer); } checkEqual_VkSparseImageMemoryBindInfo( &forMarshaling, &forUnmarshaling, [](const char* errMsg) { EXPECT_TRUE(false) << errMsg; }); } // Try a Vulkan struct that has ptr fields that are not structs TEST(VulkanStream, testMarshalVulkanStructWithSimplePtrFields) { TestStream testStream; VulkanStream stream(&testStream); const uint32_t queueCount = 4; std::vector queuePriorities; for (uint32_t i = 0; i < queueCount; i++) { queuePriorities.push_back(i * 4.0f); } VkDeviceQueueCreateInfo forMarshaling = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, 0, 0, 1, queueCount, queuePriorities.data(), }; VkDeviceQueueCreateInfo forUnmarshaling = { VK_STRUCTURE_TYPE_APPLICATION_INFO, 0, 0, 0, 0, nullptr, }; marshal_VkDeviceQueueCreateInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forMarshaling); unmarshal_VkDeviceQueueCreateInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forUnmarshaling); // As always, test the autogenerated tester. for (uint32_t i = 0; i < queueCount; i++) { EXPECT_EQ(forMarshaling.pQueuePriorities[i], forUnmarshaling.pQueuePriorities[i]); } checkEqual_VkDeviceQueueCreateInfo( &forMarshaling, &forUnmarshaling, [](const char* errMsg) { EXPECT_TRUE(false) << errMsg; }); } // Vulkan struct with a void* field that refers to actual data // that needs to get transmitted over TEST(VulkanStream, testMarshalVulkanStructWithVoidPtrToData) { TestStream testStream; VulkanStream stream(&testStream); // Not going to validate the map entries--- // that's the validation layer's job, // and this is just to make sure values match. const uint32_t numEntries = 5; const size_t dataSize = 54; std::vector entries(numEntries); for (uint32_t i = 0; i < numEntries; i++) { entries[i].constantID = 8 * i + 0; entries[i].offset = 8 * i + 1; entries[i].size = 8 * i + 2; } std::vector data(dataSize); for (size_t i = 0; i < dataSize; i++) { data[i] = (uint8_t)i; } VkSpecializationInfo forMarshaling = { numEntries, entries.data(), dataSize, data.data(), }; VkSpecializationInfo forUnmarshaling; memset(&forUnmarshaling, 0x0, sizeof(VkSpecializationInfo)); int inequalities = 0; checkEqual_VkSpecializationInfo( &forMarshaling, &forUnmarshaling, [&inequalities](const char* errMsg) { ++inequalities; }); EXPECT_GT(inequalities, 0); marshal_VkSpecializationInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forMarshaling); unmarshal_VkSpecializationInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forUnmarshaling); checkEqual_VkSpecializationInfo( &forMarshaling, &forUnmarshaling, [](const char* errMsg) { EXPECT_TRUE(false) << errMsg; }); } // Tests that marshal + unmarshal is equivalent to deepcopy. TEST(VulkanStream, testDeepcopyEquivalence) { BumpPool pool; TestStream testStream; VulkanStream stream(&testStream); VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO, 0, // pNext "VulkanStreamTest", // application name 6, // application version "VulkanStreamTestEngine", //engine name 4, // engine version, VK_API_VERSION_1_0, }; const char* const layerNames[] = { "layer0", "layer1: test layer", }; const char* const extensionNames[] = { "VK_KHR_8bit_storage", "VK_KHR_android_surface", "VK_MVK_macos_surface", }; VkInstanceCreateInfo forMarshaling = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 0, // pNext 0, // flags, &appInfo, // pApplicationInfo, arraySize(layerNames), layerNames, arraySize(extensionNames), extensionNames }; marshal_VkInstanceCreateInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forMarshaling); VkInstanceCreateInfo forUnmarshaling; VkInstanceCreateInfo forDeepcopy; unmarshal_VkInstanceCreateInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forUnmarshaling); deepcopy_VkInstanceCreateInfo(&pool, VK_STRUCTURE_TYPE_MAX_ENUM, &forMarshaling, &forDeepcopy); checkEqual_VkInstanceCreateInfo( &forMarshaling, &forUnmarshaling, [](const char* errMsg) { EXPECT_TRUE(false) << errMsg; }); checkEqual_VkInstanceCreateInfo( &forMarshaling, &forDeepcopy, [](const char* errMsg) { EXPECT_TRUE(false) << errMsg; }); } // Tests that a struct with an extension struct attached // is properly marshaled/unmarshaled. TEST(VulkanStream, testStructExtension) { BumpPool pool; TestStream testStream; VulkanStream stream(&testStream); VkImage image = (VkImage)1; VkBuffer buffer = (VkBuffer)2; VkMemoryDedicatedAllocateInfo dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, 0, image, buffer, }; VkMemoryAllocateInfo forMarshaling = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, &dedicatedAllocInfo, 4096, 5, }; marshal_VkMemoryAllocateInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forMarshaling); VkMemoryAllocateInfo forUnmarshaling; unmarshal_VkMemoryAllocateInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forUnmarshaling); VkMemoryDedicatedAllocateInfo* copiedDedicated = (VkMemoryDedicatedAllocateInfo*)forUnmarshaling.pNext; EXPECT_EQ(VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, copiedDedicated->sType); EXPECT_EQ(image, copiedDedicated->image); EXPECT_EQ(buffer, copiedDedicated->buffer); checkEqual_VkMemoryAllocateInfo( &forMarshaling, &forUnmarshaling, [](const char* errMsg) { EXPECT_TRUE(false) << errMsg; }); VkMemoryAllocateInfo forDeepcopy; deepcopy_VkMemoryAllocateInfo(&pool, VK_STRUCTURE_TYPE_MAX_ENUM, &forMarshaling, &forDeepcopy); copiedDedicated = (VkMemoryDedicatedAllocateInfo*)forDeepcopy.pNext; EXPECT_EQ(VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, copiedDedicated->sType); EXPECT_EQ(image, copiedDedicated->image); EXPECT_EQ(buffer, copiedDedicated->buffer); checkEqual_VkMemoryAllocateInfo( &forMarshaling, &forDeepcopy, [](const char* errMsg) { EXPECT_TRUE(false) << errMsg; }); } TEST(VulkanStream, testConflictStructExtensions_marshaling) { BumpPool pool; TestStream testStream; VulkanStream stream(&testStream); VkStructureType conflictSType0 = static_cast(1000218000u); { VkImportColorBufferGOOGLE importColorBuffer = { .sType = conflictSType0, .pNext = nullptr, .colorBuffer = 0xabcd1234, }; VkMemoryAllocateInfo forMarshaling = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = &importColorBuffer, .allocationSize = 0xcdab, .memoryTypeIndex = 0xabcd, }; marshal_VkMemoryAllocateInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forMarshaling); VkMemoryAllocateInfo forUnmarshaling; unmarshal_VkMemoryAllocateInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forUnmarshaling); ASSERT_TRUE(forUnmarshaling.pNext); const VkImportColorBufferGOOGLE* ext = reinterpret_cast(forUnmarshaling.pNext); EXPECT_EQ(ext->sType, VK_STRUCTURE_TYPE_IMPORT_COLOR_BUFFER_GOOGLE); EXPECT_EQ(ext->pNext, nullptr); EXPECT_EQ(ext->colorBuffer, importColorBuffer.colorBuffer); } { VkPhysicalDeviceFragmentDensityMapFeaturesEXT densityMapFeatures = { .sType = conflictSType0, .pNext = nullptr, .fragmentDensityMap = true, .fragmentDensityMapDynamic = false, .fragmentDensityMapNonSubsampledImages = true, }; VkDeviceCreateInfo forMarshaling = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = &densityMapFeatures, }; marshal_VkDeviceCreateInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forMarshaling); VkDeviceCreateInfo forUnmarshaling; unmarshal_VkDeviceCreateInfo(&stream, VK_STRUCTURE_TYPE_MAX_ENUM, &forUnmarshaling); ASSERT_TRUE(forUnmarshaling.pNext); const VkPhysicalDeviceFragmentDensityMapFeaturesEXT* ext = reinterpret_cast(forUnmarshaling.pNext); EXPECT_EQ(ext->sType, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT); EXPECT_EQ(ext->pNext, nullptr); EXPECT_EQ(ext->fragmentDensityMap, densityMapFeatures.fragmentDensityMap); EXPECT_EQ(ext->fragmentDensityMapDynamic, densityMapFeatures.fragmentDensityMapDynamic); EXPECT_EQ(ext->fragmentDensityMapNonSubsampledImages, densityMapFeatures.fragmentDensityMapNonSubsampledImages); } } TEST(VulkanStream, testConflictStructExtensions_size) { BumpPool pool; TestStream testStream; VulkanStream stream(&testStream); VkStructureType conflictSType0 = static_cast(1000218000u); { VkImportColorBufferGOOGLE importColorBuffer = { .sType = conflictSType0, .pNext = nullptr, .colorBuffer = 0xabcd1234, }; VkMemoryAllocateInfo allocateInfo = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = &importColorBuffer, .allocationSize = 0xcdab, .memoryTypeIndex = 0xabcd, }; size_t size = goldfish_vk_extension_struct_size(allocateInfo.sType, &importColorBuffer); EXPECT_EQ(size, sizeof(VkImportColorBufferGOOGLE)); } { VkPhysicalDeviceFragmentDensityMapFeaturesEXT densityMapFeatures = { .sType = conflictSType0, .pNext = nullptr, .fragmentDensityMap = true, .fragmentDensityMapDynamic = false, .fragmentDensityMapNonSubsampledImages = true, }; VkDeviceCreateInfo deviceCreateInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = &densityMapFeatures, }; size_t size = goldfish_vk_extension_struct_size(deviceCreateInfo.sType, &densityMapFeatures); EXPECT_EQ(size, sizeof(VkPhysicalDeviceFragmentDensityMapFeaturesEXT)); } } } // namespace goldfish_vk