#!/usr/bin/python3 -i # # Copyright (c) 2015-2023 The Khronos Group Inc. # Copyright (c) 2015-2023 Valve Corporation # Copyright (c) 2015-2023 LunarG, Inc. # Copyright (c) 2015-2023 Google Inc. # Copyright (c) 2023-2023 RasterGrid Kft. # # 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. # # Author: Tobin Ehlis # # This script generates a Mock ICD that intercepts almost all Vulkan # functions. That layer is not intended to be useful or even compilable # in its initial state. Rather it's intended to be a starting point that # can be copied and customized to assist in creation of a new layer. import os,re,sys from generator import * from common_codegen import * CUSTOM_C_INTERCEPTS = { 'vkCreateInstance': ''' // TODO: If loader ver <=4 ICD must fail with VK_ERROR_INCOMPATIBLE_DRIVER for all vkCreateInstance calls with // apiVersion set to > Vulkan 1.0 because the loader is still at interface version <= 4. Otherwise, the // ICD should behave as normal. if (loader_interface_version <= 4) { return VK_ERROR_INCOMPATIBLE_DRIVER; } *pInstance = (VkInstance)CreateDispObjHandle(); for (auto& physical_device : physical_device_map[*pInstance]) physical_device = (VkPhysicalDevice)CreateDispObjHandle(); // TODO: If emulating specific device caps, will need to add intelligence here return VK_SUCCESS; ''', 'vkDestroyInstance': ''' if (instance) { for (const auto physical_device : physical_device_map.at(instance)) DestroyDispObjHandle((void*)physical_device); physical_device_map.erase(instance); DestroyDispObjHandle((void*)instance); } ''', 'vkAllocateCommandBuffers': ''' unique_lock_t lock(global_lock); for (uint32_t i = 0; i < pAllocateInfo->commandBufferCount; ++i) { pCommandBuffers[i] = (VkCommandBuffer)CreateDispObjHandle(); command_pool_buffer_map[pAllocateInfo->commandPool].push_back(pCommandBuffers[i]); } return VK_SUCCESS; ''', 'vkFreeCommandBuffers': ''' unique_lock_t lock(global_lock); for (auto i = 0u; i < commandBufferCount; ++i) { if (!pCommandBuffers[i]) { continue; } for (auto& pair : command_pool_buffer_map) { auto& cbs = pair.second; auto it = std::find(cbs.begin(), cbs.end(), pCommandBuffers[i]); if (it != cbs.end()) { cbs.erase(it); } } DestroyDispObjHandle((void*) pCommandBuffers[i]); } ''', 'vkCreateCommandPool': ''' unique_lock_t lock(global_lock); *pCommandPool = (VkCommandPool)global_unique_handle++; command_pool_map[device].insert(*pCommandPool); return VK_SUCCESS; ''', 'vkDestroyCommandPool': ''' // destroy command buffers for this pool unique_lock_t lock(global_lock); auto it = command_pool_buffer_map.find(commandPool); if (it != command_pool_buffer_map.end()) { for (auto& cb : it->second) { DestroyDispObjHandle((void*) cb); } command_pool_buffer_map.erase(it); } command_pool_map[device].erase(commandPool); ''', 'vkEnumeratePhysicalDevices': ''' VkResult result_code = VK_SUCCESS; if (pPhysicalDevices) { const auto return_count = (std::min)(*pPhysicalDeviceCount, icd_physical_device_count); for (uint32_t i = 0; i < return_count; ++i) pPhysicalDevices[i] = physical_device_map.at(instance)[i]; if (return_count < icd_physical_device_count) result_code = VK_INCOMPLETE; *pPhysicalDeviceCount = return_count; } else { *pPhysicalDeviceCount = icd_physical_device_count; } return result_code; ''', 'vkCreateDevice': ''' *pDevice = (VkDevice)CreateDispObjHandle(); // TODO: If emulating specific device caps, will need to add intelligence here return VK_SUCCESS; ''', 'vkDestroyDevice': ''' unique_lock_t lock(global_lock); // First destroy sub-device objects // Destroy Queues for (auto queue_family_map_pair : queue_map[device]) { for (auto index_queue_pair : queue_map[device][queue_family_map_pair.first]) { DestroyDispObjHandle((void*)index_queue_pair.second); } } for (auto& cp : command_pool_map[device]) { for (auto& cb : command_pool_buffer_map[cp]) { DestroyDispObjHandle((void*) cb); } command_pool_buffer_map.erase(cp); } command_pool_map[device].clear(); queue_map.erase(device); buffer_map.erase(device); image_memory_size_map.erase(device); // Now destroy device DestroyDispObjHandle((void*)device); // TODO: If emulating specific device caps, will need to add intelligence here ''', 'vkGetDeviceQueue': ''' unique_lock_t lock(global_lock); auto queue = queue_map[device][queueFamilyIndex][queueIndex]; if (queue) { *pQueue = queue; } else { *pQueue = queue_map[device][queueFamilyIndex][queueIndex] = (VkQueue)CreateDispObjHandle(); } // TODO: If emulating specific device caps, will need to add intelligence here return; ''', 'vkGetDeviceQueue2': ''' GetDeviceQueue(device, pQueueInfo->queueFamilyIndex, pQueueInfo->queueIndex, pQueue); // TODO: Add further support for GetDeviceQueue2 features ''', 'vkEnumerateInstanceLayerProperties': ''' return VK_SUCCESS; ''', 'vkEnumerateInstanceVersion': ''' *pApiVersion = VK_HEADER_VERSION_COMPLETE; return VK_SUCCESS; ''', 'vkEnumerateDeviceLayerProperties': ''' return VK_SUCCESS; ''', 'vkEnumerateInstanceExtensionProperties': ''' // If requesting number of extensions, return that if (!pLayerName) { if (!pProperties) { *pPropertyCount = (uint32_t)instance_extension_map.size(); } else { uint32_t i = 0; for (const auto &name_ver_pair : instance_extension_map) { if (i == *pPropertyCount) { break; } std::strncpy(pProperties[i].extensionName, name_ver_pair.first.c_str(), sizeof(pProperties[i].extensionName)); pProperties[i].extensionName[sizeof(pProperties[i].extensionName) - 1] = 0; pProperties[i].specVersion = name_ver_pair.second; ++i; } if (i != instance_extension_map.size()) { return VK_INCOMPLETE; } } } // If requesting extension properties, fill in data struct for number of extensions return VK_SUCCESS; ''', 'vkEnumerateDeviceExtensionProperties': ''' // If requesting number of extensions, return that if (!pLayerName) { if (!pProperties) { *pPropertyCount = (uint32_t)device_extension_map.size(); } else { uint32_t i = 0; for (const auto &name_ver_pair : device_extension_map) { if (i == *pPropertyCount) { break; } std::strncpy(pProperties[i].extensionName, name_ver_pair.first.c_str(), sizeof(pProperties[i].extensionName)); pProperties[i].extensionName[sizeof(pProperties[i].extensionName) - 1] = 0; pProperties[i].specVersion = name_ver_pair.second; ++i; } if (i != device_extension_map.size()) { return VK_INCOMPLETE; } } } // If requesting extension properties, fill in data struct for number of extensions return VK_SUCCESS; ''', 'vkGetPhysicalDeviceSurfacePresentModesKHR': ''' // Currently always say that all present modes are supported if (!pPresentModes) { *pPresentModeCount = 6; } else { if (*pPresentModeCount >= 6) pPresentModes[5] = VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR; if (*pPresentModeCount >= 5) pPresentModes[4] = VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR; if (*pPresentModeCount >= 4) pPresentModes[3] = VK_PRESENT_MODE_FIFO_RELAXED_KHR; if (*pPresentModeCount >= 3) pPresentModes[2] = VK_PRESENT_MODE_FIFO_KHR; if (*pPresentModeCount >= 2) pPresentModes[1] = VK_PRESENT_MODE_MAILBOX_KHR; if (*pPresentModeCount >= 1) pPresentModes[0] = VK_PRESENT_MODE_IMMEDIATE_KHR; } return VK_SUCCESS; ''', 'vkGetPhysicalDeviceSurfaceFormatsKHR': ''' // Currently always say that RGBA8 & BGRA8 are supported if (!pSurfaceFormats) { *pSurfaceFormatCount = 2; } else { if (*pSurfaceFormatCount >= 2) { pSurfaceFormats[1].format = VK_FORMAT_R8G8B8A8_UNORM; pSurfaceFormats[1].colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; } if (*pSurfaceFormatCount >= 1) { pSurfaceFormats[0].format = VK_FORMAT_B8G8R8A8_UNORM; pSurfaceFormats[0].colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; } } return VK_SUCCESS; ''', 'vkGetPhysicalDeviceSurfaceFormats2KHR': ''' // Currently always say that RGBA8 & BGRA8 are supported if (!pSurfaceFormats) { *pSurfaceFormatCount = 2; } else { if (*pSurfaceFormatCount >= 2) { pSurfaceFormats[1].pNext = nullptr; pSurfaceFormats[1].surfaceFormat.format = VK_FORMAT_R8G8B8A8_UNORM; pSurfaceFormats[1].surfaceFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; } if (*pSurfaceFormatCount >= 1) { pSurfaceFormats[1].pNext = nullptr; pSurfaceFormats[0].surfaceFormat.format = VK_FORMAT_B8G8R8A8_UNORM; pSurfaceFormats[0].surfaceFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; } } return VK_SUCCESS; ''', 'vkGetPhysicalDeviceSurfaceSupportKHR': ''' // Currently say that all surface/queue combos are supported *pSupported = VK_TRUE; return VK_SUCCESS; ''', 'vkGetPhysicalDeviceSurfaceCapabilitiesKHR': ''' // In general just say max supported is available for requested surface pSurfaceCapabilities->minImageCount = 1; pSurfaceCapabilities->maxImageCount = 0; pSurfaceCapabilities->currentExtent.width = 0xFFFFFFFF; pSurfaceCapabilities->currentExtent.height = 0xFFFFFFFF; pSurfaceCapabilities->minImageExtent.width = 1; pSurfaceCapabilities->minImageExtent.height = 1; pSurfaceCapabilities->maxImageExtent.width = 0xFFFF; pSurfaceCapabilities->maxImageExtent.height = 0xFFFF; pSurfaceCapabilities->maxImageArrayLayers = 128; pSurfaceCapabilities->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR | VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR | VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR | VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR | VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR; pSurfaceCapabilities->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; pSurfaceCapabilities->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR | VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR | VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; pSurfaceCapabilities->supportedUsageFlags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; return VK_SUCCESS; ''', 'vkGetPhysicalDeviceSurfaceCapabilities2KHR': ''' GetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, pSurfaceInfo->surface, &pSurfaceCapabilities->surfaceCapabilities); return VK_SUCCESS; ''', 'vkGetInstanceProcAddr': ''' if (!negotiate_loader_icd_interface_called) { loader_interface_version = 0; } const auto &item = name_to_funcptr_map.find(pName); if (item != name_to_funcptr_map.end()) { return reinterpret_cast(item->second); } // Mock should intercept all functions so if we get here just return null return nullptr; ''', 'vkGetDeviceProcAddr': ''' return GetInstanceProcAddr(nullptr, pName); ''', 'vkGetPhysicalDeviceMemoryProperties': ''' pMemoryProperties->memoryTypeCount = 6; // Host visible Coherent pMemoryProperties->memoryTypes[0].propertyFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; pMemoryProperties->memoryTypes[0].heapIndex = 0; // Host visible Cached pMemoryProperties->memoryTypes[1].propertyFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; pMemoryProperties->memoryTypes[1].heapIndex = 0; // Device local and Host visible pMemoryProperties->memoryTypes[2].propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; pMemoryProperties->memoryTypes[2].heapIndex = 1; // Device local lazily pMemoryProperties->memoryTypes[3].propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT; pMemoryProperties->memoryTypes[3].heapIndex = 1; // Device local protected pMemoryProperties->memoryTypes[4].propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_PROTECTED_BIT; pMemoryProperties->memoryTypes[4].heapIndex = 1; // Device local only pMemoryProperties->memoryTypes[5].propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; pMemoryProperties->memoryTypes[5].heapIndex = 1; pMemoryProperties->memoryHeapCount = 2; pMemoryProperties->memoryHeaps[0].flags = VK_MEMORY_HEAP_MULTI_INSTANCE_BIT; pMemoryProperties->memoryHeaps[0].size = 8000000000; pMemoryProperties->memoryHeaps[1].flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT; pMemoryProperties->memoryHeaps[1].size = 8000000000; ''', 'vkGetPhysicalDeviceMemoryProperties2KHR': ''' GetPhysicalDeviceMemoryProperties(physicalDevice, &pMemoryProperties->memoryProperties); ''', 'vkGetPhysicalDeviceQueueFamilyProperties': ''' if (!pQueueFamilyProperties) { *pQueueFamilyPropertyCount = 1; } else { if (*pQueueFamilyPropertyCount) { pQueueFamilyProperties[0].queueFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT | VK_QUEUE_SPARSE_BINDING_BIT | VK_QUEUE_PROTECTED_BIT; pQueueFamilyProperties[0].queueCount = 1; pQueueFamilyProperties[0].timestampValidBits = 16; pQueueFamilyProperties[0].minImageTransferGranularity = {1,1,1}; } } ''', 'vkGetPhysicalDeviceQueueFamilyProperties2KHR': ''' if (pQueueFamilyPropertyCount && pQueueFamilyProperties) { GetPhysicalDeviceQueueFamilyProperties(physicalDevice, pQueueFamilyPropertyCount, &pQueueFamilyProperties->queueFamilyProperties); } else { GetPhysicalDeviceQueueFamilyProperties(physicalDevice, pQueueFamilyPropertyCount, nullptr); } ''', 'vkGetPhysicalDeviceFeatures': ''' uint32_t num_bools = sizeof(VkPhysicalDeviceFeatures) / sizeof(VkBool32); VkBool32 *bool_array = &pFeatures->robustBufferAccess; SetBoolArrayTrue(bool_array, num_bools); ''', 'vkGetPhysicalDeviceFeatures2KHR': ''' GetPhysicalDeviceFeatures(physicalDevice, &pFeatures->features); uint32_t num_bools = 0; // Count number of VkBool32s in extension structs VkBool32* feat_bools = nullptr; const auto *desc_idx_features = lvl_find_in_chain(pFeatures->pNext); if (desc_idx_features) { const auto bool_size = sizeof(VkPhysicalDeviceDescriptorIndexingFeaturesEXT) - offsetof(VkPhysicalDeviceDescriptorIndexingFeaturesEXT, shaderInputAttachmentArrayDynamicIndexing); num_bools = bool_size/sizeof(VkBool32); feat_bools = (VkBool32*)&desc_idx_features->shaderInputAttachmentArrayDynamicIndexing; SetBoolArrayTrue(feat_bools, num_bools); } const auto *blendop_features = lvl_find_in_chain(pFeatures->pNext); if (blendop_features) { const auto bool_size = sizeof(VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT) - offsetof(VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT, advancedBlendCoherentOperations); num_bools = bool_size/sizeof(VkBool32); feat_bools = (VkBool32*)&blendop_features->advancedBlendCoherentOperations; SetBoolArrayTrue(feat_bools, num_bools); } ''', 'vkGetPhysicalDeviceFormatProperties': ''' if (VK_FORMAT_UNDEFINED == format) { *pFormatProperties = { 0x0, 0x0, 0x0 }; } else { // Default to a color format, skip DS bit *pFormatProperties = { 0x00FFFDFF, 0x00FFFDFF, 0x00FFFDFF }; switch (format) { case VK_FORMAT_D16_UNORM: case VK_FORMAT_X8_D24_UNORM_PACK32: case VK_FORMAT_D32_SFLOAT: case VK_FORMAT_S8_UINT: case VK_FORMAT_D16_UNORM_S8_UINT: case VK_FORMAT_D24_UNORM_S8_UINT: case VK_FORMAT_D32_SFLOAT_S8_UINT: // Don't set color bits for DS formats *pFormatProperties = { 0x00FFFE7F, 0x00FFFE7F, 0x00FFFE7F }; break; default: break; } } ''', 'vkGetPhysicalDeviceFormatProperties2KHR': ''' GetPhysicalDeviceFormatProperties(physicalDevice, format, &pFormatProperties->formatProperties); VkFormatProperties3KHR *props_3 = lvl_find_mod_in_chain(pFormatProperties->pNext); if (props_3) { props_3->linearTilingFeatures = pFormatProperties->formatProperties.linearTilingFeatures; props_3->optimalTilingFeatures = pFormatProperties->formatProperties.optimalTilingFeatures; props_3->bufferFeatures = pFormatProperties->formatProperties.bufferFeatures; } ''', 'vkGetPhysicalDeviceImageFormatProperties': ''' // A hardcoded unsupported format if (format == VK_FORMAT_E5B9G9R9_UFLOAT_PACK32) { return VK_ERROR_FORMAT_NOT_SUPPORTED; } // TODO: Just hard-coding some values for now // TODO: If tiling is linear, limit the mips, levels, & sample count if (VK_IMAGE_TILING_LINEAR == tiling) { *pImageFormatProperties = { { 4096, 4096, 256 }, 1, 1, VK_SAMPLE_COUNT_1_BIT, 4294967296 }; } else { // We hard-code support for all sample counts except 64 bits. *pImageFormatProperties = { { 4096, 4096, 256 }, 12, 256, 0x7F & ~VK_SAMPLE_COUNT_64_BIT, 4294967296 }; } return VK_SUCCESS; ''', 'vkGetPhysicalDeviceImageFormatProperties2KHR': ''' GetPhysicalDeviceImageFormatProperties(physicalDevice, pImageFormatInfo->format, pImageFormatInfo->type, pImageFormatInfo->tiling, pImageFormatInfo->usage, pImageFormatInfo->flags, &pImageFormatProperties->imageFormatProperties); return VK_SUCCESS; ''', 'vkGetPhysicalDeviceSparseImageFormatProperties': ''' if (!pProperties) { *pPropertyCount = 1; } else { // arbitrary pProperties->imageGranularity = {4, 4, 4}; pProperties->flags = VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT; switch (format) { case VK_FORMAT_D16_UNORM: case VK_FORMAT_D32_SFLOAT: pProperties->aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; break; case VK_FORMAT_S8_UINT: pProperties->aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; break; case VK_FORMAT_X8_D24_UNORM_PACK32: case VK_FORMAT_D16_UNORM_S8_UINT: case VK_FORMAT_D24_UNORM_S8_UINT: case VK_FORMAT_D32_SFLOAT_S8_UINT: pProperties->aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; break; default: pProperties->aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; break; } } ''', 'vkGetPhysicalDeviceSparseImageFormatProperties2KHR': ''' if (pPropertyCount && pProperties) { GetPhysicalDeviceSparseImageFormatProperties(physicalDevice, pFormatInfo->format, pFormatInfo->type, pFormatInfo->samples, pFormatInfo->usage, pFormatInfo->tiling, pPropertyCount, &pProperties->properties); } else { GetPhysicalDeviceSparseImageFormatProperties(physicalDevice, pFormatInfo->format, pFormatInfo->type, pFormatInfo->samples, pFormatInfo->usage, pFormatInfo->tiling, pPropertyCount, nullptr); } ''', 'vkGetPhysicalDeviceProperties': ''' pProperties->apiVersion = VK_HEADER_VERSION_COMPLETE; pProperties->driverVersion = 1; pProperties->vendorID = 0xba5eba11; pProperties->deviceID = 0xf005ba11; pProperties->deviceType = VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU; //std::string devName = "Vulkan Mock Device"; strcpy(pProperties->deviceName, "Vulkan Mock Device"); pProperties->pipelineCacheUUID[0] = 18; pProperties->limits = SetLimits(&pProperties->limits); pProperties->sparseProperties = { VK_TRUE, VK_TRUE, VK_TRUE, VK_TRUE, VK_TRUE }; ''', 'vkGetPhysicalDeviceProperties2KHR': ''' // The only value that need to be set are those the Profile layer can't set // see https://github.com/KhronosGroup/Vulkan-Profiles/issues/352 // All values set are arbitrary GetPhysicalDeviceProperties(physicalDevice, &pProperties->properties); auto *props_11 = lvl_find_mod_in_chain(pProperties->pNext); if (props_11) { props_11->protectedNoFault = VK_FALSE; } auto *props_12 = lvl_find_mod_in_chain(pProperties->pNext); if (props_12) { props_12->denormBehaviorIndependence = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL; props_12->roundingModeIndependence = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL; } auto *props_13 = lvl_find_mod_in_chain(pProperties->pNext); if (props_13) { props_13->storageTexelBufferOffsetSingleTexelAlignment = VK_TRUE; props_13->uniformTexelBufferOffsetSingleTexelAlignment = VK_TRUE; props_13->storageTexelBufferOffsetAlignmentBytes = 16; props_13->uniformTexelBufferOffsetAlignmentBytes = 16; } auto *protected_memory_props = lvl_find_mod_in_chain(pProperties->pNext); if (protected_memory_props) { protected_memory_props->protectedNoFault = VK_FALSE; } auto *float_controls_props = lvl_find_mod_in_chain(pProperties->pNext); if (float_controls_props) { float_controls_props->denormBehaviorIndependence = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL; float_controls_props->roundingModeIndependence = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL; } auto *conservative_raster_props = lvl_find_mod_in_chain(pProperties->pNext); if (conservative_raster_props) { conservative_raster_props->primitiveOverestimationSize = 0.00195313f; conservative_raster_props->conservativePointAndLineRasterization = VK_TRUE; conservative_raster_props->degenerateTrianglesRasterized = VK_TRUE; conservative_raster_props->degenerateLinesRasterized = VK_TRUE; } auto *rt_pipeline_props = lvl_find_mod_in_chain(pProperties->pNext); if (rt_pipeline_props) { rt_pipeline_props->shaderGroupHandleSize = 32; rt_pipeline_props->shaderGroupBaseAlignment = 64; rt_pipeline_props->shaderGroupHandleCaptureReplaySize = 32; } auto *rt_pipeline_nv_props = lvl_find_mod_in_chain(pProperties->pNext); if (rt_pipeline_nv_props) { rt_pipeline_nv_props->shaderGroupHandleSize = 32; rt_pipeline_nv_props->shaderGroupBaseAlignment = 64; } auto *texel_buffer_props = lvl_find_mod_in_chain(pProperties->pNext); if (texel_buffer_props) { texel_buffer_props->storageTexelBufferOffsetSingleTexelAlignment = VK_TRUE; texel_buffer_props->uniformTexelBufferOffsetSingleTexelAlignment = VK_TRUE; texel_buffer_props->storageTexelBufferOffsetAlignmentBytes = 16; texel_buffer_props->uniformTexelBufferOffsetAlignmentBytes = 16; } auto *descriptor_buffer_props = lvl_find_mod_in_chain(pProperties->pNext); if (descriptor_buffer_props) { descriptor_buffer_props->combinedImageSamplerDescriptorSingleArray = VK_TRUE; descriptor_buffer_props->bufferlessPushDescriptors = VK_TRUE; descriptor_buffer_props->allowSamplerImageViewPostSubmitCreation = VK_TRUE; descriptor_buffer_props->descriptorBufferOffsetAlignment = 4; } auto *mesh_shader_props = lvl_find_mod_in_chain(pProperties->pNext); if (mesh_shader_props) { mesh_shader_props->meshOutputPerVertexGranularity = 32; mesh_shader_props->meshOutputPerPrimitiveGranularity = 32; mesh_shader_props->prefersLocalInvocationVertexOutput = VK_TRUE; mesh_shader_props->prefersLocalInvocationPrimitiveOutput = VK_TRUE; mesh_shader_props->prefersCompactVertexOutput = VK_TRUE; mesh_shader_props->prefersCompactPrimitiveOutput = VK_TRUE; } auto *fragment_density_map2_props = lvl_find_mod_in_chain(pProperties->pNext); if (fragment_density_map2_props) { fragment_density_map2_props->subsampledLoads = VK_FALSE; fragment_density_map2_props->subsampledCoarseReconstructionEarlyAccess = VK_FALSE; fragment_density_map2_props->maxSubsampledArrayLayers = 2; fragment_density_map2_props->maxDescriptorSetSubsampledSamplers = 1; } ''', 'vkGetPhysicalDeviceExternalSemaphoreProperties':''' // Hard code support for all handle types and features pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0x1F; pExternalSemaphoreProperties->compatibleHandleTypes = 0x1F; pExternalSemaphoreProperties->externalSemaphoreFeatures = 0x3; ''', 'vkGetPhysicalDeviceExternalSemaphorePropertiesKHR':''' GetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, pExternalSemaphoreInfo, pExternalSemaphoreProperties); ''', 'vkGetPhysicalDeviceExternalFenceProperties':''' // Hard-code support for all handle types and features pExternalFenceProperties->exportFromImportedHandleTypes = 0xF; pExternalFenceProperties->compatibleHandleTypes = 0xF; pExternalFenceProperties->externalFenceFeatures = 0x3; ''', 'vkGetPhysicalDeviceExternalFencePropertiesKHR':''' GetPhysicalDeviceExternalFenceProperties(physicalDevice, pExternalFenceInfo, pExternalFenceProperties); ''', 'vkGetPhysicalDeviceExternalBufferProperties':''' constexpr VkExternalMemoryHandleTypeFlags supported_flags = 0x1FF; if (pExternalBufferInfo->handleType & supported_flags) { pExternalBufferProperties->externalMemoryProperties.externalMemoryFeatures = 0x7; pExternalBufferProperties->externalMemoryProperties.exportFromImportedHandleTypes = supported_flags; pExternalBufferProperties->externalMemoryProperties.compatibleHandleTypes = supported_flags; } else { pExternalBufferProperties->externalMemoryProperties.externalMemoryFeatures = 0; pExternalBufferProperties->externalMemoryProperties.exportFromImportedHandleTypes = 0; // According to spec, handle type is always compatible with itself. Even if export/import // not supported, it's important to properly implement self-compatibility property since // application's control flow can rely on this. pExternalBufferProperties->externalMemoryProperties.compatibleHandleTypes = pExternalBufferInfo->handleType; } ''', 'vkGetPhysicalDeviceExternalBufferPropertiesKHR':''' GetPhysicalDeviceExternalBufferProperties(physicalDevice, pExternalBufferInfo, pExternalBufferProperties); ''', 'vkGetBufferMemoryRequirements': ''' // TODO: Just hard-coding reqs for now pMemoryRequirements->size = 4096; pMemoryRequirements->alignment = 1; pMemoryRequirements->memoryTypeBits = 0xFFFF; // Return a better size based on the buffer size from the create info. unique_lock_t lock(global_lock); auto d_iter = buffer_map.find(device); if (d_iter != buffer_map.end()) { auto iter = d_iter->second.find(buffer); if (iter != d_iter->second.end()) { pMemoryRequirements->size = ((iter->second.size + 4095) / 4096) * 4096; } } ''', 'vkGetBufferMemoryRequirements2KHR': ''' GetBufferMemoryRequirements(device, pInfo->buffer, &pMemoryRequirements->memoryRequirements); ''', 'vkGetDeviceBufferMemoryRequirements': ''' // TODO: Just hard-coding reqs for now pMemoryRequirements->memoryRequirements.alignment = 1; pMemoryRequirements->memoryRequirements.memoryTypeBits = 0xFFFF; // Return a size based on the buffer size from the create info. pMemoryRequirements->memoryRequirements.size = ((pInfo->pCreateInfo->size + 4095) / 4096) * 4096; ''', 'vkGetDeviceBufferMemoryRequirementsKHR': ''' GetDeviceBufferMemoryRequirements(device, pInfo, pMemoryRequirements); ''', 'vkGetImageMemoryRequirements': ''' pMemoryRequirements->size = 0; pMemoryRequirements->alignment = 1; unique_lock_t lock(global_lock); auto d_iter = image_memory_size_map.find(device); if(d_iter != image_memory_size_map.end()){ auto iter = d_iter->second.find(image); if (iter != d_iter->second.end()) { pMemoryRequirements->size = iter->second; } } // Here we hard-code that the memory type at index 3 doesn't support this image. pMemoryRequirements->memoryTypeBits = 0xFFFF & ~(0x1 << 3); ''', 'vkGetImageMemoryRequirements2KHR': ''' GetImageMemoryRequirements(device, pInfo->image, &pMemoryRequirements->memoryRequirements); ''', 'vkGetDeviceImageMemoryRequirements': ''' pMemoryRequirements->memoryRequirements.size = GetImageSizeFromCreateInfo(pInfo->pCreateInfo); pMemoryRequirements->memoryRequirements.alignment = 1; // Here we hard-code that the memory type at index 3 doesn't support this image. pMemoryRequirements->memoryRequirements.memoryTypeBits = 0xFFFF & ~(0x1 << 3); ''', 'vkGetDeviceImageMemoryRequirementsKHR': ''' GetDeviceImageMemoryRequirements(device, pInfo, pMemoryRequirements); ''', 'vkMapMemory': ''' unique_lock_t lock(global_lock); if (VK_WHOLE_SIZE == size) { if (allocated_memory_size_map.count(memory) != 0) size = allocated_memory_size_map[memory] - offset; else size = 0x10000; } void* map_addr = malloc((size_t)size); mapped_memory_map[memory].push_back(map_addr); *ppData = map_addr; return VK_SUCCESS; ''', 'vkMapMemory2KHR': ''' return MapMemory(device, pMemoryMapInfo->memory, pMemoryMapInfo->offset, pMemoryMapInfo->size, pMemoryMapInfo->flags, ppData); ''', 'vkUnmapMemory': ''' unique_lock_t lock(global_lock); for (auto map_addr : mapped_memory_map[memory]) { free(map_addr); } mapped_memory_map.erase(memory); ''', 'vkUnmapMemory2KHR': ''' UnmapMemory(device, pMemoryUnmapInfo->memory); return VK_SUCCESS; ''', 'vkGetImageSubresourceLayout': ''' // Need safe values. Callers are computing memory offsets from pLayout, with no return code to flag failure. *pLayout = VkSubresourceLayout(); // Default constructor zero values. ''', 'vkCreateSwapchainKHR': ''' unique_lock_t lock(global_lock); *pSwapchain = (VkSwapchainKHR)global_unique_handle++; for(uint32_t i = 0; i < icd_swapchain_image_count; ++i){ swapchain_image_map[*pSwapchain][i] = (VkImage)global_unique_handle++; } return VK_SUCCESS; ''', 'vkDestroySwapchainKHR': ''' unique_lock_t lock(global_lock); swapchain_image_map.clear(); ''', 'vkGetSwapchainImagesKHR': ''' if (!pSwapchainImages) { *pSwapchainImageCount = icd_swapchain_image_count; } else { unique_lock_t lock(global_lock); for (uint32_t img_i = 0; img_i < (std::min)(*pSwapchainImageCount, icd_swapchain_image_count); ++img_i){ pSwapchainImages[img_i] = swapchain_image_map.at(swapchain)[img_i]; } if (*pSwapchainImageCount < icd_swapchain_image_count) return VK_INCOMPLETE; else if (*pSwapchainImageCount > icd_swapchain_image_count) *pSwapchainImageCount = icd_swapchain_image_count; } return VK_SUCCESS; ''', 'vkAcquireNextImageKHR': ''' *pImageIndex = 0; return VK_SUCCESS; ''', 'vkAcquireNextImage2KHR': ''' *pImageIndex = 0; return VK_SUCCESS; ''', 'vkCreateBuffer': ''' unique_lock_t lock(global_lock); *pBuffer = (VkBuffer)global_unique_handle++; buffer_map[device][*pBuffer] = { pCreateInfo->size, current_available_address }; current_available_address += pCreateInfo->size; // Always align to next 64-bit pointer const uint64_t alignment = current_available_address % 64; if (alignment != 0) { current_available_address += (64 - alignment); } return VK_SUCCESS; ''', 'vkDestroyBuffer': ''' unique_lock_t lock(global_lock); buffer_map[device].erase(buffer); ''', 'vkCreateImage': ''' unique_lock_t lock(global_lock); *pImage = (VkImage)global_unique_handle++; image_memory_size_map[device][*pImage] = GetImageSizeFromCreateInfo(pCreateInfo); return VK_SUCCESS; ''', 'vkDestroyImage': ''' unique_lock_t lock(global_lock); image_memory_size_map[device].erase(image); ''', 'vkEnumeratePhysicalDeviceGroupsKHR': ''' if (!pPhysicalDeviceGroupProperties) { *pPhysicalDeviceGroupCount = 1; } else { // arbitrary pPhysicalDeviceGroupProperties->physicalDeviceCount = 1; pPhysicalDeviceGroupProperties->physicalDevices[0] = physical_device_map.at(instance)[0]; pPhysicalDeviceGroupProperties->subsetAllocation = VK_FALSE; } return VK_SUCCESS; ''', 'vkGetPhysicalDeviceMultisamplePropertiesEXT': ''' if (pMultisampleProperties) { // arbitrary pMultisampleProperties->maxSampleLocationGridSize = {32, 32}; } ''', 'vkGetPhysicalDeviceFragmentShadingRatesKHR': ''' if (!pFragmentShadingRates) { *pFragmentShadingRateCount = 1; } else { // arbitrary pFragmentShadingRates->sampleCounts = VK_SAMPLE_COUNT_1_BIT | VK_SAMPLE_COUNT_4_BIT; pFragmentShadingRates->fragmentSize = {8, 8}; } return VK_SUCCESS; ''', 'vkGetPhysicalDeviceCalibrateableTimeDomainsEXT': ''' if (!pTimeDomains) { *pTimeDomainCount = 1; } else { // arbitrary *pTimeDomains = VK_TIME_DOMAIN_DEVICE_EXT; } return VK_SUCCESS; ''', 'vkGetFenceWin32HandleKHR': ''' *pHandle = (HANDLE)0x12345678; return VK_SUCCESS; ''', 'vkGetFenceFdKHR': ''' *pFd = 0x42; return VK_SUCCESS; ''', 'vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR': ''' if (!pCounters) { *pCounterCount = 3; } else { if (*pCounterCount == 0){ return VK_INCOMPLETE; } // arbitrary pCounters[0].unit = VK_PERFORMANCE_COUNTER_UNIT_GENERIC_KHR; pCounters[0].scope = VK_QUERY_SCOPE_COMMAND_BUFFER_KHR; pCounters[0].storage = VK_PERFORMANCE_COUNTER_STORAGE_INT32_KHR; pCounters[0].uuid[0] = 0x01; if (*pCounterCount == 1){ return VK_INCOMPLETE; } pCounters[1].unit = VK_PERFORMANCE_COUNTER_UNIT_GENERIC_KHR; pCounters[1].scope = VK_QUERY_SCOPE_RENDER_PASS_KHR; pCounters[1].storage = VK_PERFORMANCE_COUNTER_STORAGE_INT32_KHR; pCounters[1].uuid[0] = 0x02; if (*pCounterCount == 2){ return VK_INCOMPLETE; } pCounters[2].unit = VK_PERFORMANCE_COUNTER_UNIT_GENERIC_KHR; pCounters[2].scope = VK_QUERY_SCOPE_COMMAND_KHR; pCounters[2].storage = VK_PERFORMANCE_COUNTER_STORAGE_INT32_KHR; pCounters[2].uuid[0] = 0x03; *pCounterCount = 3; } return VK_SUCCESS; ''', 'vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR': ''' if (pNumPasses) { // arbitrary *pNumPasses = 1; } ''', 'vkGetShaderModuleIdentifierEXT': ''' if (pIdentifier) { // arbitrary pIdentifier->identifierSize = 1; pIdentifier->identifier[0] = 0x01; } ''', 'vkGetImageSparseMemoryRequirements': ''' if (!pSparseMemoryRequirements) { *pSparseMemoryRequirementCount = 1; } else { // arbitrary pSparseMemoryRequirements->imageMipTailFirstLod = 0; pSparseMemoryRequirements->imageMipTailSize = 8; pSparseMemoryRequirements->imageMipTailOffset = 0; pSparseMemoryRequirements->imageMipTailStride = 4; pSparseMemoryRequirements->formatProperties.imageGranularity = {4, 4, 4}; pSparseMemoryRequirements->formatProperties.flags = VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT; // Would need to track the VkImage to know format for better value here pSparseMemoryRequirements->formatProperties.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT | VK_IMAGE_ASPECT_METADATA_BIT; } ''', 'vkGetImageSparseMemoryRequirements2KHR': ''' if (pSparseMemoryRequirementCount && pSparseMemoryRequirements) { GetImageSparseMemoryRequirements(device, pInfo->image, pSparseMemoryRequirementCount, &pSparseMemoryRequirements->memoryRequirements); } else { GetImageSparseMemoryRequirements(device, pInfo->image, pSparseMemoryRequirementCount, nullptr); } ''', 'vkGetBufferDeviceAddress': ''' VkDeviceAddress address = 0; auto d_iter = buffer_map.find(device); if (d_iter != buffer_map.end()) { auto iter = d_iter->second.find(pInfo->buffer); if (iter != d_iter->second.end()) { address = iter->second.address; } } return address; ''', 'vkGetBufferDeviceAddressKHR': ''' return GetBufferDeviceAddress(device, pInfo); ''', 'vkGetBufferDeviceAddressEXT': ''' return GetBufferDeviceAddress(device, pInfo); ''', 'vkGetDescriptorSetLayoutSizeEXT': ''' // Need to give something non-zero *pLayoutSizeInBytes = 4; ''', 'vkGetAccelerationStructureBuildSizesKHR': ''' // arbitrary pSizeInfo->accelerationStructureSize = 4; pSizeInfo->updateScratchSize = 4; pSizeInfo->buildScratchSize = 4; ''', 'vkGetAccelerationStructureMemoryRequirementsNV': ''' // arbitrary pMemoryRequirements->memoryRequirements.size = 4096; pMemoryRequirements->memoryRequirements.alignment = 1; pMemoryRequirements->memoryRequirements.memoryTypeBits = 0xFFFF; ''', 'vkGetAccelerationStructureDeviceAddressKHR': ''' // arbitrary - need to be aligned to 256 bytes return 0x262144; ''', 'vkGetVideoSessionMemoryRequirementsKHR': ''' if (!pMemoryRequirements) { *pMemoryRequirementsCount = 1; } else { // arbitrary pMemoryRequirements[0].memoryBindIndex = 0; pMemoryRequirements[0].memoryRequirements.size = 4096; pMemoryRequirements[0].memoryRequirements.alignment = 1; pMemoryRequirements[0].memoryRequirements.memoryTypeBits = 0xFFFF; } return VK_SUCCESS; ''', 'vkGetPhysicalDeviceVideoFormatPropertiesKHR': ''' if (!pVideoFormatProperties) { *pVideoFormatPropertyCount = 2; } else { // arbitrary pVideoFormatProperties[0].format = VK_FORMAT_R8G8B8A8_UNORM; pVideoFormatProperties[0].imageCreateFlags = VK_IMAGE_TYPE_2D; pVideoFormatProperties[0].imageType = VK_IMAGE_TYPE_2D; pVideoFormatProperties[0].imageTiling = VK_IMAGE_TILING_OPTIMAL; pVideoFormatProperties[0].imageUsageFlags = VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_SRC_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR; pVideoFormatProperties[1].format = VK_FORMAT_R8G8B8A8_SNORM; pVideoFormatProperties[1].imageCreateFlags = VK_IMAGE_TYPE_2D; pVideoFormatProperties[1].imageType = VK_IMAGE_TYPE_2D; pVideoFormatProperties[1].imageTiling = VK_IMAGE_TILING_OPTIMAL; pVideoFormatProperties[1].imageUsageFlags = VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_SRC_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR; } return VK_SUCCESS; ''', 'vkGetPhysicalDeviceVideoCapabilitiesKHR': ''' // arbitrary auto *decode_caps = lvl_find_mod_in_chain(pCapabilities->pNext); if (decode_caps) { decode_caps->flags = VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR | VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR; } pCapabilities->flags = 0; pCapabilities->minBitstreamBufferOffsetAlignment = 4; pCapabilities->minBitstreamBufferSizeAlignment = 4; pCapabilities->pictureAccessGranularity = {1, 1}; pCapabilities->minCodedExtent = {4, 4}; pCapabilities->maxCodedExtent = {16, 16}; pCapabilities->maxDpbSlots = 4; pCapabilities->maxActiveReferencePictures = 4; return VK_SUCCESS; ''', 'vkGetDescriptorSetLayoutSupport':''' if (pSupport) { pSupport->supported = VK_TRUE; } ''', 'vkGetDescriptorSetLayoutSupportKHR':''' GetDescriptorSetLayoutSupport(device, pCreateInfo, pSupport); ''', 'vkGetRenderAreaGranularity': ''' pGranularity->width = 1; pGranularity->height = 1; ''', } # MockICDGeneratorOptions - subclass of GeneratorOptions. # # Adds options used by MockICDOutputGenerator objects during Mock # ICD generation. # # Additional members # prefixText - list of strings to prefix generated header with # (usually a copyright statement + calling convention macros). # protectFile - True if multiple inclusion protection should be # generated (based on the filename) around the entire header. # protectFeature - True if #ifndef..#endif protection should be # generated around a feature interface in the header file. # genFuncPointers - True if function pointer typedefs should be # generated # protectProto - If conditional protection should be generated # around prototype declarations, set to either '#ifdef' # to require opt-in (#ifdef protectProtoStr) or '#ifndef' # to require opt-out (#ifndef protectProtoStr). Otherwise # set to None. # protectProtoStr - #ifdef/#ifndef symbol to use around prototype # declarations, if protectProto is set # apicall - string to use for the function declaration prefix, # such as APICALL on Windows. # apientry - string to use for the calling convention macro, # in typedefs, such as APIENTRY. # apientryp - string to use for the calling convention macro # in function pointer typedefs, such as APIENTRYP. # indentFuncProto - True if prototype declarations should put each # parameter on a separate line # indentFuncPointer - True if typedefed function pointers should put each # parameter on a separate line # alignFuncParam - if nonzero and parameters are being put on a # separate line, align parameter names at the specified column class MockICDGeneratorOptions(GeneratorOptions): def __init__(self, conventions = None, filename = None, directory = '.', genpath = None, apiname = None, profile = None, versions = '.*', emitversions = '.*', defaultExtensions = None, addExtensions = None, removeExtensions = None, emitExtensions = None, sortProcedure = regSortFeatures, prefixText = "", genFuncPointers = True, protectFile = True, protectFeature = True, protectProto = None, protectProtoStr = None, apicall = '', apientry = '', apientryp = '', indentFuncProto = True, indentFuncPointer = False, alignFuncParam = 0, expandEnumerants = True, helper_file_type = ''): GeneratorOptions.__init__(self, conventions = conventions, filename = filename, directory = directory, genpath = genpath, apiname = apiname, profile = profile, versions = versions, emitversions = emitversions, defaultExtensions = defaultExtensions, addExtensions = addExtensions, removeExtensions = removeExtensions, emitExtensions = emitExtensions, sortProcedure = sortProcedure) self.prefixText = prefixText self.genFuncPointers = genFuncPointers self.protectFile = protectFile self.protectFeature = protectFeature self.protectProto = protectProto self.protectProtoStr = protectProtoStr self.apicall = apicall self.apientry = apientry self.apientryp = apientryp self.indentFuncProto = indentFuncProto self.indentFuncPointer = indentFuncPointer self.alignFuncParam = alignFuncParam # MockICDOutputGenerator - subclass of OutputGenerator. # Generates a mock vulkan ICD. # This is intended to be a minimal replacement for a vulkan device in order # to enable Vulkan Validation testing. # # ---- methods ---- # MockOutputGenerator(errFile, warnFile, diagFile) - args as for # OutputGenerator. Defines additional internal state. # ---- methods overriding base class ---- # beginFile(genOpts) # endFile() # beginFeature(interface, emit) # endFeature() # genType(typeinfo,name) # genStruct(typeinfo,name) # genGroup(groupinfo,name) # genEnum(enuminfo, name) # genCmd(cmdinfo) class MockICDOutputGenerator(OutputGenerator): """Generate specified API interfaces in a specific style, such as a C header""" # This is an ordered list of sections in the header file. TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum', 'group', 'bitmask', 'funcpointer', 'struct'] ALL_SECTIONS = TYPE_SECTIONS + ['command'] def __init__(self, errFile = sys.stderr, warnFile = sys.stderr, diagFile = sys.stdout): OutputGenerator.__init__(self, errFile, warnFile, diagFile) # Internal state - accumulators for different inner block text self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) self.intercepts = [] self.function_declarations = False # Check if the parameter passed in is a pointer to an array def paramIsArray(self, param): return param.attrib.get('len') is not None # Check if the parameter passed in is a pointer def paramIsPointer(self, param): ispointer = False for elem in param: if ((elem.tag != 'type') and (elem.tail is not None)) and '*' in elem.tail: ispointer = True return ispointer # Check if an object is a non-dispatchable handle def isHandleTypeNonDispatchable(self, handletype): handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']") if handle is not None and handle.find('type').text == 'VK_DEFINE_NON_DISPATCHABLE_HANDLE': return True else: return False # Check if an object is a dispatchable handle def isHandleTypeDispatchable(self, handletype): handle = self.registry.tree.find("types/type/[name='" + handletype + "'][@category='handle']") if handle is not None and handle.find('type').text == 'VK_DEFINE_HANDLE': return True else: return False # Check that the target API is in the supported list for the extension def checkExtensionAPISupport(self, supported): return self.genOpts.apiname in supported.split(',') def beginFile(self, genOpts): OutputGenerator.beginFile(self, genOpts) # C-specific # # Multiple inclusion protection & C++ namespace. if (genOpts.protectFile and self.genOpts.filename == "function_declarations.h"): self.function_declarations = True # User-supplied prefix text, if any (list of strings) if (genOpts.prefixText): for s in genOpts.prefixText: write(s, file=self.outFile) if self.function_declarations: self.newline() # Include all of the extensions in ICD except specific ignored ones device_exts = [] instance_exts = [] # Ignore extensions that ICDs should not implement or are not safe to report ignore_exts = ['VK_EXT_validation_cache', 'VK_KHR_portability_subset'] for ext in self.registry.tree.findall("extensions/extension"): if self.checkExtensionAPISupport(ext.attrib['supported']): # Only include API-relevant extensions if (ext.attrib['name'] not in ignore_exts): # Search for extension version enum for enum in ext.findall('require/enum'): if enum.get('name', '').endswith('_SPEC_VERSION'): ext_version = enum.get('value') if (ext.attrib.get('type') == 'instance'): instance_exts.append(' {"%s", %s},' % (ext.attrib['name'], ext_version)) else: device_exts.append(' {"%s", %s},' % (ext.attrib['name'], ext_version)) break write('#pragma once\n',file=self.outFile) write('#include ',file=self.outFile) write('#include ',file=self.outFile) write('#include ',file=self.outFile) write('#include ',file=self.outFile) self.newline() write('namespace vkmock {\n', file=self.outFile) write('// Map of instance extension name to version', file=self.outFile) write('static const std::unordered_map instance_extension_map = {', file=self.outFile) write('\n'.join(instance_exts), file=self.outFile) write('};', file=self.outFile) write('// Map of device extension name to version', file=self.outFile) write('static const std::unordered_map device_extension_map = {', file=self.outFile) write('\n'.join(device_exts), file=self.outFile) write('};', file=self.outFile) else: write('#pragma once\n',file=self.outFile) write('#include "mock_icd.h"',file=self.outFile) write('#include "function_declarations.h"\n',file=self.outFile) write('namespace vkmock {', file=self.outFile) def endFile(self): # C-specific # Finish C++ namespace self.newline() if self.function_declarations: # record intercepted procedures write('// Map of all APIs to be intercepted by this layer', file=self.outFile) write('static const std::unordered_map name_to_funcptr_map = {', file=self.outFile) write('\n'.join(self.intercepts), file=self.outFile) write('};\n', file=self.outFile) write('} // namespace vkmock', file=self.outFile) self.newline() # Finish processing in superclass OutputGenerator.endFile(self) def beginFeature(self, interface, emit): #write('// starting beginFeature', file=self.outFile) # Start processing in superclass OutputGenerator.beginFeature(self, interface, emit) self.featureExtraProtect = GetFeatureProtect(interface) # C-specific # Accumulate includes, defines, types, enums, function pointer typedefs, # end function prototypes separately for this feature. They're only # printed in endFeature(). self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) #write('// ending beginFeature', file=self.outFile) def endFeature(self): # C-specific # Actually write the interface to the output file. #write('// starting endFeature', file=self.outFile) if (self.emit): self.newline() if (self.genOpts.protectFeature): write('#ifndef', self.featureName, file=self.outFile) # If type declarations are needed by other features based on # this one, it may be necessary to suppress the ExtraProtect, # or move it below the 'for section...' loop. #write('// endFeature looking at self.featureExtraProtect', file=self.outFile) if (self.featureExtraProtect != None): write('#ifdef', self.featureExtraProtect, file=self.outFile) #write('#define', self.featureName, '1', file=self.outFile) for section in self.TYPE_SECTIONS: #write('// endFeature writing section'+section, file=self.outFile) contents = self.sections[section] if contents: write('\n'.join(contents), file=self.outFile) self.newline() #write('// endFeature looking at self.sections[command]', file=self.outFile) if (self.sections['command']): write('\n'.join(self.sections['command']), end=u'', file=self.outFile) self.newline() if (self.featureExtraProtect != None): write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile) if (self.genOpts.protectFeature): write('#endif /*', self.featureName, '*/', file=self.outFile) # Finish processing in superclass OutputGenerator.endFeature(self) #write('// ending endFeature', file=self.outFile) # # Append a definition to the specified section def appendSection(self, section, text): # self.sections[section].append('SECTION: ' + section + '\n') self.sections[section].append(text) # # Type generation def genType(self, typeinfo, name, alias): pass # # Struct (e.g. C "struct" type) generation. # This is a special case of the tag where the contents are # interpreted as a set of tags instead of freeform C # C type declarations. The tags are just like # tags - they are a declaration of a struct or union member. # Only simple member declarations are supported (no nested # structs etc.) def genStruct(self, typeinfo, typeName, alias): OutputGenerator.genStruct(self, typeinfo, typeName, alias) body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n' # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam) for member in typeinfo.elem.findall('.//member'): body += self.makeCParamDecl(member, self.genOpts.alignFuncParam) body += ';\n' body += '} ' + typeName + ';\n' self.appendSection('struct', body) # # Group (e.g. C "enum" type) generation. # These are concatenated together with other types. def genGroup(self, groupinfo, groupName, alias): pass # Enumerant generation # tags may specify their values in several ways, but are usually # just integers. def genEnum(self, enuminfo, name, alias): pass # # Command generation def genCmd(self, cmdinfo, name, alias): decls = self.makeCDecls(cmdinfo.elem) if self.function_declarations: # In the header declare all intercepts self.appendSection('command', '') self.appendSection('command', 'static %s' % (decls[0])) if (self.featureExtraProtect != None): self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ] self.intercepts += [ ' {"%s", (void*)%s},' % (name,name[2:]) ] if (self.featureExtraProtect != None): self.intercepts += [ '#endif' ] return manual_functions = [ # Include functions here to be intercepted w/ manually implemented function bodies 'vkGetDeviceProcAddr', 'vkGetInstanceProcAddr', 'vkCreateDevice', 'vkDestroyDevice', 'vkCreateInstance', 'vkDestroyInstance', 'vkFreeCommandBuffers', 'vkAllocateCommandBuffers', 'vkDestroyCommandPool', #'vkCreateDebugReportCallbackEXT', #'vkDestroyDebugReportCallbackEXT', 'vkEnumerateInstanceLayerProperties', 'vkEnumerateInstanceVersion', 'vkEnumerateInstanceExtensionProperties', 'vkEnumerateDeviceLayerProperties', 'vkEnumerateDeviceExtensionProperties', ] if name in manual_functions: self.appendSection('command', '') if name not in CUSTOM_C_INTERCEPTS: self.appendSection('command', '// declare only') self.appendSection('command', 'static %s' % (decls[0])) self.appendSection('command', '// TODO: Implement custom intercept body') else: self.appendSection('command', 'static %s' % (decls[0][:-1])) self.appendSection('command', '{\n%s}' % (CUSTOM_C_INTERCEPTS[name])) self.intercepts += [ ' {"%s", (void*)%s},' % (name,name[2:]) ] return # record that the function will be intercepted if (self.featureExtraProtect != None): self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ] self.intercepts += [ ' {"%s", (void*)%s},' % (name,name[2:]) ] if (self.featureExtraProtect != None): self.intercepts += [ '#endif' ] OutputGenerator.genCmd(self, cmdinfo, name, alias) # self.appendSection('command', '') self.appendSection('command', 'static %s' % (decls[0][:-1])) if name in CUSTOM_C_INTERCEPTS: self.appendSection('command', '{%s}' % (CUSTOM_C_INTERCEPTS[name])) return # Declare result variable, if any. resulttype = cmdinfo.elem.find('proto/type') if (resulttype != None and resulttype.text == 'void'): resulttype = None # if the name w/ KHR postfix is in the CUSTOM_C_INTERCEPTS # Call the KHR custom version instead of generating separate code khr_name = name + "KHR" if khr_name in CUSTOM_C_INTERCEPTS: return_string = '' if resulttype != None: return_string = 'return ' params = cmdinfo.elem.findall('param/name') param_names = [] for param in params: param_names.append(param.text) self.appendSection('command', '{\n %s%s(%s);\n}' % (return_string, khr_name[2:], ", ".join(param_names))) return self.appendSection('command', '{') api_function_name = cmdinfo.elem.attrib.get('name') # GET THE TYPE OF FUNCTION if any(api_function_name.startswith(ftxt) for ftxt in ('vkCreate', 'vkAllocate')): # Get last param last_param = cmdinfo.elem.findall('param')[-1] lp_txt = last_param.find('name').text lp_len = None if ('len' in last_param.attrib): lp_len = last_param.attrib['len'] lp_len = lp_len.replace('::', '->') lp_type = last_param.find('type').text handle_type = 'dispatchable' allocator_txt = 'CreateDispObjHandle()'; if (self.isHandleTypeNonDispatchable(lp_type)): handle_type = 'non-' + handle_type allocator_txt = 'global_unique_handle++'; # Need to lock in both cases self.appendSection('command', ' unique_lock_t lock(global_lock);') if (lp_len != None): #print("%s last params (%s) has len %s" % (handle_type, lp_txt, lp_len)) self.appendSection('command', ' for (uint32_t i = 0; i < %s; ++i) {' % (lp_len)) self.appendSection('command', ' %s[i] = (%s)%s;' % (lp_txt, lp_type, allocator_txt)) self.appendSection('command', ' }') else: #print("Single %s last param is '%s' w/ type '%s'" % (handle_type, lp_txt, lp_type)) if 'AllocateMemory' in api_function_name: # Store allocation size in case it's mapped self.appendSection('command', ' allocated_memory_size_map[(VkDeviceMemory)global_unique_handle] = pAllocateInfo->allocationSize;') self.appendSection('command', ' *%s = (%s)%s;' % (lp_txt, lp_type, allocator_txt)) elif True in [ftxt in api_function_name for ftxt in ['Destroy', 'Free']]: self.appendSection('command', '//Destroy object') if 'FreeMemory' in api_function_name: # Remove from allocation map self.appendSection('command', ' unique_lock_t lock(global_lock);') self.appendSection('command', ' allocated_memory_size_map.erase(memory);') else: self.appendSection('command', '//Not a CREATE or DESTROY function') # Return result variable, if any. if (resulttype != None): if api_function_name == 'vkGetEventStatus': self.appendSection('command', ' return VK_EVENT_SET;') else: self.appendSection('command', ' return VK_SUCCESS;') self.appendSection('command', '}') # # override makeProtoName to drop the "vk" prefix def makeProtoName(self, name, tail): return self.genOpts.apientry + name[2:] + tail