| // Copyright (C) 2018 The Android Open Source Project |
| // Copyright (C) 2018 Google Inc. |
| // |
| // 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 "ResourceTracker.h" |
| |
| #include "../OpenglSystemCommon/EmulatorFeatureInfo.h" |
| #include "../OpenglSystemCommon/HostConnection.h" |
| #include "CommandBufferStagingStream.h" |
| #include "DescriptorSetVirtualization.h" |
| #include "HostVisibleMemoryVirtualization.h" |
| #include "Resources.h" |
| #include "VkEncoder.h" |
| #include "aemu/base/AlignedBuf.h" |
| #include "gfxstream_vk_private.h" |
| #include "goldfish_address_space.h" |
| #include "goldfish_vk_private_defs.h" |
| #include "util.h" |
| #include "virtgpu_gfxstream_protocol.h" |
| #include "vulkan/vk_enum_string_helper.h" |
| #include "vulkan/vulkan_core.h" |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| #include "vk_format_info.h" |
| #endif |
| #include <stdlib.h> |
| #include <vndk/hardware_buffer.h> |
| |
| #include <algorithm> |
| #include <set> |
| #include <string> |
| #include <unordered_map> |
| #include <unordered_set> |
| |
| #include "vk_struct_id.h" |
| #include "vk_util.h" |
| |
| #if defined(__ANDROID__) || defined(__linux__) || defined(__APPLE__) |
| |
| #include <sys/mman.h> |
| #include <sys/syscall.h> |
| |
| |
| static inline int inline_memfd_create(const char* name, unsigned int flags) { |
| #if defined(__ANDROID__) |
| return syscall(SYS_memfd_create, name, flags); |
| #else |
| return -1; |
| #endif |
| } |
| |
| #define memfd_create inline_memfd_create |
| #endif |
| |
| #ifndef VK_USE_PLATFORM_FUCHSIA |
| void zx_handle_close(zx_handle_t) {} |
| void zx_event_create(int, zx_handle_t*) {} |
| #endif |
| |
| static constexpr uint32_t kDefaultApiVersion = VK_MAKE_VERSION(1, 1, 0); |
| |
| namespace gfxstream { |
| namespace vk { |
| |
| #define MAKE_HANDLE_MAPPING_FOREACH(type_name, map_impl, map_to_u64_impl, map_from_u64_impl) \ |
| void mapHandles_##type_name(type_name* handles, size_t count) override { \ |
| for (size_t i = 0; i < count; ++i) { \ |
| map_impl; \ |
| } \ |
| } \ |
| void mapHandles_##type_name##_u64(const type_name* handles, uint64_t* handle_u64s, \ |
| size_t count) override { \ |
| for (size_t i = 0; i < count; ++i) { \ |
| map_to_u64_impl; \ |
| } \ |
| } \ |
| void mapHandles_u64_##type_name(const uint64_t* handle_u64s, type_name* handles, size_t count) \ |
| override { \ |
| for (size_t i = 0; i < count; ++i) { \ |
| map_from_u64_impl; \ |
| } \ |
| } |
| |
| #define DEFINE_RESOURCE_TRACKING_CLASS(class_name, impl) \ |
| class class_name : public VulkanHandleMapping { \ |
| public: \ |
| virtual ~class_name() {} \ |
| GOLDFISH_VK_LIST_HANDLE_TYPES(impl) \ |
| }; |
| |
| #define CREATE_MAPPING_IMPL_FOR_TYPE(type_name) \ |
| MAKE_HANDLE_MAPPING_FOREACH( \ |
| type_name, handles[i] = new_from_host_##type_name(handles[i]); \ |
| ResourceTracker::get()->register_##type_name(handles[i]); \ |
| , handle_u64s[i] = (uint64_t)new_from_host_##type_name(handles[i]), \ |
| handles[i] = (type_name)new_from_host_u64_##type_name(handle_u64s[i]); \ |
| ResourceTracker::get()->register_##type_name(handles[i]);) |
| |
| #define UNWRAP_MAPPING_IMPL_FOR_TYPE(type_name) \ |
| MAKE_HANDLE_MAPPING_FOREACH( \ |
| type_name, handles[i] = get_host_##type_name(handles[i]), \ |
| handle_u64s[i] = (uint64_t)get_host_u64_##type_name(handles[i]), \ |
| handles[i] = (type_name)get_host_##type_name((type_name)handle_u64s[i])) |
| |
| #define DESTROY_MAPPING_IMPL_FOR_TYPE(type_name) \ |
| MAKE_HANDLE_MAPPING_FOREACH(type_name, \ |
| ResourceTracker::get()->unregister_##type_name(handles[i]); \ |
| delete_goldfish_##type_name(handles[i]), (void)handle_u64s[i]; \ |
| delete_goldfish_##type_name(handles[i]), (void)handles[i]; \ |
| delete_goldfish_##type_name((type_name)handle_u64s[i])) |
| |
| DEFINE_RESOURCE_TRACKING_CLASS(CreateMapping, CREATE_MAPPING_IMPL_FOR_TYPE) |
| DEFINE_RESOURCE_TRACKING_CLASS(DestroyMapping, DESTROY_MAPPING_IMPL_FOR_TYPE) |
| |
| static uint32_t* sSeqnoPtr = nullptr; |
| |
| // static |
| uint32_t ResourceTracker::streamFeatureBits = 0; |
| ResourceTracker::ThreadingCallbacks ResourceTracker::threadingCallbacks; |
| |
| struct StagingInfo { |
| Lock mLock; |
| std::vector<CommandBufferStagingStream*> streams; |
| std::vector<VkEncoder*> encoders; |
| /// \brief sets alloc and free callbacks for memory allocation for CommandBufferStagingStream(s) |
| /// \param allocFn is the callback to allocate memory |
| /// \param freeFn is the callback to free memory |
| void setAllocFree(CommandBufferStagingStream::Alloc&& allocFn, |
| CommandBufferStagingStream::Free&& freeFn) { |
| mAlloc = allocFn; |
| mFree = freeFn; |
| } |
| |
| ~StagingInfo() { |
| for (auto stream : streams) { |
| delete stream; |
| } |
| |
| for (auto encoder : encoders) { |
| delete encoder; |
| } |
| } |
| |
| void pushStaging(CommandBufferStagingStream* stream, VkEncoder* encoder) { |
| AutoLock<Lock> lock(mLock); |
| stream->reset(); |
| streams.push_back(stream); |
| encoders.push_back(encoder); |
| } |
| |
| void popStaging(CommandBufferStagingStream** streamOut, VkEncoder** encoderOut) { |
| AutoLock<Lock> lock(mLock); |
| CommandBufferStagingStream* stream; |
| VkEncoder* encoder; |
| if (streams.empty()) { |
| if (mAlloc && mFree) { |
| // if custom allocators are provided, forward them to CommandBufferStagingStream |
| stream = new CommandBufferStagingStream(mAlloc, mFree); |
| } else { |
| stream = new CommandBufferStagingStream; |
| } |
| encoder = new VkEncoder(stream); |
| } else { |
| stream = streams.back(); |
| encoder = encoders.back(); |
| streams.pop_back(); |
| encoders.pop_back(); |
| } |
| *streamOut = stream; |
| *encoderOut = encoder; |
| } |
| |
| private: |
| CommandBufferStagingStream::Alloc mAlloc = nullptr; |
| CommandBufferStagingStream::Free mFree = nullptr; |
| }; |
| |
| static StagingInfo sStaging; |
| |
| struct CommandBufferPendingDescriptorSets { |
| std::unordered_set<VkDescriptorSet> sets; |
| }; |
| |
| #define HANDLE_REGISTER_IMPL_IMPL(type) \ |
| void ResourceTracker::register_##type(type obj) { \ |
| AutoLock<RecursiveLock> lock(mLock); \ |
| info_##type[obj] = type##_Info(); \ |
| } |
| |
| #define HANDLE_UNREGISTER_IMPL_IMPL(type) \ |
| void ResourceTracker::unregister_##type(type obj) { \ |
| AutoLock<RecursiveLock> lock(mLock); \ |
| info_##type.erase(obj); \ |
| } |
| |
| GOLDFISH_VK_LIST_HANDLE_TYPES(HANDLE_REGISTER_IMPL_IMPL) |
| GOLDFISH_VK_LIST_TRIVIAL_HANDLE_TYPES(HANDLE_UNREGISTER_IMPL_IMPL) |
| uint32_t getWaitSemaphoreCount(const VkSubmitInfo& pSubmit) { return pSubmit.waitSemaphoreCount; } |
| |
| uint32_t getWaitSemaphoreCount(const VkSubmitInfo2& pSubmit) { |
| return pSubmit.waitSemaphoreInfoCount; |
| } |
| |
| uint32_t getCommandBufferCount(const VkSubmitInfo& pSubmit) { return pSubmit.commandBufferCount; } |
| |
| uint32_t getCommandBufferCount(const VkSubmitInfo2& pSubmit) { |
| return pSubmit.commandBufferInfoCount; |
| } |
| |
| uint32_t getSignalSemaphoreCount(const VkSubmitInfo& pSubmit) { |
| return pSubmit.signalSemaphoreCount; |
| } |
| |
| uint32_t getSignalSemaphoreCount(const VkSubmitInfo2& pSubmit) { |
| return pSubmit.signalSemaphoreInfoCount; |
| } |
| |
| VkSemaphore getWaitSemaphore(const VkSubmitInfo& pSubmit, int i) { |
| return pSubmit.pWaitSemaphores[i]; |
| } |
| |
| VkSemaphore getWaitSemaphore(const VkSubmitInfo2& pSubmit, int i) { |
| return pSubmit.pWaitSemaphoreInfos[i].semaphore; |
| } |
| |
| VkSemaphore getSignalSemaphore(const VkSubmitInfo& pSubmit, int i) { |
| return pSubmit.pSignalSemaphores[i]; |
| } |
| |
| VkSemaphore getSignalSemaphore(const VkSubmitInfo2& pSubmit, int i) { |
| return pSubmit.pSignalSemaphoreInfos[i].semaphore; |
| } |
| |
| VkCommandBuffer getCommandBuffer(const VkSubmitInfo& pSubmit, int i) { |
| return pSubmit.pCommandBuffers[i]; |
| } |
| |
| VkCommandBuffer getCommandBuffer(const VkSubmitInfo2& pSubmit, int i) { |
| return pSubmit.pCommandBufferInfos[i].commandBuffer; |
| } |
| |
| bool descriptorPoolSupportsIndividualFreeLocked(VkDescriptorPool pool) { |
| return as_goldfish_VkDescriptorPool(pool)->allocInfo->createFlags & |
| VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; |
| } |
| |
| VkDescriptorImageInfo createImmutableSamplersFilteredImageInfo( |
| VkDescriptorType descType, VkDescriptorSet descSet, uint32_t binding, |
| const VkDescriptorImageInfo* pImageInfo) { |
| VkDescriptorImageInfo res = *pImageInfo; |
| |
| if (descType != VK_DESCRIPTOR_TYPE_SAMPLER && |
| descType != VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) |
| return res; |
| |
| bool immutableSampler = |
| as_goldfish_VkDescriptorSet(descSet)->reified->bindingIsImmutableSampler[binding]; |
| |
| if (!immutableSampler) return res; |
| |
| res.sampler = 0; |
| |
| return res; |
| } |
| |
| bool descriptorBindingIsImmutableSampler(VkDescriptorSet dstSet, uint32_t dstBinding) { |
| return as_goldfish_VkDescriptorSet(dstSet)->reified->bindingIsImmutableSampler[dstBinding]; |
| } |
| |
| VkDescriptorImageInfo ResourceTracker::filterNonexistentSampler( |
| const VkDescriptorImageInfo& inputInfo) { |
| VkSampler sampler = inputInfo.sampler; |
| |
| VkDescriptorImageInfo res = inputInfo; |
| |
| if (sampler) { |
| auto it = info_VkSampler.find(sampler); |
| bool samplerExists = it != info_VkSampler.end(); |
| if (!samplerExists) res.sampler = 0; |
| } |
| |
| return res; |
| } |
| |
| void ResourceTracker::emitDeviceMemoryReport(VkDevice_Info info, |
| VkDeviceMemoryReportEventTypeEXT type, |
| uint64_t memoryObjectId, VkDeviceSize size, |
| VkObjectType objectType, uint64_t objectHandle, |
| uint32_t heapIndex) { |
| if (info.deviceMemoryReportCallbacks.empty()) return; |
| |
| const VkDeviceMemoryReportCallbackDataEXT callbackData = { |
| VK_STRUCTURE_TYPE_DEVICE_MEMORY_REPORT_CALLBACK_DATA_EXT, // sType |
| nullptr, // pNext |
| 0, // flags |
| type, // type |
| memoryObjectId, // memoryObjectId |
| size, // size |
| objectType, // objectType |
| objectHandle, // objectHandle |
| heapIndex, // heapIndex |
| }; |
| for (const auto& callback : info.deviceMemoryReportCallbacks) { |
| callback.first(&callbackData, callback.second); |
| } |
| } |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| inline fuchsia_sysmem::wire::BufferCollectionConstraints defaultBufferCollectionConstraints( |
| size_t minSizeBytes, size_t minBufferCount, size_t maxBufferCount = 0u, |
| size_t minBufferCountForCamping = 0u, size_t minBufferCountForDedicatedSlack = 0u, |
| size_t minBufferCountForSharedSlack = 0u) { |
| fuchsia_sysmem::wire::BufferCollectionConstraints constraints = {}; |
| constraints.min_buffer_count = minBufferCount; |
| if (maxBufferCount > 0) { |
| constraints.max_buffer_count = maxBufferCount; |
| } |
| if (minBufferCountForCamping) { |
| constraints.min_buffer_count_for_camping = minBufferCountForCamping; |
| } |
| if (minBufferCountForSharedSlack) { |
| constraints.min_buffer_count_for_shared_slack = minBufferCountForSharedSlack; |
| } |
| constraints.has_buffer_memory_constraints = true; |
| fuchsia_sysmem::wire::BufferMemoryConstraints& buffer_constraints = |
| constraints.buffer_memory_constraints; |
| |
| buffer_constraints.min_size_bytes = minSizeBytes; |
| buffer_constraints.max_size_bytes = 0xffffffff; |
| buffer_constraints.physically_contiguous_required = false; |
| buffer_constraints.secure_required = false; |
| |
| // No restrictions on coherency domain or Heaps. |
| buffer_constraints.ram_domain_supported = true; |
| buffer_constraints.cpu_domain_supported = true; |
| buffer_constraints.inaccessible_domain_supported = true; |
| buffer_constraints.heap_permitted_count = 2; |
| buffer_constraints.heap_permitted[0] = fuchsia_sysmem::wire::HeapType::kGoldfishDeviceLocal; |
| buffer_constraints.heap_permitted[1] = fuchsia_sysmem::wire::HeapType::kGoldfishHostVisible; |
| |
| return constraints; |
| } |
| |
| uint32_t getBufferCollectionConstraintsVulkanImageUsage(const VkImageCreateInfo* pImageInfo) { |
| uint32_t usage = 0u; |
| VkImageUsageFlags imageUsage = pImageInfo->usage; |
| |
| #define SetUsageBit(BIT, VALUE) \ |
| if (imageUsage & VK_IMAGE_USAGE_##BIT##_BIT) { \ |
| usage |= fuchsia_sysmem::wire::kVulkanImageUsage##VALUE; \ |
| } |
| |
| SetUsageBit(COLOR_ATTACHMENT, ColorAttachment); |
| SetUsageBit(TRANSFER_SRC, TransferSrc); |
| SetUsageBit(TRANSFER_DST, TransferDst); |
| SetUsageBit(SAMPLED, Sampled); |
| |
| #undef SetUsageBit |
| return usage; |
| } |
| |
| uint32_t getBufferCollectionConstraintsVulkanBufferUsage(VkBufferUsageFlags bufferUsage) { |
| uint32_t usage = 0u; |
| |
| #define SetUsageBit(BIT, VALUE) \ |
| if (bufferUsage & VK_BUFFER_USAGE_##BIT##_BIT) { \ |
| usage |= fuchsia_sysmem::wire::kVulkanBufferUsage##VALUE; \ |
| } |
| |
| SetUsageBit(TRANSFER_SRC, TransferSrc); |
| SetUsageBit(TRANSFER_DST, TransferDst); |
| SetUsageBit(UNIFORM_TEXEL_BUFFER, UniformTexelBuffer); |
| SetUsageBit(STORAGE_TEXEL_BUFFER, StorageTexelBuffer); |
| SetUsageBit(UNIFORM_BUFFER, UniformBuffer); |
| SetUsageBit(STORAGE_BUFFER, StorageBuffer); |
| SetUsageBit(INDEX_BUFFER, IndexBuffer); |
| SetUsageBit(VERTEX_BUFFER, VertexBuffer); |
| SetUsageBit(INDIRECT_BUFFER, IndirectBuffer); |
| |
| #undef SetUsageBit |
| return usage; |
| } |
| |
| uint32_t getBufferCollectionConstraintsVulkanBufferUsage( |
| const VkBufferConstraintsInfoFUCHSIA* pBufferConstraintsInfo) { |
| VkBufferUsageFlags bufferUsage = pBufferConstraintsInfo->createInfo.usage; |
| return getBufferCollectionConstraintsVulkanBufferUsage(bufferUsage); |
| } |
| |
| static fuchsia_sysmem::wire::PixelFormatType vkFormatTypeToSysmem(VkFormat format) { |
| switch (format) { |
| case VK_FORMAT_B8G8R8A8_SINT: |
| case VK_FORMAT_B8G8R8A8_UNORM: |
| case VK_FORMAT_B8G8R8A8_SRGB: |
| case VK_FORMAT_B8G8R8A8_SNORM: |
| case VK_FORMAT_B8G8R8A8_SSCALED: |
| case VK_FORMAT_B8G8R8A8_USCALED: |
| return fuchsia_sysmem::wire::PixelFormatType::kBgra32; |
| case VK_FORMAT_R8G8B8A8_SINT: |
| case VK_FORMAT_R8G8B8A8_UNORM: |
| case VK_FORMAT_R8G8B8A8_SRGB: |
| case VK_FORMAT_R8G8B8A8_SNORM: |
| case VK_FORMAT_R8G8B8A8_SSCALED: |
| case VK_FORMAT_R8G8B8A8_USCALED: |
| return fuchsia_sysmem::wire::PixelFormatType::kR8G8B8A8; |
| case VK_FORMAT_R8_UNORM: |
| case VK_FORMAT_R8_UINT: |
| case VK_FORMAT_R8_USCALED: |
| case VK_FORMAT_R8_SNORM: |
| case VK_FORMAT_R8_SINT: |
| case VK_FORMAT_R8_SSCALED: |
| case VK_FORMAT_R8_SRGB: |
| return fuchsia_sysmem::wire::PixelFormatType::kR8; |
| case VK_FORMAT_R8G8_UNORM: |
| case VK_FORMAT_R8G8_UINT: |
| case VK_FORMAT_R8G8_USCALED: |
| case VK_FORMAT_R8G8_SNORM: |
| case VK_FORMAT_R8G8_SINT: |
| case VK_FORMAT_R8G8_SSCALED: |
| case VK_FORMAT_R8G8_SRGB: |
| return fuchsia_sysmem::wire::PixelFormatType::kR8G8; |
| default: |
| return fuchsia_sysmem::wire::PixelFormatType::kInvalid; |
| } |
| } |
| |
| static bool vkFormatMatchesSysmemFormat(VkFormat vkFormat, |
| fuchsia_sysmem::wire::PixelFormatType sysmemFormat) { |
| switch (vkFormat) { |
| case VK_FORMAT_B8G8R8A8_SINT: |
| case VK_FORMAT_B8G8R8A8_UNORM: |
| case VK_FORMAT_B8G8R8A8_SRGB: |
| case VK_FORMAT_B8G8R8A8_SNORM: |
| case VK_FORMAT_B8G8R8A8_SSCALED: |
| case VK_FORMAT_B8G8R8A8_USCALED: |
| return sysmemFormat == fuchsia_sysmem::wire::PixelFormatType::kBgra32; |
| case VK_FORMAT_R8G8B8A8_SINT: |
| case VK_FORMAT_R8G8B8A8_UNORM: |
| case VK_FORMAT_R8G8B8A8_SRGB: |
| case VK_FORMAT_R8G8B8A8_SNORM: |
| case VK_FORMAT_R8G8B8A8_SSCALED: |
| case VK_FORMAT_R8G8B8A8_USCALED: |
| return sysmemFormat == fuchsia_sysmem::wire::PixelFormatType::kR8G8B8A8; |
| case VK_FORMAT_R8_UNORM: |
| case VK_FORMAT_R8_UINT: |
| case VK_FORMAT_R8_USCALED: |
| case VK_FORMAT_R8_SNORM: |
| case VK_FORMAT_R8_SINT: |
| case VK_FORMAT_R8_SSCALED: |
| case VK_FORMAT_R8_SRGB: |
| return sysmemFormat == fuchsia_sysmem::wire::PixelFormatType::kR8 || |
| sysmemFormat == fuchsia_sysmem::wire::PixelFormatType::kL8; |
| case VK_FORMAT_R8G8_UNORM: |
| case VK_FORMAT_R8G8_UINT: |
| case VK_FORMAT_R8G8_USCALED: |
| case VK_FORMAT_R8G8_SNORM: |
| case VK_FORMAT_R8G8_SINT: |
| case VK_FORMAT_R8G8_SSCALED: |
| case VK_FORMAT_R8G8_SRGB: |
| return sysmemFormat == fuchsia_sysmem::wire::PixelFormatType::kR8G8; |
| default: |
| return false; |
| } |
| } |
| |
| static VkFormat sysmemPixelFormatTypeToVk(fuchsia_sysmem::wire::PixelFormatType format) { |
| switch (format) { |
| case fuchsia_sysmem::wire::PixelFormatType::kBgra32: |
| return VK_FORMAT_B8G8R8A8_SRGB; |
| case fuchsia_sysmem::wire::PixelFormatType::kR8G8B8A8: |
| return VK_FORMAT_R8G8B8A8_SRGB; |
| case fuchsia_sysmem::wire::PixelFormatType::kL8: |
| case fuchsia_sysmem::wire::PixelFormatType::kR8: |
| return VK_FORMAT_R8_UNORM; |
| case fuchsia_sysmem::wire::PixelFormatType::kR8G8: |
| return VK_FORMAT_R8G8_UNORM; |
| default: |
| return VK_FORMAT_UNDEFINED; |
| } |
| } |
| |
| // TODO(fxbug.dev/42172354): This is currently only used for allocating |
| // memory for dedicated external images. It should be migrated to use |
| // SetBufferCollectionImageConstraintsFUCHSIA. |
| VkResult ResourceTracker::setBufferCollectionConstraintsFUCHSIA( |
| VkEncoder* enc, VkDevice device, |
| fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>* collection, |
| const VkImageCreateInfo* pImageInfo) { |
| if (pImageInfo == nullptr) { |
| mesa_loge("setBufferCollectionConstraints: pImageInfo cannot be null."); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| |
| const VkSysmemColorSpaceFUCHSIA kDefaultColorSpace = { |
| .sType = VK_STRUCTURE_TYPE_SYSMEM_COLOR_SPACE_FUCHSIA, |
| .pNext = nullptr, |
| .colorSpace = static_cast<uint32_t>(fuchsia_sysmem::wire::ColorSpaceType::kSrgb), |
| }; |
| |
| std::vector<VkImageFormatConstraintsInfoFUCHSIA> formatInfos; |
| if (pImageInfo->format == VK_FORMAT_UNDEFINED) { |
| const auto kFormats = { |
| VK_FORMAT_B8G8R8A8_SRGB, |
| VK_FORMAT_R8G8B8A8_SRGB, |
| }; |
| for (auto format : kFormats) { |
| // shallow copy, using pNext from pImageInfo directly. |
| auto createInfo = *pImageInfo; |
| createInfo.format = format; |
| formatInfos.push_back(VkImageFormatConstraintsInfoFUCHSIA{ |
| .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_CONSTRAINTS_INFO_FUCHSIA, |
| .pNext = nullptr, |
| .imageCreateInfo = createInfo, |
| .colorSpaceCount = 1, |
| .pColorSpaces = &kDefaultColorSpace, |
| }); |
| } |
| } else { |
| formatInfos.push_back(VkImageFormatConstraintsInfoFUCHSIA{ |
| .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_CONSTRAINTS_INFO_FUCHSIA, |
| .pNext = nullptr, |
| .imageCreateInfo = *pImageInfo, |
| .colorSpaceCount = 1, |
| .pColorSpaces = &kDefaultColorSpace, |
| }); |
| } |
| |
| VkImageConstraintsInfoFUCHSIA imageConstraints = { |
| .sType = VK_STRUCTURE_TYPE_IMAGE_CONSTRAINTS_INFO_FUCHSIA, |
| .pNext = nullptr, |
| .formatConstraintsCount = static_cast<uint32_t>(formatInfos.size()), |
| .pFormatConstraints = formatInfos.data(), |
| .bufferCollectionConstraints = |
| VkBufferCollectionConstraintsInfoFUCHSIA{ |
| .sType = VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CONSTRAINTS_INFO_FUCHSIA, |
| .pNext = nullptr, |
| .minBufferCount = 1, |
| .maxBufferCount = 0, |
| .minBufferCountForCamping = 0, |
| .minBufferCountForDedicatedSlack = 0, |
| .minBufferCountForSharedSlack = 0, |
| }, |
| .flags = 0u, |
| }; |
| |
| return setBufferCollectionImageConstraintsFUCHSIA(enc, device, collection, &imageConstraints); |
| } |
| |
| VkResult addImageBufferCollectionConstraintsFUCHSIA( |
| VkEncoder* enc, VkDevice device, VkPhysicalDevice physicalDevice, |
| const VkImageFormatConstraintsInfoFUCHSIA* formatConstraints, // always non-zero |
| VkImageTiling tiling, fuchsia_sysmem::wire::BufferCollectionConstraints* constraints) { |
| // First check if the format, tiling and usage is supported on host. |
| VkImageFormatProperties imageFormatProperties; |
| auto createInfo = &formatConstraints->imageCreateInfo; |
| auto result = enc->vkGetPhysicalDeviceImageFormatProperties( |
| physicalDevice, createInfo->format, createInfo->imageType, tiling, createInfo->usage, |
| createInfo->flags, &imageFormatProperties, true /* do lock */); |
| if (result != VK_SUCCESS) { |
| mesa_logd( |
| "%s: Image format (%u) type (%u) tiling (%u) " |
| "usage (%u) flags (%u) not supported by physical " |
| "device", |
| __func__, static_cast<uint32_t>(createInfo->format), |
| static_cast<uint32_t>(createInfo->imageType), static_cast<uint32_t>(tiling), |
| static_cast<uint32_t>(createInfo->usage), static_cast<uint32_t>(createInfo->flags)); |
| return VK_ERROR_FORMAT_NOT_SUPPORTED; |
| } |
| |
| // Check if format constraints contains unsupported format features. |
| { |
| VkFormatProperties formatProperties; |
| enc->vkGetPhysicalDeviceFormatProperties(physicalDevice, createInfo->format, |
| &formatProperties, true /* do lock */); |
| |
| auto supportedFeatures = (tiling == VK_IMAGE_TILING_LINEAR) |
| ? formatProperties.linearTilingFeatures |
| : formatProperties.optimalTilingFeatures; |
| auto requiredFeatures = formatConstraints->requiredFormatFeatures; |
| if ((~supportedFeatures) & requiredFeatures) { |
| mesa_logd( |
| "%s: Host device support features for %s tiling: %08x, " |
| "required features: %08x, feature bits %08x missing", |
| __func__, tiling == VK_IMAGE_TILING_LINEAR ? "LINEAR" : "OPTIMAL", |
| static_cast<uint32_t>(requiredFeatures), static_cast<uint32_t>(supportedFeatures), |
| static_cast<uint32_t>((~supportedFeatures) & requiredFeatures)); |
| return VK_ERROR_FORMAT_NOT_SUPPORTED; |
| } |
| } |
| |
| fuchsia_sysmem::wire::ImageFormatConstraints imageConstraints; |
| if (formatConstraints->sysmemPixelFormat != 0) { |
| auto pixelFormat = static_cast<fuchsia_sysmem::wire::PixelFormatType>( |
| formatConstraints->sysmemPixelFormat); |
| if (createInfo->format != VK_FORMAT_UNDEFINED && |
| !vkFormatMatchesSysmemFormat(createInfo->format, pixelFormat)) { |
| mesa_logd("%s: VkFormat %u doesn't match sysmem pixelFormat %lu", __func__, |
| static_cast<uint32_t>(createInfo->format), formatConstraints->sysmemPixelFormat); |
| return VK_ERROR_FORMAT_NOT_SUPPORTED; |
| } |
| imageConstraints.pixel_format.type = pixelFormat; |
| } else { |
| auto pixel_format = vkFormatTypeToSysmem(createInfo->format); |
| if (pixel_format == fuchsia_sysmem::wire::PixelFormatType::kInvalid) { |
| mesa_logd("%s: Unsupported VkFormat %u", __func__, |
| static_cast<uint32_t>(createInfo->format)); |
| return VK_ERROR_FORMAT_NOT_SUPPORTED; |
| } |
| imageConstraints.pixel_format.type = pixel_format; |
| } |
| |
| imageConstraints.color_spaces_count = formatConstraints->colorSpaceCount; |
| for (size_t i = 0; i < formatConstraints->colorSpaceCount; i++) { |
| imageConstraints.color_space[0].type = static_cast<fuchsia_sysmem::wire::ColorSpaceType>( |
| formatConstraints->pColorSpaces[i].colorSpace); |
| } |
| |
| // Get row alignment from host GPU. |
| VkDeviceSize offset = 0; |
| VkDeviceSize rowPitchAlignment = 1u; |
| |
| if (tiling == VK_IMAGE_TILING_LINEAR) { |
| VkImageCreateInfo createInfoDup = *createInfo; |
| createInfoDup.pNext = nullptr; |
| enc->vkGetLinearImageLayout2GOOGLE(device, &createInfoDup, &offset, &rowPitchAlignment, |
| true /* do lock */); |
| mesa_logd( |
| "vkGetLinearImageLayout2GOOGLE: format %d offset %lu " |
| "rowPitchAlignment = %lu", |
| (int)createInfo->format, offset, rowPitchAlignment); |
| } |
| |
| imageConstraints.min_coded_width = createInfo->extent.width; |
| imageConstraints.max_coded_width = 0xfffffff; |
| imageConstraints.min_coded_height = createInfo->extent.height; |
| imageConstraints.max_coded_height = 0xffffffff; |
| // The min_bytes_per_row can be calculated by sysmem using |
| // |min_coded_width|, |bytes_per_row_divisor| and color format. |
| imageConstraints.min_bytes_per_row = 0; |
| imageConstraints.max_bytes_per_row = 0xffffffff; |
| imageConstraints.max_coded_width_times_coded_height = 0xffffffff; |
| |
| imageConstraints.layers = 1; |
| imageConstraints.coded_width_divisor = 1; |
| imageConstraints.coded_height_divisor = 1; |
| imageConstraints.bytes_per_row_divisor = rowPitchAlignment; |
| imageConstraints.start_offset_divisor = 1; |
| imageConstraints.display_width_divisor = 1; |
| imageConstraints.display_height_divisor = 1; |
| imageConstraints.pixel_format.has_format_modifier = true; |
| imageConstraints.pixel_format.format_modifier.value = |
| (tiling == VK_IMAGE_TILING_LINEAR) |
| ? fuchsia_sysmem::wire::kFormatModifierLinear |
| : fuchsia_sysmem::wire::kFormatModifierGoogleGoldfishOptimal; |
| |
| constraints->image_format_constraints[constraints->image_format_constraints_count++] = |
| imageConstraints; |
| return VK_SUCCESS; |
| } |
| |
| SetBufferCollectionBufferConstraintsResult setBufferCollectionBufferConstraintsImpl( |
| fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>* pCollection, |
| const VkBufferConstraintsInfoFUCHSIA* pBufferConstraintsInfo) { |
| const auto& collection = *pCollection; |
| if (pBufferConstraintsInfo == nullptr) { |
| mesa_loge( |
| "setBufferCollectionBufferConstraints: " |
| "pBufferConstraintsInfo cannot be null."); |
| return {VK_ERROR_OUT_OF_DEVICE_MEMORY}; |
| } |
| |
| fuchsia_sysmem::wire::BufferCollectionConstraints constraints = |
| defaultBufferCollectionConstraints( |
| /* min_size_bytes */ pBufferConstraintsInfo->createInfo.size, |
| /* buffer_count */ pBufferConstraintsInfo->bufferCollectionConstraints.minBufferCount); |
| constraints.usage.vulkan = |
| getBufferCollectionConstraintsVulkanBufferUsage(pBufferConstraintsInfo); |
| |
| constexpr uint32_t kVulkanPriority = 5; |
| const char kName[] = "GoldfishBufferSysmemShared"; |
| collection->SetName(kVulkanPriority, fidl::StringView(kName)); |
| |
| auto result = collection->SetConstraints(true, constraints); |
| if (!result.ok()) { |
| mesa_loge("setBufferCollectionConstraints: SetConstraints failed: %d", result.status()); |
| return {VK_ERROR_OUT_OF_DEVICE_MEMORY}; |
| } |
| |
| return {VK_SUCCESS, constraints}; |
| } |
| #endif |
| |
| uint64_t getAHardwareBufferId(AHardwareBuffer* ahw) { |
| uint64_t id = 0; |
| #if defined(ANDROID) |
| auto* gralloc = ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->grallocHelper(); |
| gralloc->getId(ahw, &id); |
| #else |
| (void)ahw; |
| #endif |
| return id; |
| } |
| |
| void transformExternalResourceMemoryDedicatedRequirementsForGuest( |
| VkMemoryDedicatedRequirements* dedicatedReqs) { |
| dedicatedReqs->prefersDedicatedAllocation = VK_TRUE; |
| dedicatedReqs->requiresDedicatedAllocation = VK_TRUE; |
| } |
| |
| void ResourceTracker::transformImageMemoryRequirementsForGuestLocked(VkImage image, |
| VkMemoryRequirements* reqs) { |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| auto it = info_VkImage.find(image); |
| if (it == info_VkImage.end()) return; |
| auto& info = it->second; |
| if (info.isSysmemBackedMemory) { |
| auto width = info.createInfo.extent.width; |
| auto height = info.createInfo.extent.height; |
| reqs->size = width * height * 4; |
| } |
| #else |
| // Bypass "unused parameter" checks. |
| (void)image; |
| (void)reqs; |
| #endif |
| } |
| |
| CoherentMemoryPtr ResourceTracker::freeCoherentMemoryLocked(VkDeviceMemory memory, |
| VkDeviceMemory_Info& info) { |
| if (info.coherentMemory && info.ptr) { |
| if (info.coherentMemory->getDeviceMemory() != memory) { |
| delete_goldfish_VkDeviceMemory(memory); |
| } |
| |
| if (info.ptr) { |
| info.coherentMemory->release(info.ptr); |
| info.ptr = nullptr; |
| } |
| |
| return std::move(info.coherentMemory); |
| } |
| |
| return nullptr; |
| } |
| |
| VkResult createFence(VkDevice device, uint64_t hostFenceHandle, int64_t& osHandle) { |
| struct VirtGpuExecBuffer exec = {}; |
| struct gfxstreamCreateExportSyncVK exportSync = {}; |
| VirtGpuDevice* instance = VirtGpuDevice::getInstance(); |
| |
| uint64_t hostDeviceHandle = get_host_u64_VkDevice(device); |
| |
| exportSync.hdr.opCode = GFXSTREAM_CREATE_EXPORT_SYNC_VK; |
| exportSync.deviceHandleLo = (uint32_t)hostDeviceHandle; |
| exportSync.deviceHandleHi = (uint32_t)(hostDeviceHandle >> 32); |
| exportSync.fenceHandleLo = (uint32_t)hostFenceHandle; |
| exportSync.fenceHandleHi = (uint32_t)(hostFenceHandle >> 32); |
| |
| exec.command = static_cast<void*>(&exportSync); |
| exec.command_size = sizeof(exportSync); |
| exec.flags = kFenceOut | kRingIdx; |
| if (instance->execBuffer(exec, nullptr)) return VK_ERROR_OUT_OF_HOST_MEMORY; |
| |
| osHandle = exec.handle.osHandle; |
| return VK_SUCCESS; |
| } |
| |
| void collectAllPendingDescriptorSetsBottomUp(const std::vector<VkCommandBuffer>& workingSet, |
| std::unordered_set<VkDescriptorSet>& allDs) { |
| if (workingSet.empty()) return; |
| |
| std::vector<VkCommandBuffer> nextLevel; |
| for (auto commandBuffer : workingSet) { |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(commandBuffer); |
| forAllObjects(cb->subObjects, [&nextLevel](void* secondary) { |
| nextLevel.push_back((VkCommandBuffer)secondary); |
| }); |
| } |
| |
| collectAllPendingDescriptorSetsBottomUp(nextLevel, allDs); |
| |
| for (auto cmdbuf : workingSet) { |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(cmdbuf); |
| |
| if (!cb->userPtr) { |
| continue; // No descriptors to update. |
| } |
| |
| CommandBufferPendingDescriptorSets* pendingDescriptorSets = |
| (CommandBufferPendingDescriptorSets*)(cb->userPtr); |
| |
| if (pendingDescriptorSets->sets.empty()) { |
| continue; // No descriptors to update. |
| } |
| |
| allDs.insert(pendingDescriptorSets->sets.begin(), pendingDescriptorSets->sets.end()); |
| } |
| } |
| |
| void commitDescriptorSetUpdates(void* context, VkQueue queue, |
| const std::unordered_set<VkDescriptorSet>& sets) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| std::unordered_map<VkDescriptorPool, uint32_t> poolSet; |
| std::vector<VkDescriptorPool> pools; |
| std::vector<VkDescriptorSetLayout> setLayouts; |
| std::vector<uint64_t> poolIds; |
| std::vector<uint32_t> descriptorSetWhichPool; |
| std::vector<uint32_t> pendingAllocations; |
| std::vector<uint32_t> writeStartingIndices; |
| std::vector<VkWriteDescriptorSet> writesForHost; |
| |
| uint32_t poolIndex = 0; |
| uint32_t currentWriteIndex = 0; |
| for (auto set : sets) { |
| ReifiedDescriptorSet* reified = as_goldfish_VkDescriptorSet(set)->reified; |
| VkDescriptorPool pool = reified->pool; |
| VkDescriptorSetLayout setLayout = reified->setLayout; |
| |
| auto it = poolSet.find(pool); |
| if (it == poolSet.end()) { |
| poolSet[pool] = poolIndex; |
| descriptorSetWhichPool.push_back(poolIndex); |
| pools.push_back(pool); |
| ++poolIndex; |
| } else { |
| uint32_t savedPoolIndex = it->second; |
| descriptorSetWhichPool.push_back(savedPoolIndex); |
| } |
| |
| poolIds.push_back(reified->poolId); |
| setLayouts.push_back(setLayout); |
| pendingAllocations.push_back(reified->allocationPending ? 1 : 0); |
| writeStartingIndices.push_back(currentWriteIndex); |
| |
| auto& writes = reified->allWrites; |
| |
| for (size_t i = 0; i < writes.size(); ++i) { |
| uint32_t binding = i; |
| |
| for (size_t j = 0; j < writes[i].size(); ++j) { |
| auto& write = writes[i][j]; |
| |
| if (write.type == DescriptorWriteType::Empty) continue; |
| |
| uint32_t dstArrayElement = 0; |
| |
| VkDescriptorImageInfo* imageInfo = nullptr; |
| VkDescriptorBufferInfo* bufferInfo = nullptr; |
| VkBufferView* bufferView = nullptr; |
| |
| switch (write.type) { |
| case DescriptorWriteType::Empty: |
| break; |
| case DescriptorWriteType::ImageInfo: |
| dstArrayElement = j; |
| imageInfo = &write.imageInfo; |
| break; |
| case DescriptorWriteType::BufferInfo: |
| dstArrayElement = j; |
| bufferInfo = &write.bufferInfo; |
| break; |
| case DescriptorWriteType::BufferView: |
| dstArrayElement = j; |
| bufferView = &write.bufferView; |
| break; |
| case DescriptorWriteType::InlineUniformBlock: |
| case DescriptorWriteType::AccelerationStructure: |
| // TODO |
| mesa_loge( |
| "Encountered pending inline uniform block or acceleration structure " |
| "desc write, abort (NYI)\n"); |
| abort(); |
| default: |
| break; |
| } |
| |
| // TODO: Combine multiple writes into one VkWriteDescriptorSet. |
| VkWriteDescriptorSet forHost = { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, |
| 0 /* TODO: inline uniform block */, |
| set, |
| binding, |
| dstArrayElement, |
| 1, |
| write.descriptorType, |
| imageInfo, |
| bufferInfo, |
| bufferView, |
| }; |
| |
| writesForHost.push_back(forHost); |
| ++currentWriteIndex; |
| |
| // Set it back to empty. |
| write.type = DescriptorWriteType::Empty; |
| } |
| } |
| } |
| |
| // Skip out if there's nothing to VkWriteDescriptorSet home about. |
| if (writesForHost.empty()) { |
| return; |
| } |
| |
| enc->vkQueueCommitDescriptorSetUpdatesGOOGLE( |
| queue, (uint32_t)pools.size(), pools.data(), (uint32_t)sets.size(), setLayouts.data(), |
| poolIds.data(), descriptorSetWhichPool.data(), pendingAllocations.data(), |
| writeStartingIndices.data(), (uint32_t)writesForHost.size(), writesForHost.data(), |
| false /* no lock */); |
| |
| // If we got here, then we definitely serviced the allocations. |
| for (auto set : sets) { |
| ReifiedDescriptorSet* reified = as_goldfish_VkDescriptorSet(set)->reified; |
| reified->allocationPending = false; |
| } |
| } |
| |
| uint32_t ResourceTracker::syncEncodersForCommandBuffer(VkCommandBuffer commandBuffer, |
| VkEncoder* currentEncoder) { |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(commandBuffer); |
| if (!cb) return 0; |
| |
| auto lastEncoder = cb->lastUsedEncoder; |
| |
| if (lastEncoder == currentEncoder) return 0; |
| |
| currentEncoder->incRef(); |
| |
| cb->lastUsedEncoder = currentEncoder; |
| |
| if (!lastEncoder) return 0; |
| |
| auto oldSeq = cb->sequenceNumber; |
| cb->sequenceNumber += 2; |
| lastEncoder->vkCommandBufferHostSyncGOOGLE(commandBuffer, false, oldSeq + 1, |
| true /* do lock */); |
| lastEncoder->flush(); |
| currentEncoder->vkCommandBufferHostSyncGOOGLE(commandBuffer, true, oldSeq + 2, |
| true /* do lock */); |
| |
| if (lastEncoder->decRef()) { |
| cb->lastUsedEncoder = nullptr; |
| } |
| return 0; |
| } |
| |
| void addPendingDescriptorSets(VkCommandBuffer commandBuffer, uint32_t descriptorSetCount, |
| const VkDescriptorSet* pDescriptorSets) { |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(commandBuffer); |
| |
| if (!cb->userPtr) { |
| CommandBufferPendingDescriptorSets* newPendingSets = new CommandBufferPendingDescriptorSets; |
| cb->userPtr = newPendingSets; |
| } |
| |
| CommandBufferPendingDescriptorSets* pendingSets = |
| (CommandBufferPendingDescriptorSets*)cb->userPtr; |
| |
| for (uint32_t i = 0; i < descriptorSetCount; ++i) { |
| pendingSets->sets.insert(pDescriptorSets[i]); |
| } |
| } |
| |
| void decDescriptorSetLayoutRef(void* context, VkDevice device, |
| VkDescriptorSetLayout descriptorSetLayout, |
| const VkAllocationCallbacks* pAllocator) { |
| if (!descriptorSetLayout) return; |
| |
| struct goldfish_VkDescriptorSetLayout* setLayout = |
| as_goldfish_VkDescriptorSetLayout(descriptorSetLayout); |
| |
| if (0 == --setLayout->layoutInfo->refcount) { |
| VkEncoder* enc = (VkEncoder*)context; |
| enc->vkDestroyDescriptorSetLayout(device, descriptorSetLayout, pAllocator, |
| true /* do lock */); |
| } |
| } |
| |
| void ResourceTracker::ensureSyncDeviceFd() { |
| #if GFXSTREAM_ENABLE_GUEST_GOLDFISH |
| if (mSyncDeviceFd >= 0) return; |
| mSyncDeviceFd = goldfish_sync_open(); |
| if (mSyncDeviceFd >= 0) { |
| mesa_logd("%s: created sync device for current Vulkan process: %d\n", __func__, mSyncDeviceFd); |
| } else { |
| mesa_logd("%s: failed to create sync device for current Vulkan process\n", __func__); |
| } |
| #endif |
| } |
| |
| void ResourceTracker::unregister_VkInstance(VkInstance instance) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkInstance.find(instance); |
| if (it == info_VkInstance.end()) return; |
| auto info = it->second; |
| info_VkInstance.erase(instance); |
| lock.unlock(); |
| } |
| |
| void ResourceTracker::unregister_VkDevice(VkDevice device) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkDevice.find(device); |
| if (it == info_VkDevice.end()) return; |
| auto info = it->second; |
| info_VkDevice.erase(device); |
| lock.unlock(); |
| } |
| |
| void ResourceTracker::unregister_VkCommandPool(VkCommandPool pool) { |
| if (!pool) return; |
| |
| clearCommandPool(pool); |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| info_VkCommandPool.erase(pool); |
| } |
| |
| void ResourceTracker::unregister_VkSampler(VkSampler sampler) { |
| if (!sampler) return; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| info_VkSampler.erase(sampler); |
| } |
| |
| void ResourceTracker::unregister_VkCommandBuffer(VkCommandBuffer commandBuffer) { |
| resetCommandBufferStagingInfo(commandBuffer, true /* also reset primaries */, |
| true /* also clear pending descriptor sets */); |
| |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(commandBuffer); |
| if (!cb) return; |
| if (cb->lastUsedEncoder) { |
| cb->lastUsedEncoder->decRef(); |
| } |
| eraseObjects(&cb->subObjects); |
| forAllObjects(cb->poolObjects, [cb](void* commandPool) { |
| struct goldfish_VkCommandPool* p = as_goldfish_VkCommandPool((VkCommandPool)commandPool); |
| eraseObject(&p->subObjects, (void*)cb); |
| }); |
| eraseObjects(&cb->poolObjects); |
| |
| if (cb->userPtr) { |
| CommandBufferPendingDescriptorSets* pendingSets = |
| (CommandBufferPendingDescriptorSets*)cb->userPtr; |
| delete pendingSets; |
| } |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| info_VkCommandBuffer.erase(commandBuffer); |
| } |
| |
| void ResourceTracker::unregister_VkQueue(VkQueue queue) { |
| struct goldfish_VkQueue* q = as_goldfish_VkQueue(queue); |
| if (!q) return; |
| if (q->lastUsedEncoder) { |
| q->lastUsedEncoder->decRef(); |
| } |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| info_VkQueue.erase(queue); |
| } |
| |
| void ResourceTracker::unregister_VkDeviceMemory(VkDeviceMemory mem) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkDeviceMemory.find(mem); |
| if (it == info_VkDeviceMemory.end()) return; |
| |
| auto& memInfo = it->second; |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| if (memInfo.ahw) { |
| auto* gralloc = |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->grallocHelper(); |
| gralloc->release(memInfo.ahw); |
| } |
| #endif |
| |
| if (memInfo.vmoHandle != ZX_HANDLE_INVALID) { |
| zx_handle_close(memInfo.vmoHandle); |
| } |
| |
| info_VkDeviceMemory.erase(mem); |
| } |
| |
| void ResourceTracker::unregister_VkImage(VkImage img) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkImage.find(img); |
| if (it == info_VkImage.end()) return; |
| |
| auto& imageInfo = it->second; |
| |
| info_VkImage.erase(img); |
| } |
| |
| void ResourceTracker::unregister_VkBuffer(VkBuffer buf) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkBuffer.find(buf); |
| if (it == info_VkBuffer.end()) return; |
| |
| info_VkBuffer.erase(buf); |
| } |
| |
| void ResourceTracker::unregister_VkSemaphore(VkSemaphore sem) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkSemaphore.find(sem); |
| if (it == info_VkSemaphore.end()) return; |
| |
| auto& semInfo = it->second; |
| |
| if (semInfo.eventHandle != ZX_HANDLE_INVALID) { |
| zx_handle_close(semInfo.eventHandle); |
| } |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| if (semInfo.syncFd.value_or(-1) >= 0) { |
| auto* syncHelper = |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->syncHelper(); |
| syncHelper->close(semInfo.syncFd.value()); |
| } |
| #endif |
| |
| info_VkSemaphore.erase(sem); |
| } |
| |
| void ResourceTracker::unregister_VkDescriptorUpdateTemplate(VkDescriptorUpdateTemplate templ) { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto it = info_VkDescriptorUpdateTemplate.find(templ); |
| if (it == info_VkDescriptorUpdateTemplate.end()) return; |
| |
| auto& info = it->second; |
| if (info.templateEntryCount) delete[] info.templateEntries; |
| if (info.imageInfoCount) { |
| delete[] info.imageInfoIndices; |
| delete[] info.imageInfos; |
| } |
| if (info.bufferInfoCount) { |
| delete[] info.bufferInfoIndices; |
| delete[] info.bufferInfos; |
| } |
| if (info.bufferViewCount) { |
| delete[] info.bufferViewIndices; |
| delete[] info.bufferViews; |
| } |
| info_VkDescriptorUpdateTemplate.erase(it); |
| } |
| |
| void ResourceTracker::unregister_VkFence(VkFence fence) { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto it = info_VkFence.find(fence); |
| if (it == info_VkFence.end()) return; |
| |
| auto& fenceInfo = it->second; |
| (void)fenceInfo; |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| if (fenceInfo.syncFd >= 0) { |
| auto* syncHelper = |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->syncHelper(); |
| syncHelper->close(fenceInfo.syncFd); |
| } |
| #endif |
| |
| info_VkFence.erase(fence); |
| } |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| void ResourceTracker::unregister_VkBufferCollectionFUCHSIA(VkBufferCollectionFUCHSIA collection) { |
| AutoLock<RecursiveLock> lock(mLock); |
| info_VkBufferCollectionFUCHSIA.erase(collection); |
| } |
| #endif |
| |
| void ResourceTracker::unregister_VkDescriptorSet_locked(VkDescriptorSet set) { |
| struct goldfish_VkDescriptorSet* ds = as_goldfish_VkDescriptorSet(set); |
| delete ds->reified; |
| info_VkDescriptorSet.erase(set); |
| } |
| |
| void ResourceTracker::unregister_VkDescriptorSet(VkDescriptorSet set) { |
| if (!set) return; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| unregister_VkDescriptorSet_locked(set); |
| } |
| |
| void ResourceTracker::unregister_VkDescriptorSetLayout(VkDescriptorSetLayout setLayout) { |
| if (!setLayout) return; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| delete as_goldfish_VkDescriptorSetLayout(setLayout)->layoutInfo; |
| info_VkDescriptorSetLayout.erase(setLayout); |
| } |
| |
| void ResourceTracker::freeDescriptorSetsIfHostAllocated(VkEncoder* enc, VkDevice device, |
| uint32_t descriptorSetCount, |
| const VkDescriptorSet* sets) { |
| for (uint32_t i = 0; i < descriptorSetCount; ++i) { |
| struct goldfish_VkDescriptorSet* ds = as_goldfish_VkDescriptorSet(sets[i]); |
| if (ds->reified->allocationPending) { |
| unregister_VkDescriptorSet(sets[i]); |
| delete_goldfish_VkDescriptorSet(sets[i]); |
| } else { |
| enc->vkFreeDescriptorSets(device, ds->reified->pool, 1, &sets[i], false /* no lock */); |
| } |
| } |
| } |
| |
| void ResourceTracker::clearDescriptorPoolAndUnregisterDescriptorSets(void* context, VkDevice device, |
| VkDescriptorPool pool) { |
| std::vector<VkDescriptorSet> toClear = |
| clearDescriptorPool(pool, mFeatureInfo->hasVulkanBatchedDescriptorSetUpdate); |
| |
| for (auto set : toClear) { |
| if (mFeatureInfo->hasVulkanBatchedDescriptorSetUpdate) { |
| VkDescriptorSetLayout setLayout = as_goldfish_VkDescriptorSet(set)->reified->setLayout; |
| decDescriptorSetLayoutRef(context, device, setLayout, nullptr); |
| } |
| unregister_VkDescriptorSet(set); |
| delete_goldfish_VkDescriptorSet(set); |
| } |
| } |
| |
| void ResourceTracker::unregister_VkDescriptorPool(VkDescriptorPool pool) { |
| if (!pool) return; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| struct goldfish_VkDescriptorPool* dp = as_goldfish_VkDescriptorPool(pool); |
| delete dp->allocInfo; |
| |
| info_VkDescriptorPool.erase(pool); |
| } |
| |
| void ResourceTracker::deviceMemoryTransform_fromhost(VkDeviceMemory* memory, uint32_t memoryCount, |
| VkDeviceSize* offset, uint32_t offsetCount, |
| VkDeviceSize* size, uint32_t sizeCount, |
| uint32_t* typeIndex, uint32_t typeIndexCount, |
| uint32_t* typeBits, uint32_t typeBitsCount) { |
| (void)memory; |
| (void)memoryCount; |
| (void)offset; |
| (void)offsetCount; |
| (void)size; |
| (void)sizeCount; |
| (void)typeIndex; |
| (void)typeIndexCount; |
| (void)typeBits; |
| (void)typeBitsCount; |
| } |
| |
| void ResourceTracker::transformImpl_VkExternalMemoryProperties_fromhost( |
| VkExternalMemoryProperties* pProperties, uint32_t) { |
| VkExternalMemoryHandleTypeFlags supportedHandleType = 0u; |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| supportedHandleType |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA; |
| #endif // VK_USE_PLATFORM_FUCHSIA |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| supportedHandleType |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT | |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID; |
| #endif // VK_USE_PLATFORM_ANDROID_KHR |
| if (supportedHandleType) { |
| pProperties->compatibleHandleTypes &= supportedHandleType; |
| pProperties->exportFromImportedHandleTypes &= supportedHandleType; |
| } |
| } |
| |
| void ResourceTracker::setInstanceInfo(VkInstance instance, uint32_t enabledExtensionCount, |
| const char* const* ppEnabledExtensionNames, |
| uint32_t apiVersion) { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto& info = info_VkInstance[instance]; |
| info.highestApiVersion = apiVersion; |
| |
| if (!ppEnabledExtensionNames) return; |
| |
| for (uint32_t i = 0; i < enabledExtensionCount; ++i) { |
| info.enabledExtensions.insert(ppEnabledExtensionNames[i]); |
| } |
| } |
| |
| void ResourceTracker::setDeviceInfo(VkDevice device, VkPhysicalDevice physdev, |
| VkPhysicalDeviceProperties props, |
| VkPhysicalDeviceMemoryProperties memProps, |
| uint32_t enabledExtensionCount, |
| const char* const* ppEnabledExtensionNames, const void* pNext) { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto& info = info_VkDevice[device]; |
| info.physdev = physdev; |
| info.props = props; |
| info.memProps = memProps; |
| info.apiVersion = props.apiVersion; |
| |
| const VkBaseInStructure* extensionCreateInfo = |
| reinterpret_cast<const VkBaseInStructure*>(pNext); |
| while (extensionCreateInfo) { |
| if (extensionCreateInfo->sType == |
| VK_STRUCTURE_TYPE_DEVICE_DEVICE_MEMORY_REPORT_CREATE_INFO_EXT) { |
| auto deviceMemoryReportCreateInfo = |
| reinterpret_cast<const VkDeviceDeviceMemoryReportCreateInfoEXT*>( |
| extensionCreateInfo); |
| if (deviceMemoryReportCreateInfo->pfnUserCallback != nullptr) { |
| info.deviceMemoryReportCallbacks.emplace_back( |
| deviceMemoryReportCreateInfo->pfnUserCallback, |
| deviceMemoryReportCreateInfo->pUserData); |
| } |
| } |
| extensionCreateInfo = extensionCreateInfo->pNext; |
| } |
| |
| if (!ppEnabledExtensionNames) return; |
| |
| for (uint32_t i = 0; i < enabledExtensionCount; ++i) { |
| info.enabledExtensions.insert(ppEnabledExtensionNames[i]); |
| } |
| } |
| |
| void ResourceTracker::setDeviceMemoryInfo(VkDevice device, VkDeviceMemory memory, |
| VkDeviceSize allocationSize, uint8_t* ptr, |
| uint32_t memoryTypeIndex, AHardwareBuffer* ahw, |
| bool imported, zx_handle_t vmoHandle, |
| VirtGpuResourcePtr blobPtr) { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto& info = info_VkDeviceMemory[memory]; |
| |
| info.device = device; |
| info.allocationSize = allocationSize; |
| info.ptr = ptr; |
| info.memoryTypeIndex = memoryTypeIndex; |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| info.ahw = ahw; |
| #endif |
| info.imported = imported; |
| info.vmoHandle = vmoHandle; |
| info.blobPtr = blobPtr; |
| } |
| |
| void ResourceTracker::setImageInfo(VkImage image, VkDevice device, |
| const VkImageCreateInfo* pCreateInfo) { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto& info = info_VkImage[image]; |
| |
| info.device = device; |
| info.createInfo = *pCreateInfo; |
| } |
| |
| uint8_t* ResourceTracker::getMappedPointer(VkDeviceMemory memory) { |
| AutoLock<RecursiveLock> lock(mLock); |
| const auto it = info_VkDeviceMemory.find(memory); |
| if (it == info_VkDeviceMemory.end()) return nullptr; |
| |
| const auto& info = it->second; |
| return info.ptr; |
| } |
| |
| VkDeviceSize ResourceTracker::getMappedSize(VkDeviceMemory memory) { |
| AutoLock<RecursiveLock> lock(mLock); |
| const auto it = info_VkDeviceMemory.find(memory); |
| if (it == info_VkDeviceMemory.end()) return 0; |
| |
| const auto& info = it->second; |
| return info.allocationSize; |
| } |
| |
| bool ResourceTracker::isValidMemoryRange(const VkMappedMemoryRange& range) const { |
| AutoLock<RecursiveLock> lock(mLock); |
| const auto it = info_VkDeviceMemory.find(range.memory); |
| if (it == info_VkDeviceMemory.end()) return false; |
| const auto& info = it->second; |
| |
| if (!info.ptr) return false; |
| |
| VkDeviceSize offset = range.offset; |
| VkDeviceSize size = range.size; |
| |
| if (size == VK_WHOLE_SIZE) { |
| return offset <= info.allocationSize; |
| } |
| |
| return offset + size <= info.allocationSize; |
| } |
| |
| void ResourceTracker::setupCaps(uint32_t& noRenderControlEnc) { |
| VirtGpuDevice* instance = VirtGpuDevice::getInstance(kCapsetGfxStreamVulkan); |
| mCaps = instance->getCaps(); |
| |
| // Delete once goldfish Linux drivers are gone |
| if (mCaps.vulkanCapset.protocolVersion == 0) { |
| mCaps.vulkanCapset.colorBufferMemoryIndex = 0xFFFFFFFF; |
| } else { |
| // Don't query the render control encoder for features, since for virtio-gpu the |
| // capabilities provide versioning. Set features to be unconditionally true, since |
| // using virtio-gpu encompasses all prior goldfish features. mFeatureInfo should be |
| // deprecated in favor of caps. |
| |
| mFeatureInfo.reset(new EmulatorFeatureInfo); |
| |
| mFeatureInfo->hasVulkanNullOptionalStrings = true; |
| mFeatureInfo->hasVulkanIgnoredHandles = true; |
| mFeatureInfo->hasVulkanShaderFloat16Int8 = true; |
| mFeatureInfo->hasVulkanQueueSubmitWithCommands = true; |
| mFeatureInfo->hasDeferredVulkanCommands = true; |
| mFeatureInfo->hasVulkanAsyncQueueSubmit = true; |
| mFeatureInfo->hasVulkanCreateResourcesWithRequirements = true; |
| mFeatureInfo->hasVirtioGpuNext = true; |
| mFeatureInfo->hasVirtioGpuNativeSync = true; |
| mFeatureInfo->hasVulkanBatchedDescriptorSetUpdate = true; |
| mFeatureInfo->hasVulkanAsyncQsri = true; |
| |
| ResourceTracker::streamFeatureBits |= VULKAN_STREAM_FEATURE_NULL_OPTIONAL_STRINGS_BIT; |
| ResourceTracker::streamFeatureBits |= VULKAN_STREAM_FEATURE_IGNORED_HANDLES_BIT; |
| ResourceTracker::streamFeatureBits |= VULKAN_STREAM_FEATURE_SHADER_FLOAT16_INT8_BIT; |
| ResourceTracker::streamFeatureBits |= VULKAN_STREAM_FEATURE_QUEUE_SUBMIT_WITH_COMMANDS_BIT; |
| } |
| |
| noRenderControlEnc = mCaps.vulkanCapset.noRenderControlEnc; |
| } |
| |
| void ResourceTracker::setupFeatures(const EmulatorFeatureInfo* features) { |
| if (!features || mFeatureInfo) return; |
| mFeatureInfo.reset(new EmulatorFeatureInfo); |
| *mFeatureInfo = *features; |
| |
| #if defined(__ANDROID__) |
| if (mFeatureInfo->hasDirectMem) { |
| mGoldfishAddressSpaceBlockProvider.reset( |
| new GoldfishAddressSpaceBlockProvider(GoldfishAddressSpaceSubdeviceType::NoSubdevice)); |
| } |
| #endif // defined(__ANDROID__) |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| if (mFeatureInfo->hasVulkan) { |
| fidl::ClientEnd<fuchsia_hardware_goldfish::ControlDevice> channel{zx::channel( |
| GetConnectToServiceFunction()("/loader-gpu-devices/class/goldfish-control/000"))}; |
| if (!channel) { |
| mesa_loge("failed to open control device"); |
| abort(); |
| } |
| mControlDevice = |
| fidl::WireSyncClient<fuchsia_hardware_goldfish::ControlDevice>(std::move(channel)); |
| |
| fidl::ClientEnd<fuchsia_sysmem::Allocator> sysmem_channel{ |
| zx::channel(GetConnectToServiceFunction()("/svc/fuchsia.sysmem.Allocator"))}; |
| if (!sysmem_channel) { |
| mesa_loge("failed to open sysmem connection"); |
| } |
| mSysmemAllocator = |
| fidl::WireSyncClient<fuchsia_sysmem::Allocator>(std::move(sysmem_channel)); |
| char name[ZX_MAX_NAME_LEN] = {}; |
| zx_object_get_property(zx_process_self(), ZX_PROP_NAME, name, sizeof(name)); |
| std::string client_name(name); |
| client_name += "-goldfish"; |
| zx_info_handle_basic_t info; |
| zx_object_get_info(zx_process_self(), ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, |
| nullptr); |
| mSysmemAllocator->SetDebugClientInfo(fidl::StringView::FromExternal(client_name), |
| info.koid); |
| } |
| #endif |
| |
| if (mFeatureInfo->hasVulkanNullOptionalStrings) { |
| ResourceTracker::streamFeatureBits |= VULKAN_STREAM_FEATURE_NULL_OPTIONAL_STRINGS_BIT; |
| } |
| if (mFeatureInfo->hasVulkanIgnoredHandles) { |
| ResourceTracker::streamFeatureBits |= VULKAN_STREAM_FEATURE_IGNORED_HANDLES_BIT; |
| } |
| if (mFeatureInfo->hasVulkanShaderFloat16Int8) { |
| ResourceTracker::streamFeatureBits |= VULKAN_STREAM_FEATURE_SHADER_FLOAT16_INT8_BIT; |
| } |
| if (mFeatureInfo->hasVulkanQueueSubmitWithCommands) { |
| ResourceTracker::streamFeatureBits |= VULKAN_STREAM_FEATURE_QUEUE_SUBMIT_WITH_COMMANDS_BIT; |
| } |
| } |
| |
| void ResourceTracker::setThreadingCallbacks(const ResourceTracker::ThreadingCallbacks& callbacks) { |
| ResourceTracker::threadingCallbacks = callbacks; |
| } |
| |
| bool ResourceTracker::hostSupportsVulkan() const { |
| if (!mFeatureInfo) return false; |
| |
| return mFeatureInfo->hasVulkan; |
| } |
| |
| bool ResourceTracker::usingDirectMapping() const { return true; } |
| |
| uint32_t ResourceTracker::getStreamFeatures() const { return ResourceTracker::streamFeatureBits; } |
| |
| bool ResourceTracker::supportsDeferredCommands() const { |
| if (!mFeatureInfo) return false; |
| return mFeatureInfo->hasDeferredVulkanCommands; |
| } |
| |
| bool ResourceTracker::supportsAsyncQueueSubmit() const { |
| if (!mFeatureInfo) return false; |
| return mFeatureInfo->hasVulkanAsyncQueueSubmit; |
| } |
| |
| bool ResourceTracker::supportsCreateResourcesWithRequirements() const { |
| if (!mFeatureInfo) return false; |
| return mFeatureInfo->hasVulkanCreateResourcesWithRequirements; |
| } |
| |
| int ResourceTracker::getHostInstanceExtensionIndex(const std::string& extName) const { |
| int i = 0; |
| for (const auto& prop : mHostInstanceExtensions) { |
| if (extName == std::string(prop.extensionName)) { |
| return i; |
| } |
| ++i; |
| } |
| return -1; |
| } |
| |
| int ResourceTracker::getHostDeviceExtensionIndex(const std::string& extName) const { |
| int i = 0; |
| for (const auto& prop : mHostDeviceExtensions) { |
| if (extName == std::string(prop.extensionName)) { |
| return i; |
| } |
| ++i; |
| } |
| return -1; |
| } |
| |
| void ResourceTracker::deviceMemoryTransform_tohost(VkDeviceMemory* memory, uint32_t memoryCount, |
| VkDeviceSize* offset, uint32_t offsetCount, |
| VkDeviceSize* size, uint32_t sizeCount, |
| uint32_t* typeIndex, uint32_t typeIndexCount, |
| uint32_t* typeBits, uint32_t typeBitsCount) { |
| (void)memoryCount; |
| (void)offsetCount; |
| (void)sizeCount; |
| (void)typeIndex; |
| (void)typeIndexCount; |
| (void)typeBits; |
| (void)typeBitsCount; |
| |
| if (memory) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| for (uint32_t i = 0; i < memoryCount; ++i) { |
| VkDeviceMemory mem = memory[i]; |
| |
| auto it = info_VkDeviceMemory.find(mem); |
| if (it == info_VkDeviceMemory.end()) return; |
| |
| const auto& info = it->second; |
| |
| if (!info.coherentMemory) continue; |
| |
| memory[i] = info.coherentMemory->getDeviceMemory(); |
| |
| if (offset) { |
| offset[i] = info.coherentMemoryOffset + offset[i]; |
| } |
| |
| if (size && size[i] == VK_WHOLE_SIZE) { |
| size[i] = info.allocationSize; |
| } |
| |
| // TODO |
| (void)memory; |
| (void)offset; |
| (void)size; |
| } |
| } |
| } |
| |
| uint32_t ResourceTracker::getColorBufferMemoryIndex(void* context, VkDevice device) { |
| // Create test image to get the memory requirements |
| VkEncoder* enc = (VkEncoder*)context; |
| VkImageCreateInfo createInfo = { |
| .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, |
| .imageType = VK_IMAGE_TYPE_2D, |
| .format = VK_FORMAT_R8G8B8A8_UNORM, |
| .extent = {64, 64, 1}, |
| .mipLevels = 1, |
| .arrayLayers = 1, |
| .samples = VK_SAMPLE_COUNT_1_BIT, |
| .tiling = VK_IMAGE_TILING_OPTIMAL, |
| .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | |
| VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | |
| VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, |
| .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, |
| }; |
| VkImage image = VK_NULL_HANDLE; |
| VkResult res = enc->vkCreateImage(device, &createInfo, nullptr, &image, true /* do lock */); |
| |
| if (res != VK_SUCCESS) { |
| return 0; |
| } |
| |
| VkMemoryRequirements memReqs; |
| enc->vkGetImageMemoryRequirements(device, image, &memReqs, true /* do lock */); |
| enc->vkDestroyImage(device, image, nullptr, true /* do lock */); |
| |
| const VkPhysicalDeviceMemoryProperties& memProps = |
| getPhysicalDeviceMemoryProperties(context, device, VK_NULL_HANDLE); |
| |
| // Currently, host looks for the last index that has with memory |
| // property type VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
| VkMemoryPropertyFlags memoryProperty = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
| for (int i = VK_MAX_MEMORY_TYPES - 1; i >= 0; --i) { |
| if ((memReqs.memoryTypeBits & (1u << i)) && |
| (memProps.memoryTypes[i].propertyFlags & memoryProperty)) { |
| return i; |
| } |
| } |
| |
| return 0; |
| } |
| |
| VkResult ResourceTracker::on_vkEnumerateInstanceExtensionProperties( |
| void* context, VkResult, const char*, uint32_t* pPropertyCount, |
| VkExtensionProperties* pProperties) { |
| std::vector<const char*> allowedExtensionNames = { |
| "VK_KHR_get_physical_device_properties2", |
| "VK_KHR_sampler_ycbcr_conversion", |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| "VK_KHR_external_semaphore_capabilities", |
| "VK_KHR_external_memory_capabilities", |
| "VK_KHR_external_fence_capabilities", |
| "VK_EXT_debug_utils", |
| #endif |
| }; |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| // Only advertise a select set of extensions. |
| if (mHostInstanceExtensions.empty()) { |
| uint32_t hostPropCount = 0; |
| enc->vkEnumerateInstanceExtensionProperties(nullptr, &hostPropCount, nullptr, |
| true /* do lock */); |
| mHostInstanceExtensions.resize(hostPropCount); |
| |
| VkResult hostRes = enc->vkEnumerateInstanceExtensionProperties( |
| nullptr, &hostPropCount, mHostInstanceExtensions.data(), true /* do lock */); |
| |
| if (hostRes != VK_SUCCESS) { |
| return hostRes; |
| } |
| } |
| |
| std::vector<VkExtensionProperties> filteredExts; |
| |
| for (size_t i = 0; i < allowedExtensionNames.size(); ++i) { |
| auto extIndex = getHostInstanceExtensionIndex(allowedExtensionNames[i]); |
| if (extIndex != -1) { |
| filteredExts.push_back(mHostInstanceExtensions[extIndex]); |
| } |
| } |
| |
| VkExtensionProperties anbExtProps[] = { |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| {"VK_KHR_external_memory_capabilities", 1}, |
| {"VK_KHR_external_semaphore_capabilities", 1}, |
| #endif |
| }; |
| |
| for (auto& anbExtProp : anbExtProps) { |
| filteredExts.push_back(anbExtProp); |
| } |
| |
| // Spec: |
| // |
| // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkEnumerateInstanceExtensionProperties.html |
| // |
| // If pProperties is NULL, then the number of extensions properties |
| // available is returned in pPropertyCount. Otherwise, pPropertyCount |
| // must point to a variable set by the user to the number of elements |
| // in the pProperties array, and on return the variable is overwritten |
| // with the number of structures actually written to pProperties. If |
| // pPropertyCount is less than the number of extension properties |
| // available, at most pPropertyCount structures will be written. If |
| // pPropertyCount is smaller than the number of extensions available, |
| // VK_INCOMPLETE will be returned instead of VK_SUCCESS, to indicate |
| // that not all the available properties were returned. |
| // |
| // pPropertyCount must be a valid pointer to a uint32_t value |
| if (!pPropertyCount) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| if (!pProperties) { |
| *pPropertyCount = (uint32_t)filteredExts.size(); |
| return VK_SUCCESS; |
| } else { |
| auto actualExtensionCount = (uint32_t)filteredExts.size(); |
| if (*pPropertyCount > actualExtensionCount) { |
| *pPropertyCount = actualExtensionCount; |
| } |
| |
| for (uint32_t i = 0; i < *pPropertyCount; ++i) { |
| pProperties[i] = filteredExts[i]; |
| } |
| |
| if (actualExtensionCount > *pPropertyCount) { |
| return VK_INCOMPLETE; |
| } |
| |
| return VK_SUCCESS; |
| } |
| } |
| |
| VkResult ResourceTracker::on_vkEnumerateDeviceExtensionProperties( |
| void* context, VkResult, VkPhysicalDevice physdev, const char*, uint32_t* pPropertyCount, |
| VkExtensionProperties* pProperties) { |
| std::vector<const char*> allowedExtensionNames = { |
| "VK_KHR_vulkan_memory_model", |
| "VK_KHR_buffer_device_address", |
| "VK_KHR_maintenance1", |
| "VK_KHR_maintenance2", |
| "VK_KHR_maintenance3", |
| "VK_KHR_bind_memory2", |
| "VK_KHR_dedicated_allocation", |
| "VK_KHR_get_memory_requirements2", |
| "VK_KHR_sampler_ycbcr_conversion", |
| "VK_KHR_shader_float16_int8", |
| // Timeline semaphores buggy in newer NVIDIA drivers |
| // (vkWaitSemaphoresKHR causes further vkCommandBuffer dispatches to deadlock) |
| #ifndef VK_USE_PLATFORM_ANDROID_KHR |
| "VK_KHR_timeline_semaphore", |
| #endif |
| "VK_AMD_gpu_shader_half_float", |
| "VK_NV_shader_subgroup_partitioned", |
| "VK_KHR_shader_subgroup_extended_types", |
| "VK_EXT_subgroup_size_control", |
| "VK_EXT_provoking_vertex", |
| "VK_EXT_line_rasterization", |
| "VK_KHR_shader_terminate_invocation", |
| "VK_EXT_transform_feedback", |
| "VK_EXT_primitive_topology_list_restart", |
| "VK_EXT_index_type_uint8", |
| "VK_EXT_load_store_op_none", |
| "VK_EXT_swapchain_colorspace", |
| "VK_EXT_image_robustness", |
| "VK_EXT_custom_border_color", |
| "VK_EXT_shader_stencil_export", |
| "VK_KHR_image_format_list", |
| "VK_KHR_incremental_present", |
| "VK_KHR_pipeline_executable_properties", |
| "VK_EXT_queue_family_foreign", |
| "VK_EXT_scalar_block_layout", |
| "VK_KHR_descriptor_update_template", |
| "VK_KHR_storage_buffer_storage_class", |
| "VK_EXT_depth_clip_enable", |
| "VK_KHR_create_renderpass2", |
| "VK_EXT_vertex_attribute_divisor", |
| "VK_EXT_host_query_reset", |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| "VK_KHR_external_semaphore", |
| "VK_KHR_external_semaphore_fd", |
| // "VK_KHR_external_semaphore_win32", not exposed because it's translated to fd |
| "VK_KHR_external_memory", |
| "VK_KHR_external_fence", |
| "VK_KHR_external_fence_fd", |
| "VK_EXT_device_memory_report", |
| #endif |
| #if defined(__linux__) && !defined(VK_USE_PLATFORM_ANDROID_KHR) |
| "VK_KHR_imageless_framebuffer", |
| #endif |
| // Vulkan 1.3 |
| "VK_KHR_synchronization2", |
| "VK_EXT_private_data", |
| "VK_EXT_color_write_enable", |
| }; |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| if (mHostDeviceExtensions.empty()) { |
| uint32_t hostPropCount = 0; |
| enc->vkEnumerateDeviceExtensionProperties(physdev, nullptr, &hostPropCount, nullptr, |
| true /* do lock */); |
| mHostDeviceExtensions.resize(hostPropCount); |
| |
| VkResult hostRes = enc->vkEnumerateDeviceExtensionProperties( |
| physdev, nullptr, &hostPropCount, mHostDeviceExtensions.data(), true /* do lock */); |
| |
| if (hostRes != VK_SUCCESS) { |
| return hostRes; |
| } |
| } |
| |
| std::vector<VkExtensionProperties> filteredExts; |
| |
| for (size_t i = 0; i < allowedExtensionNames.size(); ++i) { |
| auto extIndex = getHostDeviceExtensionIndex(allowedExtensionNames[i]); |
| if (extIndex != -1) { |
| filteredExts.push_back(mHostDeviceExtensions[extIndex]); |
| } |
| } |
| |
| VkExtensionProperties anbExtProps[] = { |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| {"VK_ANDROID_native_buffer", 7}, |
| #endif |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| {"VK_KHR_external_memory", 1}, |
| {"VK_KHR_external_semaphore", 1}, |
| {"VK_FUCHSIA_external_semaphore", 1}, |
| #endif |
| }; |
| |
| for (auto& anbExtProp : anbExtProps) { |
| filteredExts.push_back(anbExtProp); |
| } |
| |
| /* |
| * GfxstreamEnd2EndVkTest::DeviceMemoryReport always assumes the memory report |
| * extension is present. It's is filtered out when sent host side, since for a |
| * virtual GPU this is quite difficult to implement. |
| * |
| * Mesa runtime checks physical device features. So if the test tries to enable |
| * device level extension without it definitely existing, the test will fail. |
| * |
| * The test can also be modified to check VkPhysicalDeviceDeviceMemoryReportFeaturesEXT, |
| * but that's more involved. Work around this by always advertising the extension. |
| * Tracking bug: b/338270042 |
| */ |
| filteredExts.push_back(VkExtensionProperties{"VK_EXT_device_memory_report", 1}); |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| bool hostSupportsExternalFenceFd = |
| getHostDeviceExtensionIndex("VK_KHR_external_fence_fd") != -1; |
| if (!hostSupportsExternalFenceFd) { |
| filteredExts.push_back(VkExtensionProperties{"VK_KHR_external_fence_fd", 1}); |
| } |
| #endif |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| bool hostHasPosixExternalSemaphore = |
| getHostDeviceExtensionIndex("VK_KHR_external_semaphore_fd") != -1; |
| if (!hostHasPosixExternalSemaphore) { |
| // Always advertise posix external semaphore capabilities on Android/Linux. |
| // SYNC_FD handles will always work, regardless of host support. Support |
| // for non-sync, opaque FDs, depends on host driver support, but will |
| // be handled accordingly by host. |
| filteredExts.push_back(VkExtensionProperties{"VK_KHR_external_semaphore_fd", 1}); |
| } |
| #endif |
| |
| bool win32ExtMemAvailable = getHostDeviceExtensionIndex("VK_KHR_external_memory_win32") != -1; |
| bool posixExtMemAvailable = getHostDeviceExtensionIndex("VK_KHR_external_memory_fd") != -1; |
| //TODO(b/349066492): this should check external_memory_metal extension when it's ready |
| bool moltenVkExtAvailable = getHostDeviceExtensionIndex("VK_MVK_moltenvk") != -1; |
| bool qnxExtMemAvailable = |
| getHostDeviceExtensionIndex("VK_QNX_external_memory_screen_buffer") != -1; |
| |
| bool hostHasExternalMemorySupport = |
| win32ExtMemAvailable || posixExtMemAvailable || moltenVkExtAvailable || qnxExtMemAvailable; |
| |
| if (hostHasExternalMemorySupport) { |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| filteredExts.push_back( |
| VkExtensionProperties{"VK_ANDROID_external_memory_android_hardware_buffer", 7}); |
| filteredExts.push_back(VkExtensionProperties{"VK_EXT_queue_family_foreign", 1}); |
| #endif |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| filteredExts.push_back(VkExtensionProperties{"VK_FUCHSIA_external_memory", 1}); |
| filteredExts.push_back(VkExtensionProperties{"VK_FUCHSIA_buffer_collection", 1}); |
| #endif |
| #if !defined(VK_USE_PLATFORM_ANDROID_KHR) && defined(__linux__) |
| filteredExts.push_back(VkExtensionProperties{"VK_KHR_external_memory_fd", 1}); |
| filteredExts.push_back(VkExtensionProperties{"VK_EXT_external_memory_dma_buf", 1}); |
| #endif |
| } |
| |
| // NOTE: the Vulkan Loader's trampoline functions will remove duplicates. This can lead |
| // to lead errors if this function returns VK_SUCCESS with N elements (including a duplicate) |
| // but the Vulkan Loader's trampoline function returns VK_INCOMPLETE with N-1 elements |
| // (without the duplicate). |
| std::sort(filteredExts.begin(), |
| filteredExts.end(), |
| [](const VkExtensionProperties& a, |
| const VkExtensionProperties& b) { |
| return strcmp(a.extensionName, b.extensionName) < 0; |
| }); |
| filteredExts.erase(std::unique(filteredExts.begin(), |
| filteredExts.end(), |
| [](const VkExtensionProperties& a, |
| const VkExtensionProperties& b) { |
| return strcmp(a.extensionName, b.extensionName) == 0; |
| }), |
| filteredExts.end()); |
| |
| // Spec: |
| // |
| // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkEnumerateDeviceExtensionProperties.html |
| // |
| // pPropertyCount is a pointer to an integer related to the number of |
| // extension properties available or queried, and is treated in the |
| // same fashion as the |
| // vkEnumerateInstanceExtensionProperties::pPropertyCount parameter. |
| // |
| // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkEnumerateInstanceExtensionProperties.html |
| // |
| // If pProperties is NULL, then the number of extensions properties |
| // available is returned in pPropertyCount. Otherwise, pPropertyCount |
| // must point to a variable set by the user to the number of elements |
| // in the pProperties array, and on return the variable is overwritten |
| // with the number of structures actually written to pProperties. If |
| // pPropertyCount is less than the number of extension properties |
| // available, at most pPropertyCount structures will be written. If |
| // pPropertyCount is smaller than the number of extensions available, |
| // VK_INCOMPLETE will be returned instead of VK_SUCCESS, to indicate |
| // that not all the available properties were returned. |
| // |
| // pPropertyCount must be a valid pointer to a uint32_t value |
| |
| if (!pPropertyCount) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| if (!pProperties) { |
| *pPropertyCount = (uint32_t)filteredExts.size(); |
| return VK_SUCCESS; |
| } else { |
| auto actualExtensionCount = (uint32_t)filteredExts.size(); |
| if (*pPropertyCount > actualExtensionCount) { |
| *pPropertyCount = actualExtensionCount; |
| } |
| |
| for (uint32_t i = 0; i < *pPropertyCount; ++i) { |
| pProperties[i] = filteredExts[i]; |
| } |
| |
| if (actualExtensionCount > *pPropertyCount) { |
| return VK_INCOMPLETE; |
| } |
| |
| return VK_SUCCESS; |
| } |
| } |
| |
| VkResult ResourceTracker::on_vkEnumeratePhysicalDevices(void* context, VkResult, |
| VkInstance instance, |
| uint32_t* pPhysicalDeviceCount, |
| VkPhysicalDevice* pPhysicalDevices) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| if (!instance) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| if (!pPhysicalDeviceCount) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| // When this function is called, we actually need to do two things: |
| // - Get full information about physical devices from the host, |
| // even if the guest did not ask for it |
| // - Serve the guest query according to the spec: |
| // |
| // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkEnumeratePhysicalDevices.html |
| |
| auto it = info_VkInstance.find(instance); |
| |
| if (it == info_VkInstance.end()) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| auto& info = it->second; |
| |
| // Get the full host information here if it doesn't exist already. |
| if (info.physicalDevices.empty()) { |
| uint32_t hostPhysicalDeviceCount = 0; |
| |
| lock.unlock(); |
| VkResult countRes = enc->vkEnumeratePhysicalDevices(instance, &hostPhysicalDeviceCount, |
| nullptr, false /* no lock */); |
| lock.lock(); |
| |
| if (countRes != VK_SUCCESS) { |
| mesa_loge( |
| "%s: failed: could not count host physical devices. " |
| "Error %d\n", |
| __func__, countRes); |
| return countRes; |
| } |
| |
| info.physicalDevices.resize(hostPhysicalDeviceCount); |
| |
| lock.unlock(); |
| VkResult enumRes = enc->vkEnumeratePhysicalDevices( |
| instance, &hostPhysicalDeviceCount, info.physicalDevices.data(), false /* no lock */); |
| lock.lock(); |
| |
| if (enumRes != VK_SUCCESS) { |
| mesa_loge( |
| "%s: failed: could not retrieve host physical devices. " |
| "Error %d\n", |
| __func__, enumRes); |
| return enumRes; |
| } |
| } |
| |
| // Serve the guest query according to the spec. |
| // |
| // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkEnumeratePhysicalDevices.html |
| // |
| // If pPhysicalDevices is NULL, then the number of physical devices |
| // available is returned in pPhysicalDeviceCount. Otherwise, |
| // pPhysicalDeviceCount must point to a variable set by the user to the |
| // number of elements in the pPhysicalDevices array, and on return the |
| // variable is overwritten with the number of handles actually written |
| // to pPhysicalDevices. If pPhysicalDeviceCount is less than the number |
| // of physical devices available, at most pPhysicalDeviceCount |
| // structures will be written. If pPhysicalDeviceCount is smaller than |
| // the number of physical devices available, VK_INCOMPLETE will be |
| // returned instead of VK_SUCCESS, to indicate that not all the |
| // available physical devices were returned. |
| |
| if (!pPhysicalDevices) { |
| *pPhysicalDeviceCount = (uint32_t)info.physicalDevices.size(); |
| return VK_SUCCESS; |
| } else { |
| uint32_t actualDeviceCount = (uint32_t)info.physicalDevices.size(); |
| uint32_t toWrite = |
| actualDeviceCount < *pPhysicalDeviceCount ? actualDeviceCount : *pPhysicalDeviceCount; |
| |
| for (uint32_t i = 0; i < toWrite; ++i) { |
| pPhysicalDevices[i] = info.physicalDevices[i]; |
| } |
| |
| *pPhysicalDeviceCount = toWrite; |
| |
| if (actualDeviceCount > *pPhysicalDeviceCount) { |
| return VK_INCOMPLETE; |
| } |
| |
| return VK_SUCCESS; |
| } |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceProperties(void*, VkPhysicalDevice, |
| VkPhysicalDeviceProperties* pProperties) { |
| #if defined(__linux__) && !defined(VK_USE_PLATFORM_ANDROID_KHR) |
| if (pProperties) { |
| if (VK_PHYSICAL_DEVICE_TYPE_CPU == pProperties->deviceType) { |
| /* For Linux guest: Even if host driver reports DEVICE_TYPE_CPU, |
| * override this to VIRTUAL_GPU, otherwise Linux DRM interfaces |
| * will take unexpected code paths to deal with "software" driver |
| */ |
| pProperties->deviceType = VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU; |
| } |
| } |
| #endif |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceFeatures2(void*, VkPhysicalDevice, |
| VkPhysicalDeviceFeatures2* pFeatures) { |
| if (pFeatures) { |
| VkPhysicalDeviceDeviceMemoryReportFeaturesEXT* memoryReportFeaturesEXT = |
| vk_find_struct<VkPhysicalDeviceDeviceMemoryReportFeaturesEXT>(pFeatures); |
| if (memoryReportFeaturesEXT) { |
| memoryReportFeaturesEXT->deviceMemoryReport = VK_TRUE; |
| } |
| } |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceFeatures2KHR(void* context, |
| VkPhysicalDevice physicalDevice, |
| VkPhysicalDeviceFeatures2* pFeatures) { |
| on_vkGetPhysicalDeviceFeatures2(context, physicalDevice, pFeatures); |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceProperties2(void* context, |
| VkPhysicalDevice physicalDevice, |
| VkPhysicalDeviceProperties2* pProperties) { |
| if (pProperties) { |
| VkPhysicalDeviceDeviceMemoryReportFeaturesEXT* memoryReportFeaturesEXT = |
| vk_find_struct<VkPhysicalDeviceDeviceMemoryReportFeaturesEXT>(pProperties); |
| if (memoryReportFeaturesEXT) { |
| memoryReportFeaturesEXT->deviceMemoryReport = VK_TRUE; |
| } |
| on_vkGetPhysicalDeviceProperties(context, physicalDevice, &pProperties->properties); |
| } |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceProperties2KHR( |
| void* context, VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties) { |
| on_vkGetPhysicalDeviceProperties2(context, physicalDevice, pProperties); |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceMemoryProperties( |
| void* context, VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* out) { |
| // gfxstream decides which physical device to expose to the guest on startup. |
| // Otherwise, we would need a physical device to properties mapping. |
| *out = getPhysicalDeviceMemoryProperties(context, VK_NULL_HANDLE, physicalDevice); |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceMemoryProperties2( |
| void*, VkPhysicalDevice physdev, VkPhysicalDeviceMemoryProperties2* out) { |
| on_vkGetPhysicalDeviceMemoryProperties(nullptr, physdev, &out->memoryProperties); |
| } |
| |
| void ResourceTracker::on_vkGetDeviceQueue(void*, VkDevice device, uint32_t, uint32_t, |
| VkQueue* pQueue) { |
| AutoLock<RecursiveLock> lock(mLock); |
| info_VkQueue[*pQueue].device = device; |
| } |
| |
| void ResourceTracker::on_vkGetDeviceQueue2(void*, VkDevice device, const VkDeviceQueueInfo2*, |
| VkQueue* pQueue) { |
| AutoLock<RecursiveLock> lock(mLock); |
| info_VkQueue[*pQueue].device = device; |
| } |
| |
| VkResult ResourceTracker::on_vkCreateInstance(void* context, VkResult input_result, |
| const VkInstanceCreateInfo* createInfo, |
| const VkAllocationCallbacks*, VkInstance* pInstance) { |
| if (input_result != VK_SUCCESS) return input_result; |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| uint32_t apiVersion; |
| VkResult enumInstanceVersionRes = |
| enc->vkEnumerateInstanceVersion(&apiVersion, false /* no lock */); |
| |
| setInstanceInfo(*pInstance, createInfo->enabledExtensionCount, |
| createInfo->ppEnabledExtensionNames, apiVersion); |
| |
| return input_result; |
| } |
| |
| VkResult ResourceTracker::on_vkCreateDevice(void* context, VkResult input_result, |
| VkPhysicalDevice physicalDevice, |
| const VkDeviceCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks*, VkDevice* pDevice) { |
| if (input_result != VK_SUCCESS) return input_result; |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| VkPhysicalDeviceProperties props; |
| VkPhysicalDeviceMemoryProperties memProps; |
| enc->vkGetPhysicalDeviceProperties(physicalDevice, &props, false /* no lock */); |
| enc->vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps, false /* no lock */); |
| |
| setDeviceInfo(*pDevice, physicalDevice, props, memProps, pCreateInfo->enabledExtensionCount, |
| pCreateInfo->ppEnabledExtensionNames, pCreateInfo->pNext); |
| |
| return input_result; |
| } |
| |
| void ResourceTracker::on_vkDestroyDevice_pre(void* context, VkDevice device, |
| const VkAllocationCallbacks*) { |
| (void)context; |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkDevice.find(device); |
| if (it == info_VkDevice.end()) return; |
| |
| for (auto itr = info_VkDeviceMemory.cbegin(); itr != info_VkDeviceMemory.cend();) { |
| auto& memInfo = itr->second; |
| if (memInfo.device == device) { |
| itr = info_VkDeviceMemory.erase(itr); |
| } else { |
| itr++; |
| } |
| } |
| } |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| void updateMemoryTypeBits(uint32_t* memoryTypeBits, uint32_t memoryIndex) { |
| *memoryTypeBits = 1u << memoryIndex; |
| } |
| #endif |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| |
| VkResult ResourceTracker::on_vkGetAndroidHardwareBufferPropertiesANDROID( |
| void* context, VkResult, VkDevice device, const AHardwareBuffer* buffer, |
| VkAndroidHardwareBufferPropertiesANDROID* pProperties) { |
| auto grallocHelper = |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->grallocHelper(); |
| |
| // Delete once goldfish Linux drivers are gone |
| if (mCaps.vulkanCapset.colorBufferMemoryIndex == 0xFFFFFFFF) { |
| mCaps.vulkanCapset.colorBufferMemoryIndex = getColorBufferMemoryIndex(context, device); |
| } |
| |
| updateMemoryTypeBits(&pProperties->memoryTypeBits, mCaps.vulkanCapset.colorBufferMemoryIndex); |
| |
| return getAndroidHardwareBufferPropertiesANDROID(grallocHelper, buffer, pProperties); |
| } |
| |
| VkResult ResourceTracker::on_vkGetMemoryAndroidHardwareBufferANDROID( |
| void*, VkResult, VkDevice device, const VkMemoryGetAndroidHardwareBufferInfoANDROID* pInfo, |
| struct AHardwareBuffer** pBuffer) { |
| if (!pInfo) return VK_ERROR_INITIALIZATION_FAILED; |
| if (!pInfo->memory) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto deviceIt = info_VkDevice.find(device); |
| |
| if (deviceIt == info_VkDevice.end()) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| auto memoryIt = info_VkDeviceMemory.find(pInfo->memory); |
| |
| if (memoryIt == info_VkDeviceMemory.end()) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| auto& info = memoryIt->second; |
| |
| auto* gralloc = ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->grallocHelper(); |
| VkResult queryRes = getMemoryAndroidHardwareBufferANDROID(gralloc, &info.ahw); |
| |
| if (queryRes != VK_SUCCESS) return queryRes; |
| |
| *pBuffer = info.ahw; |
| |
| return queryRes; |
| } |
| #endif |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| VkResult ResourceTracker::on_vkGetMemoryZirconHandleFUCHSIA( |
| void*, VkResult, VkDevice device, const VkMemoryGetZirconHandleInfoFUCHSIA* pInfo, |
| uint32_t* pHandle) { |
| if (!pInfo) return VK_ERROR_INITIALIZATION_FAILED; |
| if (!pInfo->memory) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto deviceIt = info_VkDevice.find(device); |
| |
| if (deviceIt == info_VkDevice.end()) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| auto memoryIt = info_VkDeviceMemory.find(pInfo->memory); |
| |
| if (memoryIt == info_VkDeviceMemory.end()) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| auto& info = memoryIt->second; |
| |
| if (info.vmoHandle == ZX_HANDLE_INVALID) { |
| mesa_loge("%s: memory cannot be exported", __func__); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| *pHandle = ZX_HANDLE_INVALID; |
| zx_handle_duplicate(info.vmoHandle, ZX_RIGHT_SAME_RIGHTS, pHandle); |
| return VK_SUCCESS; |
| } |
| |
| VkResult ResourceTracker::on_vkGetMemoryZirconHandlePropertiesFUCHSIA( |
| void*, VkResult, VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, |
| uint32_t handle, VkMemoryZirconHandlePropertiesFUCHSIA* pProperties) { |
| using fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal; |
| using fuchsia_hardware_goldfish::wire::kMemoryPropertyHostVisible; |
| |
| if (handleType != VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| zx_info_handle_basic_t handleInfo; |
| zx_status_t status = zx::unowned_vmo(handle)->get_info(ZX_INFO_HANDLE_BASIC, &handleInfo, |
| sizeof(handleInfo), nullptr, nullptr); |
| if (status != ZX_OK || handleInfo.type != ZX_OBJ_TYPE_VMO) { |
| return VK_ERROR_INVALID_EXTERNAL_HANDLE; |
| } |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto deviceIt = info_VkDevice.find(device); |
| |
| if (deviceIt == info_VkDevice.end()) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| auto& info = deviceIt->second; |
| |
| zx::vmo vmo_dup; |
| status = zx::unowned_vmo(handle)->duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_dup); |
| if (status != ZX_OK) { |
| mesa_loge("zx_handle_duplicate() error: %d", status); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| uint32_t memoryProperty = 0u; |
| |
| auto result = mControlDevice->GetBufferHandleInfo(std::move(vmo_dup)); |
| if (!result.ok()) { |
| mesa_loge("mControlDevice->GetBufferHandleInfo fatal error: epitaph: %d", result.status()); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| if (result.value().is_ok()) { |
| memoryProperty = result.value().value()->info.memory_property(); |
| } else if (result.value().error_value() == ZX_ERR_NOT_FOUND) { |
| // If a VMO is allocated while ColorBuffer/Buffer is not created, |
| // it must be a device-local buffer, since for host-visible buffers, |
| // ColorBuffer/Buffer is created at sysmem allocation time. |
| memoryProperty = kMemoryPropertyDeviceLocal; |
| } else { |
| // Importing read-only host memory into the Vulkan driver should not |
| // work, but it is not an error to try to do so. Returning a |
| // VkMemoryZirconHandlePropertiesFUCHSIA with no available |
| // memoryType bits should be enough for clients. See fxbug.dev/42098398 |
| // for other issues this this flow. |
| mesa_logw("GetBufferHandleInfo failed: %d", result.value().error_value()); |
| pProperties->memoryTypeBits = 0; |
| return VK_SUCCESS; |
| } |
| |
| pProperties->memoryTypeBits = 0; |
| for (uint32_t i = 0; i < info.memProps.memoryTypeCount; ++i) { |
| if (((memoryProperty & kMemoryPropertyDeviceLocal) && |
| (info.memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) || |
| ((memoryProperty & kMemoryPropertyHostVisible) && |
| (info.memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))) { |
| pProperties->memoryTypeBits |= 1ull << i; |
| } |
| } |
| return VK_SUCCESS; |
| } |
| |
| zx_koid_t getEventKoid(zx_handle_t eventHandle) { |
| if (eventHandle == ZX_HANDLE_INVALID) { |
| return ZX_KOID_INVALID; |
| } |
| |
| zx_info_handle_basic_t info; |
| zx_status_t status = zx_object_get_info(eventHandle, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), |
| nullptr, nullptr); |
| if (status != ZX_OK) { |
| mesa_loge("Cannot get object info of handle %u: %d", eventHandle, status); |
| return ZX_KOID_INVALID; |
| } |
| return info.koid; |
| } |
| |
| VkResult ResourceTracker::on_vkImportSemaphoreZirconHandleFUCHSIA( |
| void*, VkResult, VkDevice device, const VkImportSemaphoreZirconHandleInfoFUCHSIA* pInfo) { |
| if (!pInfo) return VK_ERROR_INITIALIZATION_FAILED; |
| if (!pInfo->semaphore) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto deviceIt = info_VkDevice.find(device); |
| |
| if (deviceIt == info_VkDevice.end()) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| auto semaphoreIt = info_VkSemaphore.find(pInfo->semaphore); |
| |
| if (semaphoreIt == info_VkSemaphore.end()) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| auto& info = semaphoreIt->second; |
| |
| if (info.eventHandle != ZX_HANDLE_INVALID) { |
| zx_handle_close(info.eventHandle); |
| } |
| #if VK_HEADER_VERSION < 174 |
| info.eventHandle = pInfo->handle; |
| #else // VK_HEADER_VERSION >= 174 |
| info.eventHandle = pInfo->zirconHandle; |
| #endif // VK_HEADER_VERSION < 174 |
| if (info.eventHandle != ZX_HANDLE_INVALID) { |
| info.eventKoid = getEventKoid(info.eventHandle); |
| } |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult ResourceTracker::on_vkGetSemaphoreZirconHandleFUCHSIA( |
| void*, VkResult, VkDevice device, const VkSemaphoreGetZirconHandleInfoFUCHSIA* pInfo, |
| uint32_t* pHandle) { |
| if (!pInfo) return VK_ERROR_INITIALIZATION_FAILED; |
| if (!pInfo->semaphore) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto deviceIt = info_VkDevice.find(device); |
| |
| if (deviceIt == info_VkDevice.end()) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| auto semaphoreIt = info_VkSemaphore.find(pInfo->semaphore); |
| |
| if (semaphoreIt == info_VkSemaphore.end()) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| auto& info = semaphoreIt->second; |
| |
| if (info.eventHandle == ZX_HANDLE_INVALID) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| *pHandle = ZX_HANDLE_INVALID; |
| zx_handle_duplicate(info.eventHandle, ZX_RIGHT_SAME_RIGHTS, pHandle); |
| return VK_SUCCESS; |
| } |
| |
| VkResult ResourceTracker::on_vkCreateBufferCollectionFUCHSIA( |
| void*, VkResult, VkDevice, const VkBufferCollectionCreateInfoFUCHSIA* pInfo, |
| const VkAllocationCallbacks*, VkBufferCollectionFUCHSIA* pCollection) { |
| fidl::ClientEnd<::fuchsia_sysmem::BufferCollectionToken> token_client; |
| |
| if (pInfo->collectionToken) { |
| token_client = fidl::ClientEnd<::fuchsia_sysmem::BufferCollectionToken>( |
| zx::channel(pInfo->collectionToken)); |
| } else { |
| auto endpoints = fidl::CreateEndpoints<::fuchsia_sysmem::BufferCollectionToken>(); |
| if (!endpoints.is_ok()) { |
| mesa_loge("zx_channel_create failed: %d", endpoints.status_value()); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| auto result = mSysmemAllocator->AllocateSharedCollection(std::move(endpoints->server)); |
| if (!result.ok()) { |
| mesa_loge("AllocateSharedCollection failed: %d", result.status()); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| token_client = std::move(endpoints->client); |
| } |
| |
| auto endpoints = fidl::CreateEndpoints<::fuchsia_sysmem::BufferCollection>(); |
| if (!endpoints.is_ok()) { |
| mesa_loge("zx_channel_create failed: %d", endpoints.status_value()); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| auto [collection_client, collection_server] = std::move(endpoints.value()); |
| |
| auto result = mSysmemAllocator->BindSharedCollection(std::move(token_client), |
| std::move(collection_server)); |
| if (!result.ok()) { |
| mesa_loge("BindSharedCollection failed: %d", result.status()); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| auto* sysmem_collection = |
| new fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>(std::move(collection_client)); |
| *pCollection = reinterpret_cast<VkBufferCollectionFUCHSIA>(sysmem_collection); |
| |
| register_VkBufferCollectionFUCHSIA(*pCollection); |
| return VK_SUCCESS; |
| } |
| |
| void ResourceTracker::on_vkDestroyBufferCollectionFUCHSIA(void*, VkResult, VkDevice, |
| VkBufferCollectionFUCHSIA collection, |
| const VkAllocationCallbacks*) { |
| auto sysmem_collection = |
| reinterpret_cast<fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>*>(collection); |
| if (sysmem_collection) { |
| (*sysmem_collection)->Close(); |
| } |
| delete sysmem_collection; |
| |
| unregister_VkBufferCollectionFUCHSIA(collection); |
| } |
| |
| SetBufferCollectionImageConstraintsResult ResourceTracker::setBufferCollectionImageConstraintsImpl( |
| VkEncoder* enc, VkDevice device, |
| fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>* pCollection, |
| const VkImageConstraintsInfoFUCHSIA* pImageConstraintsInfo) { |
| const auto& collection = *pCollection; |
| if (!pImageConstraintsInfo || |
| pImageConstraintsInfo->sType != VK_STRUCTURE_TYPE_IMAGE_CONSTRAINTS_INFO_FUCHSIA) { |
| mesa_loge("%s: invalid pImageConstraintsInfo", __func__); |
| return {VK_ERROR_INITIALIZATION_FAILED}; |
| } |
| |
| if (pImageConstraintsInfo->formatConstraintsCount == 0) { |
| mesa_loge("%s: formatConstraintsCount must be greater than 0", __func__); |
| abort(); |
| } |
| |
| fuchsia_sysmem::wire::BufferCollectionConstraints constraints = |
| defaultBufferCollectionConstraints( |
| /* min_size_bytes */ 0, |
| pImageConstraintsInfo->bufferCollectionConstraints.minBufferCount, |
| pImageConstraintsInfo->bufferCollectionConstraints.maxBufferCount, |
| pImageConstraintsInfo->bufferCollectionConstraints.minBufferCountForCamping, |
| pImageConstraintsInfo->bufferCollectionConstraints.minBufferCountForDedicatedSlack, |
| pImageConstraintsInfo->bufferCollectionConstraints.minBufferCountForSharedSlack); |
| |
| std::vector<fuchsia_sysmem::wire::ImageFormatConstraints> format_constraints; |
| |
| VkPhysicalDevice physicalDevice; |
| { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto deviceIt = info_VkDevice.find(device); |
| if (deviceIt == info_VkDevice.end()) { |
| return {VK_ERROR_INITIALIZATION_FAILED}; |
| } |
| physicalDevice = deviceIt->second.physdev; |
| } |
| |
| std::vector<uint32_t> createInfoIndex; |
| |
| bool hasOptimalTiling = false; |
| for (uint32_t i = 0; i < pImageConstraintsInfo->formatConstraintsCount; i++) { |
| const VkImageCreateInfo* createInfo = |
| &pImageConstraintsInfo->pFormatConstraints[i].imageCreateInfo; |
| const VkImageFormatConstraintsInfoFUCHSIA* formatConstraints = |
| &pImageConstraintsInfo->pFormatConstraints[i]; |
| |
| // add ImageFormatConstraints for *optimal* tiling |
| VkResult optimalResult = VK_ERROR_FORMAT_NOT_SUPPORTED; |
| if (createInfo->tiling == VK_IMAGE_TILING_OPTIMAL) { |
| optimalResult = addImageBufferCollectionConstraintsFUCHSIA( |
| enc, device, physicalDevice, formatConstraints, VK_IMAGE_TILING_OPTIMAL, |
| &constraints); |
| if (optimalResult == VK_SUCCESS) { |
| createInfoIndex.push_back(i); |
| hasOptimalTiling = true; |
| } |
| } |
| |
| // Add ImageFormatConstraints for *linear* tiling |
| VkResult linearResult = addImageBufferCollectionConstraintsFUCHSIA( |
| enc, device, physicalDevice, formatConstraints, VK_IMAGE_TILING_LINEAR, &constraints); |
| if (linearResult == VK_SUCCESS) { |
| createInfoIndex.push_back(i); |
| } |
| |
| // Update usage and BufferMemoryConstraints |
| if (linearResult == VK_SUCCESS || optimalResult == VK_SUCCESS) { |
| constraints.usage.vulkan |= getBufferCollectionConstraintsVulkanImageUsage(createInfo); |
| |
| if (formatConstraints && formatConstraints->flags) { |
| mesa_logw( |
| "%s: Non-zero flags (%08x) in image format " |
| "constraints; this is currently not supported, see " |
| "fxbug.dev/42147900.", |
| __func__, formatConstraints->flags); |
| } |
| } |
| } |
| |
| // Set buffer memory constraints based on optimal/linear tiling support |
| // and flags. |
| VkImageConstraintsInfoFlagsFUCHSIA flags = pImageConstraintsInfo->flags; |
| if (flags & VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_RARELY_FUCHSIA) |
| constraints.usage.cpu |= fuchsia_sysmem::wire::kCpuUsageRead; |
| if (flags & VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_OFTEN_FUCHSIA) |
| constraints.usage.cpu |= fuchsia_sysmem::wire::kCpuUsageReadOften; |
| if (flags & VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_RARELY_FUCHSIA) |
| constraints.usage.cpu |= fuchsia_sysmem::wire::kCpuUsageWrite; |
| if (flags & VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_OFTEN_FUCHSIA) |
| constraints.usage.cpu |= fuchsia_sysmem::wire::kCpuUsageWriteOften; |
| |
| constraints.has_buffer_memory_constraints = true; |
| auto& memory_constraints = constraints.buffer_memory_constraints; |
| memory_constraints.cpu_domain_supported = true; |
| memory_constraints.ram_domain_supported = true; |
| memory_constraints.inaccessible_domain_supported = |
| hasOptimalTiling && !(flags & (VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_RARELY_FUCHSIA | |
| VK_IMAGE_CONSTRAINTS_INFO_CPU_READ_OFTEN_FUCHSIA | |
| VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_RARELY_FUCHSIA | |
| VK_IMAGE_CONSTRAINTS_INFO_CPU_WRITE_OFTEN_FUCHSIA)); |
| |
| if (memory_constraints.inaccessible_domain_supported) { |
| memory_constraints.heap_permitted_count = 2; |
| memory_constraints.heap_permitted[0] = fuchsia_sysmem::wire::HeapType::kGoldfishDeviceLocal; |
| memory_constraints.heap_permitted[1] = fuchsia_sysmem::wire::HeapType::kGoldfishHostVisible; |
| } else { |
| memory_constraints.heap_permitted_count = 1; |
| memory_constraints.heap_permitted[0] = fuchsia_sysmem::wire::HeapType::kGoldfishHostVisible; |
| } |
| |
| if (constraints.image_format_constraints_count == 0) { |
| mesa_loge("%s: none of the specified formats is supported by device", __func__); |
| return {VK_ERROR_FORMAT_NOT_SUPPORTED}; |
| } |
| |
| constexpr uint32_t kVulkanPriority = 5; |
| const char kName[] = "GoldfishSysmemShared"; |
| collection->SetName(kVulkanPriority, fidl::StringView(kName)); |
| |
| auto result = collection->SetConstraints(true, constraints); |
| if (!result.ok()) { |
| mesa_loge("setBufferCollectionConstraints: SetConstraints failed: %d", result.status()); |
| return {VK_ERROR_INITIALIZATION_FAILED}; |
| } |
| |
| return {VK_SUCCESS, constraints, std::move(createInfoIndex)}; |
| } |
| |
| VkResult ResourceTracker::setBufferCollectionImageConstraintsFUCHSIA( |
| VkEncoder* enc, VkDevice device, |
| fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>* pCollection, |
| const VkImageConstraintsInfoFUCHSIA* pImageConstraintsInfo) { |
| const auto& collection = *pCollection; |
| |
| auto setConstraintsResult = |
| setBufferCollectionImageConstraintsImpl(enc, device, pCollection, pImageConstraintsInfo); |
| if (setConstraintsResult.result != VK_SUCCESS) { |
| return setConstraintsResult.result; |
| } |
| |
| // copy constraints to info_VkBufferCollectionFUCHSIA if |
| // |collection| is a valid VkBufferCollectionFUCHSIA handle. |
| AutoLock<RecursiveLock> lock(mLock); |
| VkBufferCollectionFUCHSIA buffer_collection = |
| reinterpret_cast<VkBufferCollectionFUCHSIA>(pCollection); |
| if (info_VkBufferCollectionFUCHSIA.find(buffer_collection) != |
| info_VkBufferCollectionFUCHSIA.end()) { |
| info_VkBufferCollectionFUCHSIA[buffer_collection].constraints = |
| gfxstream::guest::makeOptional(std::move(setConstraintsResult.constraints)); |
| info_VkBufferCollectionFUCHSIA[buffer_collection].createInfoIndex = |
| std::move(setConstraintsResult.createInfoIndex); |
| } |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult ResourceTracker::setBufferCollectionBufferConstraintsFUCHSIA( |
| fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>* pCollection, |
| const VkBufferConstraintsInfoFUCHSIA* pBufferConstraintsInfo) { |
| auto setConstraintsResult = |
| setBufferCollectionBufferConstraintsImpl(pCollection, pBufferConstraintsInfo); |
| if (setConstraintsResult.result != VK_SUCCESS) { |
| return setConstraintsResult.result; |
| } |
| |
| // copy constraints to info_VkBufferCollectionFUCHSIA if |
| // |collection| is a valid VkBufferCollectionFUCHSIA handle. |
| AutoLock<RecursiveLock> lock(mLock); |
| VkBufferCollectionFUCHSIA buffer_collection = |
| reinterpret_cast<VkBufferCollectionFUCHSIA>(pCollection); |
| if (info_VkBufferCollectionFUCHSIA.find(buffer_collection) != |
| info_VkBufferCollectionFUCHSIA.end()) { |
| info_VkBufferCollectionFUCHSIA[buffer_collection].constraints = |
| gfxstream::guest::makeOptional(setConstraintsResult.constraints); |
| } |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult ResourceTracker::on_vkSetBufferCollectionImageConstraintsFUCHSIA( |
| void* context, VkResult, VkDevice device, VkBufferCollectionFUCHSIA collection, |
| const VkImageConstraintsInfoFUCHSIA* pImageConstraintsInfo) { |
| VkEncoder* enc = (VkEncoder*)context; |
| auto sysmem_collection = |
| reinterpret_cast<fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>*>(collection); |
| return setBufferCollectionImageConstraintsFUCHSIA(enc, device, sysmem_collection, |
| pImageConstraintsInfo); |
| } |
| |
| VkResult ResourceTracker::on_vkSetBufferCollectionBufferConstraintsFUCHSIA( |
| void*, VkResult, VkDevice, VkBufferCollectionFUCHSIA collection, |
| const VkBufferConstraintsInfoFUCHSIA* pBufferConstraintsInfo) { |
| auto sysmem_collection = |
| reinterpret_cast<fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>*>(collection); |
| return setBufferCollectionBufferConstraintsFUCHSIA(sysmem_collection, pBufferConstraintsInfo); |
| } |
| |
| VkResult ResourceTracker::getBufferCollectionImageCreateInfoIndexLocked( |
| VkBufferCollectionFUCHSIA collection, fuchsia_sysmem::wire::BufferCollectionInfo2& info, |
| uint32_t* outCreateInfoIndex) { |
| if (!info_VkBufferCollectionFUCHSIA[collection].constraints.hasValue()) { |
| mesa_loge("%s: constraints not set", __func__); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| |
| if (!info.settings.has_image_format_constraints) { |
| // no image format constraints, skip getting createInfoIndex. |
| return VK_SUCCESS; |
| } |
| |
| const auto& constraints = *info_VkBufferCollectionFUCHSIA[collection].constraints; |
| const auto& createInfoIndices = info_VkBufferCollectionFUCHSIA[collection].createInfoIndex; |
| const auto& out = info.settings.image_format_constraints; |
| bool foundCreateInfo = false; |
| |
| for (size_t imageFormatIndex = 0; imageFormatIndex < constraints.image_format_constraints_count; |
| imageFormatIndex++) { |
| const auto& in = constraints.image_format_constraints[imageFormatIndex]; |
| // These checks are sorted in order of how often they're expected to |
| // mismatch, from most likely to least likely. They aren't always |
| // equality comparisons, since sysmem may change some values in |
| // compatible ways on behalf of the other participants. |
| if ((out.pixel_format.type != in.pixel_format.type) || |
| (out.pixel_format.has_format_modifier != in.pixel_format.has_format_modifier) || |
| (out.pixel_format.format_modifier.value != in.pixel_format.format_modifier.value) || |
| (out.min_bytes_per_row < in.min_bytes_per_row) || |
| (out.required_max_coded_width < in.required_max_coded_width) || |
| (out.required_max_coded_height < in.required_max_coded_height) || |
| (in.bytes_per_row_divisor != 0 && |
| out.bytes_per_row_divisor % in.bytes_per_row_divisor != 0)) { |
| continue; |
| } |
| // Check if the out colorspaces are a subset of the in color spaces. |
| bool all_color_spaces_found = true; |
| for (uint32_t j = 0; j < out.color_spaces_count; j++) { |
| bool found_matching_color_space = false; |
| for (uint32_t k = 0; k < in.color_spaces_count; k++) { |
| if (out.color_space[j].type == in.color_space[k].type) { |
| found_matching_color_space = true; |
| break; |
| } |
| } |
| if (!found_matching_color_space) { |
| all_color_spaces_found = false; |
| break; |
| } |
| } |
| if (!all_color_spaces_found) { |
| continue; |
| } |
| |
| // Choose the first valid format for now. |
| *outCreateInfoIndex = createInfoIndices[imageFormatIndex]; |
| return VK_SUCCESS; |
| } |
| |
| mesa_loge("%s: cannot find a valid image format in constraints", __func__); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| |
| VkResult ResourceTracker::on_vkGetBufferCollectionPropertiesFUCHSIA( |
| void* context, VkResult, VkDevice device, VkBufferCollectionFUCHSIA collection, |
| VkBufferCollectionPropertiesFUCHSIA* pProperties) { |
| VkEncoder* enc = (VkEncoder*)context; |
| const auto& sysmem_collection = |
| *reinterpret_cast<fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>*>(collection); |
| |
| auto result = sysmem_collection->WaitForBuffersAllocated(); |
| if (!result.ok() || result->status != ZX_OK) { |
| mesa_loge("Failed wait for allocation: %d %d", result.status(), |
| GET_STATUS_SAFE(result, status)); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| fuchsia_sysmem::wire::BufferCollectionInfo2 info = std::move(result->buffer_collection_info); |
| |
| bool is_host_visible = |
| info.settings.buffer_settings.heap == fuchsia_sysmem::wire::HeapType::kGoldfishHostVisible; |
| bool is_device_local = |
| info.settings.buffer_settings.heap == fuchsia_sysmem::wire::HeapType::kGoldfishDeviceLocal; |
| if (!is_host_visible && !is_device_local) { |
| mesa_loge("buffer collection uses a non-goldfish heap (type 0x%lu)", |
| static_cast<uint64_t>(info.settings.buffer_settings.heap)); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| // memoryTypeBits |
| // ==================================================================== |
| { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto deviceIt = info_VkDevice.find(device); |
| if (deviceIt == info_VkDevice.end()) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| auto& deviceInfo = deviceIt->second; |
| |
| // Device local memory type supported. |
| pProperties->memoryTypeBits = 0; |
| for (uint32_t i = 0; i < deviceInfo.memProps.memoryTypeCount; ++i) { |
| if ((is_device_local && (deviceInfo.memProps.memoryTypes[i].propertyFlags & |
| VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) || |
| (is_host_visible && (deviceInfo.memProps.memoryTypes[i].propertyFlags & |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))) { |
| pProperties->memoryTypeBits |= 1ull << i; |
| } |
| } |
| } |
| |
| // bufferCount |
| // ==================================================================== |
| pProperties->bufferCount = info.buffer_count; |
| |
| auto storeProperties = [this, collection, pProperties]() -> VkResult { |
| // store properties to storage |
| AutoLock<RecursiveLock> lock(mLock); |
| if (info_VkBufferCollectionFUCHSIA.find(collection) == |
| info_VkBufferCollectionFUCHSIA.end()) { |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| |
| info_VkBufferCollectionFUCHSIA[collection].properties = |
| gfxstream::guest::makeOptional(*pProperties); |
| |
| // We only do a shallow copy so we should remove all pNext pointers. |
| info_VkBufferCollectionFUCHSIA[collection].properties->pNext = nullptr; |
| info_VkBufferCollectionFUCHSIA[collection].properties->sysmemColorSpaceIndex.pNext = |
| nullptr; |
| return VK_SUCCESS; |
| }; |
| |
| // The fields below only apply to buffer collections with image formats. |
| if (!info.settings.has_image_format_constraints) { |
| mesa_logd("%s: buffer collection doesn't have image format constraints", __func__); |
| return storeProperties(); |
| } |
| |
| // sysmemFormat |
| // ==================================================================== |
| |
| pProperties->sysmemPixelFormat = |
| static_cast<uint64_t>(info.settings.image_format_constraints.pixel_format.type); |
| |
| // colorSpace |
| // ==================================================================== |
| if (info.settings.image_format_constraints.color_spaces_count == 0) { |
| mesa_loge( |
| "%s: color space missing from allocated buffer collection " |
| "constraints", |
| __func__); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| // Only report first colorspace for now. |
| pProperties->sysmemColorSpaceIndex.colorSpace = |
| static_cast<uint32_t>(info.settings.image_format_constraints.color_space[0].type); |
| |
| // createInfoIndex |
| // ==================================================================== |
| { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto getIndexResult = getBufferCollectionImageCreateInfoIndexLocked( |
| collection, info, &pProperties->createInfoIndex); |
| if (getIndexResult != VK_SUCCESS) { |
| return getIndexResult; |
| } |
| } |
| |
| // formatFeatures |
| // ==================================================================== |
| VkPhysicalDevice physicalDevice; |
| { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto deviceIt = info_VkDevice.find(device); |
| if (deviceIt == info_VkDevice.end()) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| physicalDevice = deviceIt->second.physdev; |
| } |
| |
| VkFormat vkFormat = |
| sysmemPixelFormatTypeToVk(info.settings.image_format_constraints.pixel_format.type); |
| VkFormatProperties formatProperties; |
| enc->vkGetPhysicalDeviceFormatProperties(physicalDevice, vkFormat, &formatProperties, |
| true /* do lock */); |
| if (is_device_local) { |
| pProperties->formatFeatures = formatProperties.optimalTilingFeatures; |
| } |
| if (is_host_visible) { |
| pProperties->formatFeatures = formatProperties.linearTilingFeatures; |
| } |
| |
| // YCbCr properties |
| // ==================================================================== |
| // TODO(59804): Implement this correctly when we support YUV pixel |
| // formats in goldfish ICD. |
| pProperties->samplerYcbcrConversionComponents.r = VK_COMPONENT_SWIZZLE_IDENTITY; |
| pProperties->samplerYcbcrConversionComponents.g = VK_COMPONENT_SWIZZLE_IDENTITY; |
| pProperties->samplerYcbcrConversionComponents.b = VK_COMPONENT_SWIZZLE_IDENTITY; |
| pProperties->samplerYcbcrConversionComponents.a = VK_COMPONENT_SWIZZLE_IDENTITY; |
| pProperties->suggestedYcbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY; |
| pProperties->suggestedYcbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL; |
| pProperties->suggestedXChromaOffset = VK_CHROMA_LOCATION_MIDPOINT; |
| pProperties->suggestedYChromaOffset = VK_CHROMA_LOCATION_MIDPOINT; |
| |
| return storeProperties(); |
| } |
| #endif |
| |
| static uint32_t getVirglFormat(VkFormat vkFormat) { |
| uint32_t virglFormat = 0; |
| |
| switch (vkFormat) { |
| case VK_FORMAT_R8G8B8A8_SINT: |
| case VK_FORMAT_R8G8B8A8_UNORM: |
| case VK_FORMAT_R8G8B8A8_SRGB: |
| case VK_FORMAT_R8G8B8A8_SNORM: |
| case VK_FORMAT_R8G8B8A8_SSCALED: |
| case VK_FORMAT_R8G8B8A8_USCALED: |
| virglFormat = VIRGL_FORMAT_R8G8B8A8_UNORM; |
| break; |
| case VK_FORMAT_B8G8R8A8_SINT: |
| case VK_FORMAT_B8G8R8A8_UNORM: |
| case VK_FORMAT_B8G8R8A8_SRGB: |
| case VK_FORMAT_B8G8R8A8_SNORM: |
| case VK_FORMAT_B8G8R8A8_SSCALED: |
| case VK_FORMAT_B8G8R8A8_USCALED: |
| virglFormat = VIRGL_FORMAT_B8G8R8A8_UNORM; |
| break; |
| default: |
| break; |
| } |
| |
| return virglFormat; |
| } |
| |
| CoherentMemoryPtr ResourceTracker::createCoherentMemory( |
| VkDevice device, VkDeviceMemory mem, const VkMemoryAllocateInfo& hostAllocationInfo, |
| VkEncoder* enc, VkResult& res) { |
| CoherentMemoryPtr coherentMemory = nullptr; |
| |
| #if defined(__ANDROID__) |
| if (mFeatureInfo->hasDirectMem) { |
| uint64_t gpuAddr = 0; |
| GoldfishAddressSpaceBlockPtr block = nullptr; |
| res = enc->vkMapMemoryIntoAddressSpaceGOOGLE(device, mem, &gpuAddr, true); |
| if (res != VK_SUCCESS) { |
| mesa_loge( |
| "Failed to create coherent memory: vkMapMemoryIntoAddressSpaceGOOGLE " |
| "returned:%d.", |
| res); |
| return coherentMemory; |
| } |
| { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto it = info_VkDeviceMemory.find(mem); |
| if (it == info_VkDeviceMemory.end()) { |
| mesa_loge("Failed to create coherent memory: failed to find device memory."); |
| res = VK_ERROR_OUT_OF_HOST_MEMORY; |
| return coherentMemory; |
| } |
| auto& info = it->second; |
| block = info.goldfishBlock; |
| info.goldfishBlock = nullptr; |
| |
| coherentMemory = std::make_shared<CoherentMemory>( |
| block, gpuAddr, hostAllocationInfo.allocationSize, device, mem); |
| } |
| } else |
| #endif // defined(__ANDROID__) |
| if (mFeatureInfo->hasVirtioGpuNext) { |
| struct VirtGpuCreateBlob createBlob = {0}; |
| uint64_t hvaSizeId[3]; |
| res = enc->vkGetMemoryHostAddressInfoGOOGLE(device, mem, &hvaSizeId[0], &hvaSizeId[1], |
| &hvaSizeId[2], true /* do lock */); |
| if (res != VK_SUCCESS) { |
| mesa_loge( |
| "Failed to create coherent memory: vkMapMemoryIntoAddressSpaceGOOGLE " |
| "returned:%d.", |
| res); |
| return coherentMemory; |
| } |
| { |
| AutoLock<RecursiveLock> lock(mLock); |
| VirtGpuDevice* instance = VirtGpuDevice::getInstance((enum VirtGpuCapset)3); |
| createBlob.blobMem = kBlobMemHost3d; |
| createBlob.flags = kBlobFlagMappable; |
| createBlob.blobId = hvaSizeId[2]; |
| createBlob.size = hostAllocationInfo.allocationSize; |
| |
| auto blob = instance->createBlob(createBlob); |
| if (!blob) { |
| mesa_loge("Failed to create coherent memory: failed to create blob."); |
| res = VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| return coherentMemory; |
| } |
| |
| VirtGpuResourceMappingPtr mapping = blob->createMapping(); |
| if (!mapping) { |
| mesa_loge("Failed to create coherent memory: failed to create blob mapping."); |
| res = VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| return coherentMemory; |
| } |
| |
| coherentMemory = |
| std::make_shared<CoherentMemory>(mapping, createBlob.size, device, mem); |
| } |
| } else { |
| mesa_loge("FATAL: Unsupported virtual memory feature"); |
| abort(); |
| } |
| return coherentMemory; |
| } |
| |
| VkResult ResourceTracker::allocateCoherentMemory(VkDevice device, |
| const VkMemoryAllocateInfo* pAllocateInfo, |
| VkEncoder* enc, VkDeviceMemory* pMemory) { |
| uint64_t blobId = 0; |
| uint64_t offset = 0; |
| uint8_t* ptr = nullptr; |
| VkMemoryAllocateFlagsInfo allocFlagsInfo; |
| VkMemoryOpaqueCaptureAddressAllocateInfo opaqueCaptureAddressAllocInfo; |
| VkCreateBlobGOOGLE createBlobInfo; |
| VirtGpuResourcePtr guestBlob = nullptr; |
| |
| memset(&createBlobInfo, 0, sizeof(struct VkCreateBlobGOOGLE)); |
| createBlobInfo.sType = VK_STRUCTURE_TYPE_CREATE_BLOB_GOOGLE; |
| |
| const VkMemoryAllocateFlagsInfo* allocFlagsInfoPtr = |
| vk_find_struct<VkMemoryAllocateFlagsInfo>(pAllocateInfo); |
| const VkMemoryOpaqueCaptureAddressAllocateInfo* opaqueCaptureAddressAllocInfoPtr = |
| vk_find_struct<VkMemoryOpaqueCaptureAddressAllocateInfo>(pAllocateInfo); |
| |
| bool deviceAddressMemoryAllocation = |
| allocFlagsInfoPtr && |
| ((allocFlagsInfoPtr->flags & VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT) || |
| (allocFlagsInfoPtr->flags & VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT)); |
| |
| bool dedicated = deviceAddressMemoryAllocation; |
| |
| if (mCaps.vulkanCapset.deferredMapping || mCaps.params[kParamCreateGuestHandle]) |
| dedicated = true; |
| |
| VkMemoryAllocateInfo hostAllocationInfo = vk_make_orphan_copy(*pAllocateInfo); |
| vk_struct_chain_iterator structChainIter = vk_make_chain_iterator(&hostAllocationInfo); |
| |
| if (mCaps.vulkanCapset.deferredMapping || mCaps.params[kParamCreateGuestHandle]) { |
| hostAllocationInfo.allocationSize = |
| ALIGN(pAllocateInfo->allocationSize, mCaps.vulkanCapset.blobAlignment); |
| } else if (dedicated) { |
| // Over-aligning to kLargestSize to some Windows drivers (b:152769369). Can likely |
| // have host report the desired alignment. |
| hostAllocationInfo.allocationSize = ALIGN(pAllocateInfo->allocationSize, kLargestPageSize); |
| } else { |
| VkDeviceSize roundedUpAllocSize = ALIGN(pAllocateInfo->allocationSize, kMegaByte); |
| hostAllocationInfo.allocationSize = std::max(roundedUpAllocSize, kDefaultHostMemBlockSize); |
| } |
| |
| // Support device address capture/replay allocations |
| if (deviceAddressMemoryAllocation) { |
| if (allocFlagsInfoPtr) { |
| mesa_logi("%s: has alloc flags\n", __func__); |
| allocFlagsInfo = *allocFlagsInfoPtr; |
| vk_append_struct(&structChainIter, &allocFlagsInfo); |
| } |
| |
| if (opaqueCaptureAddressAllocInfoPtr) { |
| mesa_logi("%s: has opaque capture address\n", __func__); |
| opaqueCaptureAddressAllocInfo = *opaqueCaptureAddressAllocInfoPtr; |
| vk_append_struct(&structChainIter, &opaqueCaptureAddressAllocInfo); |
| } |
| } |
| |
| if (mCaps.params[kParamCreateGuestHandle]) { |
| struct VirtGpuCreateBlob createBlob = {0}; |
| struct VirtGpuExecBuffer exec = {}; |
| VirtGpuDevice* instance = VirtGpuDevice::getInstance(); |
| struct gfxstreamPlaceholderCommandVk placeholderCmd = {}; |
| |
| createBlobInfo.blobId = ++mBlobId; |
| createBlobInfo.blobMem = kBlobMemGuest; |
| createBlobInfo.blobFlags = kBlobFlagCreateGuestHandle; |
| vk_append_struct(&structChainIter, &createBlobInfo); |
| |
| createBlob.blobMem = kBlobMemGuest; |
| createBlob.flags = kBlobFlagCreateGuestHandle; |
| createBlob.blobId = createBlobInfo.blobId; |
| createBlob.size = hostAllocationInfo.allocationSize; |
| |
| guestBlob = instance->createBlob(createBlob); |
| if (!guestBlob) { |
| mesa_loge("Failed to allocate coherent memory: failed to create blob."); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| |
| placeholderCmd.hdr.opCode = GFXSTREAM_PLACEHOLDER_COMMAND_VK; |
| exec.command = static_cast<void*>(&placeholderCmd); |
| exec.command_size = sizeof(placeholderCmd); |
| exec.flags = kRingIdx; |
| exec.ring_idx = 1; |
| if (instance->execBuffer(exec, guestBlob.get())) { |
| mesa_loge("Failed to allocate coherent memory: failed to execbuffer for wait."); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| guestBlob->wait(); |
| } else if (mCaps.vulkanCapset.deferredMapping) { |
| createBlobInfo.blobId = ++mBlobId; |
| createBlobInfo.blobMem = kBlobMemHost3d; |
| vk_append_struct(&structChainIter, &createBlobInfo); |
| } |
| |
| VkDeviceMemory mem = VK_NULL_HANDLE; |
| VkResult host_res = |
| enc->vkAllocateMemory(device, &hostAllocationInfo, nullptr, &mem, true /* do lock */); |
| if (host_res != VK_SUCCESS) { |
| mesa_loge("Failed to allocate coherent memory: failed to allocate on the host: %d.", |
| host_res); |
| return host_res; |
| } |
| |
| struct VkDeviceMemory_Info info; |
| if (mCaps.vulkanCapset.deferredMapping || mCaps.params[kParamCreateGuestHandle]) { |
| info.allocationSize = pAllocateInfo->allocationSize; |
| info.blobId = createBlobInfo.blobId; |
| } |
| |
| if (guestBlob) { |
| auto mapping = guestBlob->createMapping(); |
| if (!mapping) { |
| mesa_loge("Failed to allocate coherent memory: failed to create blob mapping."); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| |
| auto coherentMemory = std::make_shared<CoherentMemory>( |
| mapping, hostAllocationInfo.allocationSize, device, mem); |
| |
| coherentMemory->subAllocate(pAllocateInfo->allocationSize, &ptr, offset); |
| info.coherentMemoryOffset = offset; |
| info.coherentMemory = coherentMemory; |
| info.ptr = ptr; |
| } |
| |
| info.coherentMemorySize = hostAllocationInfo.allocationSize; |
| info.memoryTypeIndex = hostAllocationInfo.memoryTypeIndex; |
| info.device = device; |
| info.dedicated = dedicated; |
| { |
| // createCoherentMemory inside need to access info_VkDeviceMemory |
| // information. set it before use. |
| AutoLock<RecursiveLock> lock(mLock); |
| info_VkDeviceMemory[mem] = info; |
| } |
| |
| if (mCaps.vulkanCapset.deferredMapping || mCaps.params[kParamCreateGuestHandle]) { |
| *pMemory = mem; |
| return host_res; |
| } |
| |
| auto coherentMemory = createCoherentMemory(device, mem, hostAllocationInfo, enc, host_res); |
| if (coherentMemory) { |
| AutoLock<RecursiveLock> lock(mLock); |
| coherentMemory->subAllocate(pAllocateInfo->allocationSize, &ptr, offset); |
| info.allocationSize = pAllocateInfo->allocationSize; |
| info.coherentMemoryOffset = offset; |
| info.coherentMemory = coherentMemory; |
| info.ptr = ptr; |
| info_VkDeviceMemory[mem] = info; |
| *pMemory = mem; |
| } else { |
| enc->vkFreeMemory(device, mem, nullptr, true); |
| AutoLock<RecursiveLock> lock(mLock); |
| info_VkDeviceMemory.erase(mem); |
| } |
| return host_res; |
| } |
| |
| VkResult ResourceTracker::getCoherentMemory(const VkMemoryAllocateInfo* pAllocateInfo, |
| VkEncoder* enc, VkDevice device, |
| VkDeviceMemory* pMemory) { |
| VkMemoryAllocateFlagsInfo allocFlagsInfo; |
| VkMemoryOpaqueCaptureAddressAllocateInfo opaqueCaptureAddressAllocInfo; |
| |
| // Add buffer device address capture structs |
| const VkMemoryAllocateFlagsInfo* allocFlagsInfoPtr = |
| vk_find_struct<VkMemoryAllocateFlagsInfo>(pAllocateInfo); |
| |
| bool dedicated = |
| allocFlagsInfoPtr && |
| ((allocFlagsInfoPtr->flags & VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT) || |
| (allocFlagsInfoPtr->flags & VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT)); |
| |
| if (mCaps.vulkanCapset.deferredMapping || mCaps.params[kParamCreateGuestHandle]) |
| dedicated = true; |
| |
| CoherentMemoryPtr coherentMemory = nullptr; |
| uint8_t* ptr = nullptr; |
| uint64_t offset = 0; |
| { |
| AutoLock<RecursiveLock> lock(mLock); |
| for (const auto& [memory, info] : info_VkDeviceMemory) { |
| if (info.device != device) continue; |
| |
| if (info.memoryTypeIndex != pAllocateInfo->memoryTypeIndex) continue; |
| |
| if (info.dedicated || dedicated) continue; |
| |
| if (!info.coherentMemory) continue; |
| |
| if (!info.coherentMemory->subAllocate(pAllocateInfo->allocationSize, &ptr, offset)) |
| continue; |
| |
| coherentMemory = info.coherentMemory; |
| break; |
| } |
| if (coherentMemory) { |
| struct VkDeviceMemory_Info info; |
| info.coherentMemoryOffset = offset; |
| info.ptr = ptr; |
| info.memoryTypeIndex = pAllocateInfo->memoryTypeIndex; |
| info.allocationSize = pAllocateInfo->allocationSize; |
| info.coherentMemory = coherentMemory; |
| info.device = device; |
| |
| // for suballocated memory, create an alias VkDeviceMemory handle for application |
| // memory used for suballocations will still be VkDeviceMemory associated with |
| // CoherentMemory |
| auto mem = new_from_host_VkDeviceMemory(VK_NULL_HANDLE); |
| info_VkDeviceMemory[mem] = info; |
| *pMemory = mem; |
| return VK_SUCCESS; |
| } |
| } |
| return allocateCoherentMemory(device, pAllocateInfo, enc, pMemory); |
| } |
| |
| VkResult ResourceTracker::on_vkAllocateMemory(void* context, VkResult input_result, VkDevice device, |
| const VkMemoryAllocateInfo* pAllocateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkDeviceMemory* pMemory) { |
| #define _RETURN_FAILURE_WITH_DEVICE_MEMORY_REPORT(result) \ |
| { \ |
| auto it = info_VkDevice.find(device); \ |
| if (it == info_VkDevice.end()) return result; \ |
| emitDeviceMemoryReport(it->second, \ |
| VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATION_FAILED_EXT, 0, \ |
| pAllocateInfo->allocationSize, VK_OBJECT_TYPE_DEVICE_MEMORY, 0, \ |
| pAllocateInfo->memoryTypeIndex); \ |
| return result; \ |
| } |
| |
| #define _RETURN_SCUCCESS_WITH_DEVICE_MEMORY_REPORT \ |
| { \ |
| uint64_t memoryObjectId = (uint64_t)(void*)*pMemory; \ |
| if (ahw) { \ |
| memoryObjectId = getAHardwareBufferId(ahw); \ |
| } \ |
| emitDeviceMemoryReport(info_VkDevice[device], \ |
| isImport ? VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_IMPORT_EXT \ |
| : VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATE_EXT, \ |
| memoryObjectId, pAllocateInfo->allocationSize, \ |
| VK_OBJECT_TYPE_DEVICE_MEMORY, (uint64_t)(void*)*pMemory, \ |
| pAllocateInfo->memoryTypeIndex); \ |
| return VK_SUCCESS; \ |
| } |
| |
| if (input_result != VK_SUCCESS) _RETURN_FAILURE_WITH_DEVICE_MEMORY_REPORT(input_result); |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| bool hasDedicatedImage = false; |
| bool hasDedicatedBuffer = false; |
| |
| VkMemoryAllocateInfo finalAllocInfo = vk_make_orphan_copy(*pAllocateInfo); |
| vk_struct_chain_iterator structChainIter = vk_make_chain_iterator(&finalAllocInfo); |
| |
| VkMemoryAllocateFlagsInfo allocFlagsInfo; |
| VkMemoryOpaqueCaptureAddressAllocateInfo opaqueCaptureAddressAllocInfo; |
| |
| // Add buffer device address capture structs |
| const VkMemoryAllocateFlagsInfo* allocFlagsInfoPtr = |
| vk_find_struct<VkMemoryAllocateFlagsInfo>(pAllocateInfo); |
| const VkMemoryOpaqueCaptureAddressAllocateInfo* opaqueCaptureAddressAllocInfoPtr = |
| vk_find_struct<VkMemoryOpaqueCaptureAddressAllocateInfo>(pAllocateInfo); |
| |
| if (allocFlagsInfoPtr) { |
| mesa_logi("%s: has alloc flags\n", __func__); |
| allocFlagsInfo = *allocFlagsInfoPtr; |
| vk_append_struct(&structChainIter, &allocFlagsInfo); |
| } |
| |
| if (opaqueCaptureAddressAllocInfoPtr) { |
| mesa_logi("%s: has opaque capture address\n", __func__); |
| opaqueCaptureAddressAllocInfo = *opaqueCaptureAddressAllocInfoPtr; |
| vk_append_struct(&structChainIter, &opaqueCaptureAddressAllocInfo); |
| } |
| |
| VkMemoryDedicatedAllocateInfo dedicatedAllocInfo; |
| VkImportColorBufferGOOGLE importCbInfo = { |
| VK_STRUCTURE_TYPE_IMPORT_COLOR_BUFFER_GOOGLE, |
| 0, |
| }; |
| VkImportBufferGOOGLE importBufferInfo = { |
| VK_STRUCTURE_TYPE_IMPORT_BUFFER_GOOGLE, |
| 0, |
| }; |
| // VkImportPhysicalAddressGOOGLE importPhysAddrInfo = { |
| // VK_STRUCTURE_TYPE_IMPORT_PHYSICAL_ADDRESS_GOOGLE, 0, |
| // }; |
| |
| const VkExportMemoryAllocateInfo* exportAllocateInfoPtr = |
| vk_find_struct<VkExportMemoryAllocateInfo>(pAllocateInfo); |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| const VkImportAndroidHardwareBufferInfoANDROID* importAhbInfoPtr = |
| vk_find_struct<VkImportAndroidHardwareBufferInfoANDROID>(pAllocateInfo); |
| #else |
| const void* importAhbInfoPtr = nullptr; |
| #endif |
| |
| #if defined(__linux__) && !defined(VK_USE_PLATFORM_ANDROID_KHR) |
| const VkImportMemoryFdInfoKHR* importFdInfoPtr = |
| vk_find_struct<VkImportMemoryFdInfoKHR>(pAllocateInfo); |
| #else |
| const VkImportMemoryFdInfoKHR* importFdInfoPtr = nullptr; |
| #endif |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| const VkImportMemoryBufferCollectionFUCHSIA* importBufferCollectionInfoPtr = |
| vk_find_struct<VkImportMemoryBufferCollectionFUCHSIA>(pAllocateInfo); |
| |
| const VkImportMemoryZirconHandleInfoFUCHSIA* importVmoInfoPtr = |
| vk_find_struct<VkImportMemoryZirconHandleInfoFUCHSIA>(pAllocateInfo); |
| #else |
| const void* importBufferCollectionInfoPtr = nullptr; |
| const void* importVmoInfoPtr = nullptr; |
| #endif // VK_USE_PLATFORM_FUCHSIA |
| |
| const VkMemoryDedicatedAllocateInfo* dedicatedAllocInfoPtr = |
| vk_find_struct<VkMemoryDedicatedAllocateInfo>(pAllocateInfo); |
| |
| // Note for AHardwareBuffers, the Vulkan spec states: |
| // |
| // Android hardware buffers have intrinsic width, height, format, and usage |
| // properties, so Vulkan images bound to memory imported from an Android |
| // hardware buffer must use dedicated allocations |
| // |
| // so any allocation requests with a VkImportAndroidHardwareBufferInfoANDROID |
| // will necessarily have a VkMemoryDedicatedAllocateInfo. However, the host |
| // may or may not actually use a dedicated allocation to emulate |
| // AHardwareBuffers. As such, the VkMemoryDedicatedAllocateInfo is passed to the |
| // host and the host will decide whether or not to use it. |
| |
| bool shouldPassThroughDedicatedAllocInfo = |
| !exportAllocateInfoPtr && !importBufferCollectionInfoPtr && !importVmoInfoPtr; |
| |
| const VkPhysicalDeviceMemoryProperties& physicalDeviceMemoryProps = |
| getPhysicalDeviceMemoryProperties(context, device, VK_NULL_HANDLE); |
| |
| const bool requestedMemoryIsHostVisible = |
| isHostVisible(&physicalDeviceMemoryProps, pAllocateInfo->memoryTypeIndex); |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| shouldPassThroughDedicatedAllocInfo &= !requestedMemoryIsHostVisible; |
| #endif // VK_USE_PLATFORM_FUCHSIA |
| |
| if (shouldPassThroughDedicatedAllocInfo && dedicatedAllocInfoPtr) { |
| dedicatedAllocInfo = vk_make_orphan_copy(*dedicatedAllocInfoPtr); |
| vk_append_struct(&structChainIter, &dedicatedAllocInfo); |
| } |
| |
| // State needed for import/export. |
| bool exportAhb = false; |
| bool exportVmo = false; |
| bool exportDmabuf = false; |
| bool importAhb = false; |
| bool importBufferCollection = false; |
| bool importVmo = false; |
| bool importDmabuf = false; |
| (void)exportVmo; |
| |
| // Even if we export allocate, the underlying operation |
| // for the host is always going to be an import operation. |
| // This is also how Intel's implementation works, |
| // and is generally simpler; |
| // even in an export allocation, |
| // we perform AHardwareBuffer allocation |
| // on the guest side, at this layer, |
| // and then we attach a new VkDeviceMemory |
| // to the AHardwareBuffer on the host via an "import" operation. |
| AHardwareBuffer* ahw = nullptr; |
| |
| if (exportAllocateInfoPtr) { |
| exportAhb = exportAllocateInfoPtr->handleTypes & |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID; |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| exportVmo = exportAllocateInfoPtr->handleTypes & |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA; |
| #endif // VK_USE_PLATFORM_FUCHSIA |
| exportDmabuf = |
| exportAllocateInfoPtr->handleTypes & (VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT | |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT); |
| } else if (importAhbInfoPtr) { |
| importAhb = true; |
| } else if (importBufferCollectionInfoPtr) { |
| importBufferCollection = true; |
| } else if (importVmoInfoPtr) { |
| importVmo = true; |
| } |
| |
| if (importFdInfoPtr) { |
| importDmabuf = |
| (importFdInfoPtr->handleType & (VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT | |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT)); |
| } |
| bool isImport = importAhb || importBufferCollection || importVmo || importDmabuf; |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) |
| if (exportAhb) { |
| hasDedicatedImage = |
| dedicatedAllocInfoPtr && (dedicatedAllocInfoPtr->image != VK_NULL_HANDLE); |
| hasDedicatedBuffer = |
| dedicatedAllocInfoPtr && (dedicatedAllocInfoPtr->buffer != VK_NULL_HANDLE); |
| VkExtent3D imageExtent = {0, 0, 0}; |
| uint32_t imageLayers = 0; |
| VkFormat imageFormat = VK_FORMAT_UNDEFINED; |
| VkImageUsageFlags imageUsage = 0; |
| VkImageCreateFlags imageCreateFlags = 0; |
| VkDeviceSize bufferSize = 0; |
| VkDeviceSize allocationInfoAllocSize = finalAllocInfo.allocationSize; |
| |
| if (hasDedicatedImage) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkImage.find(dedicatedAllocInfoPtr->image); |
| if (it == info_VkImage.end()) |
| _RETURN_FAILURE_WITH_DEVICE_MEMORY_REPORT(VK_ERROR_INITIALIZATION_FAILED); |
| const auto& info = it->second; |
| const auto& imgCi = info.createInfo; |
| |
| imageExtent = imgCi.extent; |
| imageLayers = imgCi.arrayLayers; |
| imageFormat = imgCi.format; |
| imageUsage = imgCi.usage; |
| imageCreateFlags = imgCi.flags; |
| } |
| |
| if (hasDedicatedBuffer) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkBuffer.find(dedicatedAllocInfoPtr->buffer); |
| if (it == info_VkBuffer.end()) |
| _RETURN_FAILURE_WITH_DEVICE_MEMORY_REPORT(VK_ERROR_INITIALIZATION_FAILED); |
| const auto& info = it->second; |
| const auto& bufCi = info.createInfo; |
| |
| bufferSize = bufCi.size; |
| } |
| |
| VkResult ahbCreateRes = createAndroidHardwareBuffer( |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->grallocHelper(), |
| hasDedicatedImage, hasDedicatedBuffer, imageExtent, imageLayers, imageFormat, |
| imageUsage, imageCreateFlags, bufferSize, allocationInfoAllocSize, &ahw); |
| |
| if (ahbCreateRes != VK_SUCCESS) { |
| _RETURN_FAILURE_WITH_DEVICE_MEMORY_REPORT(ahbCreateRes); |
| } |
| } |
| |
| if (importAhb) { |
| ahw = importAhbInfoPtr->buffer; |
| // We still need to acquire the AHardwareBuffer. |
| importAndroidHardwareBuffer( |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->grallocHelper(), |
| importAhbInfoPtr, nullptr); |
| } |
| |
| if (ahw) { |
| auto* gralloc = |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->grallocHelper(); |
| |
| const uint32_t hostHandle = gralloc->getHostHandle(ahw); |
| if (gralloc->getFormat(ahw) == AHARDWAREBUFFER_FORMAT_BLOB && |
| !gralloc->treatBlobAsImage()) { |
| importBufferInfo.buffer = hostHandle; |
| vk_append_struct(&structChainIter, &importBufferInfo); |
| } else { |
| importCbInfo.colorBuffer = hostHandle; |
| vk_append_struct(&structChainIter, &importCbInfo); |
| } |
| } |
| #endif |
| zx_handle_t vmo_handle = ZX_HANDLE_INVALID; |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| if (importBufferCollection) { |
| const auto& collection = |
| *reinterpret_cast<fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>*>( |
| importBufferCollectionInfoPtr->collection); |
| auto result = collection->WaitForBuffersAllocated(); |
| if (!result.ok() || result->status != ZX_OK) { |
| mesa_loge("WaitForBuffersAllocated failed: %d %d", result.status(), |
| GET_STATUS_SAFE(result, status)); |
| _RETURN_FAILURE_WITH_DEVICE_MEMORY_REPORT(VK_ERROR_INITIALIZATION_FAILED); |
| } |
| fuchsia_sysmem::wire::BufferCollectionInfo2& info = result->buffer_collection_info; |
| uint32_t index = importBufferCollectionInfoPtr->index; |
| if (info.buffer_count < index) { |
| mesa_loge("Invalid buffer index: %d %d", index); |
| _RETURN_FAILURE_WITH_DEVICE_MEMORY_REPORT(VK_ERROR_INITIALIZATION_FAILED); |
| } |
| vmo_handle = info.buffers[index].vmo.release(); |
| } |
| |
| if (importVmo) { |
| vmo_handle = importVmoInfoPtr->handle; |
| } |
| |
| if (exportVmo) { |
| hasDedicatedImage = |
| dedicatedAllocInfoPtr && (dedicatedAllocInfoPtr->image != VK_NULL_HANDLE); |
| hasDedicatedBuffer = |
| dedicatedAllocInfoPtr && (dedicatedAllocInfoPtr->buffer != VK_NULL_HANDLE); |
| |
| if (hasDedicatedImage && hasDedicatedBuffer) { |
| mesa_loge( |
| "Invalid VkMemoryDedicatedAllocationInfo: At least one " |
| "of image and buffer must be VK_NULL_HANDLE."); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| |
| const VkImageCreateInfo* pImageCreateInfo = nullptr; |
| |
| VkBufferConstraintsInfoFUCHSIA bufferConstraintsInfo = { |
| .sType = VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIA, |
| .pNext = nullptr, |
| .createInfo = {}, |
| .requiredFormatFeatures = 0, |
| .bufferCollectionConstraints = |
| VkBufferCollectionConstraintsInfoFUCHSIA{ |
| .sType = VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CONSTRAINTS_INFO_FUCHSIA, |
| .pNext = nullptr, |
| .minBufferCount = 1, |
| .maxBufferCount = 0, |
| .minBufferCountForCamping = 0, |
| .minBufferCountForDedicatedSlack = 0, |
| .minBufferCountForSharedSlack = 0, |
| }, |
| }; |
| const VkBufferConstraintsInfoFUCHSIA* pBufferConstraintsInfo = nullptr; |
| |
| if (hasDedicatedImage) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkImage.find(dedicatedAllocInfoPtr->image); |
| if (it == info_VkImage.end()) return VK_ERROR_INITIALIZATION_FAILED; |
| const auto& imageInfo = it->second; |
| |
| pImageCreateInfo = &imageInfo.createInfo; |
| } |
| |
| if (hasDedicatedBuffer) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkBuffer.find(dedicatedAllocInfoPtr->buffer); |
| if (it == info_VkBuffer.end()) return VK_ERROR_INITIALIZATION_FAILED; |
| const auto& bufferInfo = it->second; |
| |
| bufferConstraintsInfo.createInfo = bufferInfo.createInfo; |
| pBufferConstraintsInfo = &bufferConstraintsInfo; |
| } |
| |
| hasDedicatedImage = |
| hasDedicatedImage && getBufferCollectionConstraintsVulkanImageUsage(pImageCreateInfo); |
| hasDedicatedBuffer = hasDedicatedBuffer && getBufferCollectionConstraintsVulkanBufferUsage( |
| pBufferConstraintsInfo); |
| |
| if (hasDedicatedImage || hasDedicatedBuffer) { |
| auto token_ends = fidl::CreateEndpoints<::fuchsia_sysmem::BufferCollectionToken>(); |
| if (!token_ends.is_ok()) { |
| mesa_loge("zx_channel_create failed: %d", token_ends.status_value()); |
| abort(); |
| } |
| |
| { |
| auto result = |
| mSysmemAllocator->AllocateSharedCollection(std::move(token_ends->server)); |
| if (!result.ok()) { |
| mesa_loge("AllocateSharedCollection failed: %d", result.status()); |
| abort(); |
| } |
| } |
| |
| auto collection_ends = fidl::CreateEndpoints<::fuchsia_sysmem::BufferCollection>(); |
| if (!collection_ends.is_ok()) { |
| mesa_loge("zx_channel_create failed: %d", collection_ends.status_value()); |
| abort(); |
| } |
| |
| { |
| auto result = mSysmemAllocator->BindSharedCollection( |
| std::move(token_ends->client), std::move(collection_ends->server)); |
| if (!result.ok()) { |
| mesa_loge("BindSharedCollection failed: %d", result.status()); |
| abort(); |
| } |
| } |
| |
| fidl::WireSyncClient<fuchsia_sysmem::BufferCollection> collection( |
| std::move(collection_ends->client)); |
| if (hasDedicatedImage) { |
| // TODO(fxbug.dev/42172354): Use setBufferCollectionImageConstraintsFUCHSIA. |
| VkResult res = setBufferCollectionConstraintsFUCHSIA(enc, device, &collection, |
| pImageCreateInfo); |
| if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) { |
| mesa_loge("setBufferCollectionConstraints failed: format %u is not supported", |
| pImageCreateInfo->format); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| if (res != VK_SUCCESS) { |
| mesa_loge("setBufferCollectionConstraints failed: %d", res); |
| abort(); |
| } |
| } |
| |
| if (hasDedicatedBuffer) { |
| VkResult res = setBufferCollectionBufferConstraintsFUCHSIA(&collection, |
| pBufferConstraintsInfo); |
| if (res != VK_SUCCESS) { |
| mesa_loge("setBufferCollectionBufferConstraints failed: %d", res); |
| abort(); |
| } |
| } |
| |
| { |
| auto result = collection->WaitForBuffersAllocated(); |
| if (result.ok() && result->status == ZX_OK) { |
| fuchsia_sysmem::wire::BufferCollectionInfo2& info = |
| result->buffer_collection_info; |
| if (!info.buffer_count) { |
| mesa_loge( |
| "WaitForBuffersAllocated returned " |
| "invalid count: %d", |
| info.buffer_count); |
| abort(); |
| } |
| vmo_handle = info.buffers[0].vmo.release(); |
| } else { |
| mesa_loge("WaitForBuffersAllocated failed: %d %d", result.status(), |
| GET_STATUS_SAFE(result, status)); |
| abort(); |
| } |
| } |
| |
| collection->Close(); |
| |
| zx::vmo vmo_copy; |
| zx_status_t status = zx_handle_duplicate(vmo_handle, ZX_RIGHT_SAME_RIGHTS, |
| vmo_copy.reset_and_get_address()); |
| if (status != ZX_OK) { |
| mesa_loge("Failed to duplicate VMO: %d", status); |
| abort(); |
| } |
| |
| if (pImageCreateInfo) { |
| // Only device-local images need to create color buffer; for |
| // host-visible images, the color buffer is already created |
| // when sysmem allocates memory. Here we use the |tiling| |
| // field of image creation info to determine if it uses |
| // host-visible memory. |
| bool isLinear = pImageCreateInfo->tiling == VK_IMAGE_TILING_LINEAR; |
| if (!isLinear) { |
| fuchsia_hardware_goldfish::wire::ColorBufferFormatType format; |
| switch (pImageCreateInfo->format) { |
| case VK_FORMAT_B8G8R8A8_SINT: |
| case VK_FORMAT_B8G8R8A8_UNORM: |
| case VK_FORMAT_B8G8R8A8_SRGB: |
| case VK_FORMAT_B8G8R8A8_SNORM: |
| case VK_FORMAT_B8G8R8A8_SSCALED: |
| case VK_FORMAT_B8G8R8A8_USCALED: |
| format = fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kBgra; |
| break; |
| case VK_FORMAT_R8G8B8A8_SINT: |
| case VK_FORMAT_R8G8B8A8_UNORM: |
| case VK_FORMAT_R8G8B8A8_SRGB: |
| case VK_FORMAT_R8G8B8A8_SNORM: |
| case VK_FORMAT_R8G8B8A8_SSCALED: |
| case VK_FORMAT_R8G8B8A8_USCALED: |
| format = fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kRgba; |
| break; |
| case VK_FORMAT_R8_UNORM: |
| case VK_FORMAT_R8_UINT: |
| case VK_FORMAT_R8_USCALED: |
| case VK_FORMAT_R8_SNORM: |
| case VK_FORMAT_R8_SINT: |
| case VK_FORMAT_R8_SSCALED: |
| case VK_FORMAT_R8_SRGB: |
| format = |
| fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kLuminance; |
| break; |
| case VK_FORMAT_R8G8_UNORM: |
| case VK_FORMAT_R8G8_UINT: |
| case VK_FORMAT_R8G8_USCALED: |
| case VK_FORMAT_R8G8_SNORM: |
| case VK_FORMAT_R8G8_SINT: |
| case VK_FORMAT_R8G8_SSCALED: |
| case VK_FORMAT_R8G8_SRGB: |
| format = fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kRg; |
| break; |
| default: |
| mesa_loge("Unsupported format: %d", pImageCreateInfo->format); |
| abort(); |
| } |
| |
| fidl::Arena arena; |
| fuchsia_hardware_goldfish::wire::CreateColorBuffer2Params createParams(arena); |
| createParams.set_width(pImageCreateInfo->extent.width) |
| .set_height(pImageCreateInfo->extent.height) |
| .set_format(format) |
| .set_memory_property( |
| fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal); |
| |
| auto result = mControlDevice->CreateColorBuffer2(std::move(vmo_copy), |
| std::move(createParams)); |
| if (!result.ok() || result->res != ZX_OK) { |
| if (result.ok() && result->res == ZX_ERR_ALREADY_EXISTS) { |
| mesa_logd( |
| "CreateColorBuffer: color buffer already " |
| "exists\n"); |
| } else { |
| mesa_loge("CreateColorBuffer failed: %d:%d", result.status(), |
| GET_STATUS_SAFE(result, res)); |
| abort(); |
| } |
| } |
| } |
| } |
| |
| if (pBufferConstraintsInfo) { |
| fidl::Arena arena; |
| fuchsia_hardware_goldfish::wire::CreateBuffer2Params createParams(arena); |
| createParams.set_size(arena, pBufferConstraintsInfo->createInfo.size) |
| .set_memory_property( |
| fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal); |
| |
| auto result = |
| mControlDevice->CreateBuffer2(std::move(vmo_copy), std::move(createParams)); |
| if (!result.ok() || result->is_error()) { |
| mesa_loge("CreateBuffer2 failed: %d:%d", result.status(), |
| GET_STATUS_SAFE(result, error_value())); |
| abort(); |
| } |
| } |
| } else { |
| mesa_logw( |
| "Dedicated image / buffer not available. Cannot create " |
| "BufferCollection to export VMOs."); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| } |
| |
| if (vmo_handle != ZX_HANDLE_INVALID) { |
| zx::vmo vmo_copy; |
| zx_status_t status = |
| zx_handle_duplicate(vmo_handle, ZX_RIGHT_SAME_RIGHTS, vmo_copy.reset_and_get_address()); |
| if (status != ZX_OK) { |
| mesa_loge("Failed to duplicate VMO: %d", status); |
| abort(); |
| } |
| zx_status_t status2 = ZX_OK; |
| |
| auto result = mControlDevice->GetBufferHandle(std::move(vmo_copy)); |
| if (!result.ok() || result->res != ZX_OK) { |
| mesa_loge("GetBufferHandle failed: %d:%d", result.status(), |
| GET_STATUS_SAFE(result, res)); |
| } else { |
| fuchsia_hardware_goldfish::wire::BufferHandleType handle_type = result->type; |
| uint32_t buffer_handle = result->id; |
| |
| if (handle_type == fuchsia_hardware_goldfish::wire::BufferHandleType::kBuffer) { |
| importBufferInfo.buffer = buffer_handle; |
| vk_append_struct(&structChainIter, &importBufferInfo); |
| } else { |
| importCbInfo.colorBuffer = buffer_handle; |
| vk_append_struct(&structChainIter, &importCbInfo); |
| } |
| } |
| } |
| #endif |
| |
| VirtGpuResourcePtr bufferBlob = nullptr; |
| #if defined(LINUX_GUEST_BUILD) |
| if (exportDmabuf) { |
| VirtGpuDevice* instance = VirtGpuDevice::getInstance(); |
| hasDedicatedImage = |
| dedicatedAllocInfoPtr && (dedicatedAllocInfoPtr->image != VK_NULL_HANDLE); |
| hasDedicatedBuffer = |
| dedicatedAllocInfoPtr && (dedicatedAllocInfoPtr->buffer != VK_NULL_HANDLE); |
| |
| if (hasDedicatedImage) { |
| VkImageCreateInfo imageCreateInfo; |
| bool isDmaBufImage = false; |
| { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkImage.find(dedicatedAllocInfoPtr->image); |
| if (it == info_VkImage.end()) return VK_ERROR_INITIALIZATION_FAILED; |
| const auto& imageInfo = it->second; |
| |
| imageCreateInfo = imageInfo.createInfo; |
| isDmaBufImage = imageInfo.isDmaBufImage; |
| } |
| |
| // TODO (b/326956485): Support DRM format modifiers for dmabuf memory |
| // For now, can only externalize memory for linear images |
| if (isDmaBufImage) { |
| const VkImageSubresource imageSubresource = { |
| .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| .mipLevel = 0, |
| .arrayLayer = 0, |
| }; |
| VkSubresourceLayout subResourceLayout; |
| on_vkGetImageSubresourceLayout(context, device, dedicatedAllocInfoPtr->image, |
| &imageSubresource, &subResourceLayout); |
| if (!subResourceLayout.rowPitch) { |
| mesa_loge("Failed to query stride for VirtGpu resource creation."); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| uint32_t virglFormat = gfxstream::vk::getVirglFormat(imageCreateInfo.format); |
| if (!virglFormat) { |
| mesa_loge("Unsupported VK format for VirtGpu resource, vkFormat: 0x%x", |
| imageCreateInfo.format); |
| return VK_ERROR_FORMAT_NOT_SUPPORTED; |
| } |
| const uint32_t target = PIPE_TEXTURE_2D; |
| uint32_t bind = VIRGL_BIND_RENDER_TARGET; |
| if (VK_IMAGE_TILING_LINEAR == imageCreateInfo.tiling) { |
| bind |= VIRGL_BIND_LINEAR; |
| } |
| |
| if (mCaps.vulkanCapset.alwaysBlob) { |
| struct gfxstreamResourceCreate3d create3d = {}; |
| struct VirtGpuExecBuffer exec = {}; |
| struct gfxstreamPlaceholderCommandVk placeholderCmd = {}; |
| struct VirtGpuCreateBlob createBlob = {}; |
| |
| create3d.hdr.opCode = GFXSTREAM_RESOURCE_CREATE_3D; |
| create3d.bind = bind; |
| create3d.target = target; |
| create3d.format = virglFormat; |
| create3d.width = imageCreateInfo.extent.width; |
| create3d.height = imageCreateInfo.extent.height; |
| create3d.blobId = ++mBlobId; |
| |
| createBlob.blobCmd = reinterpret_cast<uint8_t*>(&create3d); |
| createBlob.blobCmdSize = sizeof(create3d); |
| createBlob.blobMem = kBlobMemHost3d; |
| createBlob.flags = kBlobFlagShareable | kBlobFlagCrossDevice; |
| createBlob.blobId = create3d.blobId; |
| createBlob.size = finalAllocInfo.allocationSize; |
| |
| bufferBlob = instance->createBlob(createBlob); |
| if (!bufferBlob) return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| |
| placeholderCmd.hdr.opCode = GFXSTREAM_PLACEHOLDER_COMMAND_VK; |
| exec.command = static_cast<void*>(&placeholderCmd); |
| exec.command_size = sizeof(placeholderCmd); |
| exec.flags = kRingIdx; |
| exec.ring_idx = 1; |
| if (instance->execBuffer(exec, bufferBlob.get())) { |
| mesa_loge("Failed to execbuffer placeholder command."); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| if (bufferBlob->wait()) { |
| mesa_loge("Failed to wait for blob."); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| } else { |
| bufferBlob = instance->createResource( |
| imageCreateInfo.extent.width, imageCreateInfo.extent.height, |
| subResourceLayout.rowPitch, virglFormat, target, bind); |
| if (!bufferBlob) { |
| mesa_loge("Failed to create colorBuffer resource for Image memory"); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| if (!bufferBlob->wait()) { |
| mesa_loge("Failed to wait for colorBuffer resource for Image memory"); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| } |
| } else { |
| mesa_logw( |
| "The VkMemoryDedicatedAllocateInfo::image associated with VkDeviceMemory " |
| "allocation cannot be used to create exportable resource " |
| "(VkExportMemoryAllocateInfo).\n"); |
| } |
| } else if (hasDedicatedBuffer) { |
| uint32_t virglFormat = VIRGL_FORMAT_R8_UNORM; |
| const uint32_t target = PIPE_BUFFER; |
| uint32_t bind = VIRGL_BIND_LINEAR; |
| uint32_t width = finalAllocInfo.allocationSize; |
| uint32_t height = 1; |
| |
| if (mCaps.vulkanCapset.alwaysBlob) { |
| struct gfxstreamResourceCreate3d create3d = {}; |
| struct VirtGpuExecBuffer exec = {}; |
| struct gfxstreamPlaceholderCommandVk placeholderCmd = {}; |
| struct VirtGpuCreateBlob createBlob = {}; |
| |
| create3d.hdr.opCode = GFXSTREAM_RESOURCE_CREATE_3D; |
| create3d.bind = bind; |
| create3d.target = target; |
| create3d.format = virglFormat; |
| create3d.width = width; |
| create3d.height = height; |
| create3d.blobId = ++mBlobId; |
| |
| createBlob.blobCmd = reinterpret_cast<uint8_t*>(&create3d); |
| createBlob.blobCmdSize = sizeof(create3d); |
| createBlob.blobMem = kBlobMemHost3d; |
| createBlob.flags = kBlobFlagShareable | kBlobFlagCrossDevice; |
| createBlob.blobId = create3d.blobId; |
| createBlob.size = width; |
| |
| bufferBlob = instance->createBlob(createBlob); |
| if (!bufferBlob) return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| |
| placeholderCmd.hdr.opCode = GFXSTREAM_PLACEHOLDER_COMMAND_VK; |
| exec.command = static_cast<void*>(&placeholderCmd); |
| exec.command_size = sizeof(placeholderCmd); |
| exec.flags = kRingIdx; |
| exec.ring_idx = 1; |
| if (instance->execBuffer(exec, bufferBlob.get())) { |
| mesa_loge("Failed to allocate coherent memory: failed to execbuffer for wait."); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| bufferBlob->wait(); |
| } else { |
| bufferBlob = |
| instance->createResource(width, height, width, virglFormat, target, bind); |
| if (!bufferBlob) { |
| mesa_loge("Failed to create colorBuffer resource for Image memory"); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| if (!bufferBlob->wait()) { |
| mesa_loge("Failed to wait for colorBuffer resource for Image memory"); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| } |
| } else { |
| mesa_logw( |
| "VkDeviceMemory is not exportable (VkExportMemoryAllocateInfo). Requires " |
| "VkMemoryDedicatedAllocateInfo::image to create external resource."); |
| } |
| } |
| |
| if (importDmabuf) { |
| VirtGpuExternalHandle importHandle = {}; |
| importHandle.osHandle = importFdInfoPtr->fd; |
| importHandle.type = kMemHandleDmabuf; |
| |
| auto instance = VirtGpuDevice::getInstance(); |
| bufferBlob = instance->importBlob(importHandle); |
| if (!bufferBlob) { |
| mesa_loge("%s: Failed to import colorBuffer resource\n", __func__); |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| } |
| |
| if (bufferBlob) { |
| if (hasDedicatedBuffer) { |
| importBufferInfo.buffer = bufferBlob->getResourceHandle(); |
| vk_append_struct(&structChainIter, &importBufferInfo); |
| } else { |
| importCbInfo.colorBuffer = bufferBlob->getResourceHandle(); |
| vk_append_struct(&structChainIter, &importCbInfo); |
| } |
| } |
| #endif |
| |
| if (ahw || bufferBlob || !requestedMemoryIsHostVisible) { |
| input_result = |
| enc->vkAllocateMemory(device, &finalAllocInfo, pAllocator, pMemory, true /* do lock */); |
| |
| if (input_result != VK_SUCCESS) _RETURN_FAILURE_WITH_DEVICE_MEMORY_REPORT(input_result); |
| |
| VkDeviceSize allocationSize = finalAllocInfo.allocationSize; |
| setDeviceMemoryInfo(device, *pMemory, 0, nullptr, finalAllocInfo.memoryTypeIndex, ahw, |
| isImport, vmo_handle, bufferBlob); |
| |
| _RETURN_SCUCCESS_WITH_DEVICE_MEMORY_REPORT; |
| } |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| if (vmo_handle != ZX_HANDLE_INVALID) { |
| input_result = |
| enc->vkAllocateMemory(device, &finalAllocInfo, pAllocator, pMemory, true /* do lock */); |
| |
| // Get VMO handle rights, and only use allowed rights to map the |
| // host memory. |
| zx_info_handle_basic handle_info; |
| zx_status_t status = zx_object_get_info(vmo_handle, ZX_INFO_HANDLE_BASIC, &handle_info, |
| sizeof(handle_info), nullptr, nullptr); |
| if (status != ZX_OK) { |
| mesa_loge("%s: cannot get vmo object info: vmo = %u status: %d.", __func__, vmo_handle, |
| status); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| zx_vm_option_t vm_permission = 0u; |
| vm_permission |= (handle_info.rights & ZX_RIGHT_READ) ? ZX_VM_PERM_READ : 0; |
| vm_permission |= (handle_info.rights & ZX_RIGHT_WRITE) ? ZX_VM_PERM_WRITE : 0; |
| |
| zx_paddr_t addr; |
| status = zx_vmar_map(zx_vmar_root_self(), vm_permission, 0, vmo_handle, 0, |
| finalAllocInfo.allocationSize, &addr); |
| if (status != ZX_OK) { |
| mesa_loge("%s: cannot map vmar: status %d.", __func__, status); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| setDeviceMemoryInfo(device, *pMemory, finalAllocInfo.allocationSize, |
| reinterpret_cast<uint8_t*>(addr), finalAllocInfo.memoryTypeIndex, |
| /*ahw=*/nullptr, isImport, vmo_handle, /*blobPtr=*/nullptr); |
| return VK_SUCCESS; |
| } |
| #endif |
| |
| // Host visible memory with direct mapping |
| VkResult result = getCoherentMemory(&finalAllocInfo, enc, device, pMemory); |
| if (result != VK_SUCCESS) return result; |
| |
| _RETURN_SCUCCESS_WITH_DEVICE_MEMORY_REPORT; |
| } |
| |
| void ResourceTracker::on_vkFreeMemory(void* context, VkDevice device, VkDeviceMemory memory, |
| const VkAllocationCallbacks* pAllocateInfo) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkDeviceMemory.find(memory); |
| if (it == info_VkDeviceMemory.end()) return; |
| auto& info = it->second; |
| uint64_t memoryObjectId = (uint64_t)(void*)memory; |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| if (info.ahw) { |
| memoryObjectId = getAHardwareBufferId(info.ahw); |
| } |
| #endif |
| |
| emitDeviceMemoryReport(info_VkDevice[device], |
| info.imported ? VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_UNIMPORT_EXT |
| : VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_FREE_EXT, |
| memoryObjectId, 0 /* size */, VK_OBJECT_TYPE_DEVICE_MEMORY, |
| (uint64_t)(void*)memory); |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| if (info.vmoHandle && info.ptr) { |
| zx_status_t status = zx_vmar_unmap( |
| zx_vmar_root_self(), reinterpret_cast<zx_paddr_t>(info.ptr), info.allocationSize); |
| if (status != ZX_OK) { |
| mesa_loge("%s: Cannot unmap ptr: status %d", status); |
| } |
| info.ptr = nullptr; |
| } |
| #endif |
| |
| if (!info.coherentMemory) { |
| lock.unlock(); |
| VkEncoder* enc = (VkEncoder*)context; |
| enc->vkFreeMemory(device, memory, pAllocateInfo, true /* do lock */); |
| return; |
| } |
| |
| auto coherentMemory = freeCoherentMemoryLocked(memory, info); |
| |
| // We have to release the lock before we could possibly free a |
| // CoherentMemory, because that will call into VkEncoder, which |
| // shouldn't be called when the lock is held. |
| lock.unlock(); |
| coherentMemory = nullptr; |
| } |
| |
| VkResult ResourceTracker::on_vkMapMemory(void* context, VkResult host_result, VkDevice device, |
| VkDeviceMemory memory, VkDeviceSize offset, |
| VkDeviceSize size, VkMemoryMapFlags, void** ppData) { |
| if (host_result != VK_SUCCESS) { |
| mesa_loge("%s: Host failed to map", __func__); |
| return host_result; |
| } |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto deviceMemoryInfoIt = info_VkDeviceMemory.find(memory); |
| if (deviceMemoryInfoIt == info_VkDeviceMemory.end()) { |
| mesa_loge("%s: Failed to find VkDeviceMemory.", __func__); |
| return VK_ERROR_MEMORY_MAP_FAILED; |
| } |
| auto& deviceMemoryInfo = deviceMemoryInfoIt->second; |
| |
| if (deviceMemoryInfo.blobId && !deviceMemoryInfo.coherentMemory && |
| !mCaps.params[kParamCreateGuestHandle]) { |
| // NOTE: must not hold lock while calling into the encoder. |
| lock.unlock(); |
| VkEncoder* enc = (VkEncoder*)context; |
| VkResult vkResult = enc->vkGetBlobGOOGLE(device, memory, /*doLock*/ false); |
| if (vkResult != VK_SUCCESS) { |
| mesa_loge("%s: Failed to vkGetBlobGOOGLE().", __func__); |
| return vkResult; |
| } |
| lock.lock(); |
| |
| // NOTE: deviceMemoryInfoIt potentially invalidated but deviceMemoryInfo still okay. |
| |
| struct VirtGpuCreateBlob createBlob = {}; |
| createBlob.blobMem = kBlobMemHost3d; |
| createBlob.flags = kBlobFlagMappable; |
| createBlob.blobId = deviceMemoryInfo.blobId; |
| createBlob.size = deviceMemoryInfo.coherentMemorySize; |
| |
| auto blob = VirtGpuDevice::getInstance()->createBlob(createBlob); |
| if (!blob) return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| |
| VirtGpuResourceMappingPtr mapping = blob->createMapping(); |
| if (!mapping) return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| |
| auto coherentMemory = |
| std::make_shared<CoherentMemory>(mapping, createBlob.size, device, memory); |
| |
| uint8_t* ptr; |
| uint64_t offset; |
| coherentMemory->subAllocate(deviceMemoryInfo.allocationSize, &ptr, offset); |
| |
| deviceMemoryInfo.coherentMemoryOffset = offset; |
| deviceMemoryInfo.coherentMemory = coherentMemory; |
| deviceMemoryInfo.ptr = ptr; |
| } |
| |
| if (!deviceMemoryInfo.ptr) { |
| mesa_loge("%s: VkDeviceMemory has nullptr.", __func__); |
| return VK_ERROR_MEMORY_MAP_FAILED; |
| } |
| |
| if (size != VK_WHOLE_SIZE && (deviceMemoryInfo.ptr + offset + size > |
| deviceMemoryInfo.ptr + deviceMemoryInfo.allocationSize)) { |
| mesa_loge( |
| "%s: size is too big. alloc size 0x%llx while we wanted offset 0x%llx size 0x%llx " |
| "total 0x%llx", |
| __func__, (unsigned long long)deviceMemoryInfo.allocationSize, |
| (unsigned long long)offset, (unsigned long long)size, (unsigned long long)offset); |
| return VK_ERROR_MEMORY_MAP_FAILED; |
| } |
| |
| *ppData = deviceMemoryInfo.ptr + offset; |
| |
| return host_result; |
| } |
| |
| void ResourceTracker::on_vkUnmapMemory(void*, VkDevice, VkDeviceMemory) { |
| // no-op |
| } |
| |
| void ResourceTracker::transformImageMemoryRequirements2ForGuest(VkImage image, |
| VkMemoryRequirements2* reqs2) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkImage.find(image); |
| if (it == info_VkImage.end()) return; |
| |
| auto& info = it->second; |
| |
| if (!info.external || !info.externalCreateInfo.handleTypes) { |
| transformImageMemoryRequirementsForGuestLocked(image, &reqs2->memoryRequirements); |
| return; |
| } |
| |
| transformImageMemoryRequirementsForGuestLocked(image, &reqs2->memoryRequirements); |
| |
| VkMemoryDedicatedRequirements* dedicatedReqs = |
| vk_find_struct<VkMemoryDedicatedRequirements>(reqs2); |
| |
| if (!dedicatedReqs) return; |
| |
| transformExternalResourceMemoryDedicatedRequirementsForGuest(dedicatedReqs); |
| } |
| |
| void ResourceTracker::transformBufferMemoryRequirements2ForGuest(VkBuffer buffer, |
| VkMemoryRequirements2* reqs2) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkBuffer.find(buffer); |
| if (it == info_VkBuffer.end()) return; |
| |
| auto& info = it->second; |
| |
| if (!info.external || !info.externalCreateInfo.handleTypes) { |
| return; |
| } |
| |
| VkMemoryDedicatedRequirements* dedicatedReqs = |
| vk_find_struct<VkMemoryDedicatedRequirements>(reqs2); |
| |
| if (!dedicatedReqs) return; |
| |
| transformExternalResourceMemoryDedicatedRequirementsForGuest(dedicatedReqs); |
| } |
| |
| VkResult ResourceTracker::on_vkCreateImage(void* context, VkResult, VkDevice device, |
| const VkImageCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkImage* pImage) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| VkImageCreateInfo localCreateInfo = vk_make_orphan_copy(*pCreateInfo); |
| if (localCreateInfo.sharingMode != VK_SHARING_MODE_CONCURRENT) { |
| localCreateInfo.queueFamilyIndexCount = 0; |
| localCreateInfo.pQueueFamilyIndices = nullptr; |
| } |
| |
| vk_struct_chain_iterator structChainIter = vk_make_chain_iterator(&localCreateInfo); |
| VkExternalMemoryImageCreateInfo localExtImgCi; |
| |
| const VkExternalMemoryImageCreateInfo* extImgCiPtr = |
| vk_find_struct<VkExternalMemoryImageCreateInfo>(pCreateInfo); |
| |
| if (extImgCiPtr) { |
| localExtImgCi = vk_make_orphan_copy(*extImgCiPtr); |
| vk_append_struct(&structChainIter, &localExtImgCi); |
| } |
| |
| #if defined(LINUX_GUEST_BUILD) |
| bool isDmaBufImage = false; |
| if (extImgCiPtr && |
| (extImgCiPtr->handleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT)) { |
| const wsi_image_create_info* wsiImageCi = |
| vk_find_struct<wsi_image_create_info>(pCreateInfo); |
| if (wsiImageCi) { |
| if (!wsiImageCi->scanout) { |
| mesa_logd( |
| "gfxstream only supports native DRM image scanout path for Linux WSI " |
| "(wsi_image_create_info::scanout)"); |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| // Linux WSI creates swapchain images with VK_IMAGE_CREATE_ALIAS_BIT. Vulkan spec |
| // states: "If the pNext chain includes a VkExternalMemoryImageCreateInfo or |
| // VkExternalMemoryImageCreateInfoNV structure whose handleTypes member is not 0, it is |
| // as if VK_IMAGE_CREATE_ALIAS_BIT is set." To avoid flag mismatches on host driver, |
| // remove the VK_IMAGE_CREATE_ALIAS_BIT here. |
| localCreateInfo.flags &= ~VK_IMAGE_CREATE_ALIAS_BIT; |
| // TODO (b/326956485): DRM format modifiers to support client/compositor awareness |
| // For now, override WSI images to use linear tiling, as compositor will default to |
| // DRM_FORMAT_MOD_LINEAR. |
| localCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; |
| } |
| isDmaBufImage = true; |
| } |
| #endif |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| VkNativeBufferANDROID localAnb; |
| const VkNativeBufferANDROID* anbInfoPtr = vk_find_struct<VkNativeBufferANDROID>(pCreateInfo); |
| if (anbInfoPtr) { |
| localAnb = vk_make_orphan_copy(*anbInfoPtr); |
| vk_append_struct(&structChainIter, &localAnb); |
| } |
| |
| VkExternalFormatANDROID localExtFormatAndroid; |
| const VkExternalFormatANDROID* extFormatAndroidPtr = |
| vk_find_struct<VkExternalFormatANDROID>(pCreateInfo); |
| if (extFormatAndroidPtr) { |
| localExtFormatAndroid = vk_make_orphan_copy(*extFormatAndroidPtr); |
| |
| // Do not append external format android; |
| // instead, replace the local image localCreateInfo format |
| // with the corresponding Vulkan format |
| if (extFormatAndroidPtr->externalFormat) { |
| localCreateInfo.format = vk_format_from_fourcc(extFormatAndroidPtr->externalFormat); |
| if (localCreateInfo.format == VK_FORMAT_UNDEFINED) |
| return VK_ERROR_VALIDATION_FAILED_EXT; |
| } |
| } |
| #endif |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| const VkBufferCollectionImageCreateInfoFUCHSIA* extBufferCollectionPtr = |
| vk_find_struct<VkBufferCollectionImageCreateInfoFUCHSIA>(pCreateInfo); |
| |
| bool isSysmemBackedMemory = false; |
| |
| if (extImgCiPtr && |
| (extImgCiPtr->handleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA)) { |
| isSysmemBackedMemory = true; |
| } |
| |
| if (extBufferCollectionPtr) { |
| const auto& collection = |
| *reinterpret_cast<fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>*>( |
| extBufferCollectionPtr->collection); |
| uint32_t index = extBufferCollectionPtr->index; |
| zx::vmo vmo; |
| |
| fuchsia_sysmem::wire::BufferCollectionInfo2 info; |
| |
| auto result = collection->WaitForBuffersAllocated(); |
| if (result.ok() && result->status == ZX_OK) { |
| info = std::move(result->buffer_collection_info); |
| if (index < info.buffer_count && info.settings.has_image_format_constraints) { |
| vmo = std::move(info.buffers[index].vmo); |
| } |
| } else { |
| mesa_loge("WaitForBuffersAllocated failed: %d %d", result.status(), |
| GET_STATUS_SAFE(result, status)); |
| } |
| |
| if (vmo.is_valid()) { |
| zx::vmo vmo_dup; |
| if (zx_status_t status = vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_dup); |
| status != ZX_OK) { |
| mesa_loge("%s: zx_vmo_duplicate failed: %d", __func__, status); |
| abort(); |
| } |
| |
| auto buffer_handle_result = mControlDevice->GetBufferHandle(std::move(vmo_dup)); |
| if (!buffer_handle_result.ok()) { |
| mesa_loge("%s: GetBufferHandle FIDL error: %d", __func__, |
| buffer_handle_result.status()); |
| abort(); |
| } |
| if (buffer_handle_result.value().res == ZX_OK) { |
| // Buffer handle already exists. |
| // If it is a ColorBuffer, no-op; Otherwise return error. |
| if (buffer_handle_result.value().type != |
| fuchsia_hardware_goldfish::wire::BufferHandleType::kColorBuffer) { |
| mesa_loge("%s: BufferHandle %u is not a ColorBuffer", __func__, |
| buffer_handle_result.value().id); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| } else if (buffer_handle_result.value().res == ZX_ERR_NOT_FOUND) { |
| // Buffer handle not found. Create ColorBuffer based on buffer settings. |
| auto format = info.settings.image_format_constraints.pixel_format.type == |
| fuchsia_sysmem::wire::PixelFormatType::kR8G8B8A8 |
| ? fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kRgba |
| : fuchsia_hardware_goldfish::wire::ColorBufferFormatType::kBgra; |
| |
| uint32_t memory_property = |
| info.settings.buffer_settings.heap == |
| fuchsia_sysmem::wire::HeapType::kGoldfishDeviceLocal |
| ? fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal |
| : fuchsia_hardware_goldfish::wire::kMemoryPropertyHostVisible; |
| |
| fidl::Arena arena; |
| fuchsia_hardware_goldfish::wire::CreateColorBuffer2Params createParams(arena); |
| createParams.set_width(info.settings.image_format_constraints.min_coded_width) |
| .set_height(info.settings.image_format_constraints.min_coded_height) |
| .set_format(format) |
| .set_memory_property(memory_property); |
| |
| auto result = |
| mControlDevice->CreateColorBuffer2(std::move(vmo), std::move(createParams)); |
| if (result.ok() && result->res == ZX_ERR_ALREADY_EXISTS) { |
| mesa_logd("CreateColorBuffer: color buffer already exists\n"); |
| } else if (!result.ok() || result->res != ZX_OK) { |
| mesa_loge("CreateColorBuffer failed: %d:%d", result.status(), |
| GET_STATUS_SAFE(result, res)); |
| } |
| } |
| |
| if (info.settings.buffer_settings.heap == |
| fuchsia_sysmem::wire::HeapType::kGoldfishHostVisible) { |
| mesa_logd( |
| "%s: Image uses host visible memory heap; set tiling " |
| "to linear to match host ImageCreateInfo", |
| __func__); |
| localCreateInfo.tiling = VK_IMAGE_TILING_LINEAR; |
| } |
| } |
| isSysmemBackedMemory = true; |
| } |
| |
| if (isSysmemBackedMemory) { |
| localCreateInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; |
| } |
| #endif |
| |
| VkResult res; |
| VkMemoryRequirements memReqs; |
| |
| if (supportsCreateResourcesWithRequirements()) { |
| res = enc->vkCreateImageWithRequirementsGOOGLE(device, &localCreateInfo, pAllocator, pImage, |
| &memReqs, true /* do lock */); |
| } else { |
| res = enc->vkCreateImage(device, &localCreateInfo, pAllocator, pImage, true /* do lock */); |
| } |
| |
| if (res != VK_SUCCESS) return res; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkImage.find(*pImage); |
| if (it == info_VkImage.end()) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| auto& info = it->second; |
| |
| info.device = device; |
| info.createInfo = *pCreateInfo; |
| info.createInfo.pNext = nullptr; |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| if (extFormatAndroidPtr && extFormatAndroidPtr->externalFormat) { |
| info.hasExternalFormat = true; |
| info.externalFourccFormat = extFormatAndroidPtr->externalFormat; |
| } |
| #endif // VK_USE_PLATFORM_ANDROID_KHR |
| |
| if (supportsCreateResourcesWithRequirements()) { |
| info.baseRequirementsKnown = true; |
| } |
| |
| if (extImgCiPtr) { |
| info.external = true; |
| info.externalCreateInfo = *extImgCiPtr; |
| } |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| if (isSysmemBackedMemory) { |
| info.isSysmemBackedMemory = true; |
| } |
| #endif |
| |
| // Delete `protocolVersion` check goldfish drivers are gone. |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) |
| if (mCaps.vulkanCapset.colorBufferMemoryIndex == 0xFFFFFFFF) { |
| mCaps.vulkanCapset.colorBufferMemoryIndex = getColorBufferMemoryIndex(context, device); |
| } |
| if ((extImgCiPtr && (extImgCiPtr->handleTypes & |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID))) { |
| updateMemoryTypeBits(&memReqs.memoryTypeBits, mCaps.vulkanCapset.colorBufferMemoryIndex); |
| } |
| #endif |
| #if defined(LINUX_GUEST_BUILD) |
| if (mCaps.vulkanCapset.colorBufferMemoryIndex == 0xFFFFFFFF) { |
| mCaps.vulkanCapset.colorBufferMemoryIndex = getColorBufferMemoryIndex(context, device); |
| } |
| info.isDmaBufImage = isDmaBufImage; |
| if (info.isDmaBufImage) { |
| updateMemoryTypeBits(&memReqs.memoryTypeBits, mCaps.vulkanCapset.colorBufferMemoryIndex); |
| if (localCreateInfo.tiling == VK_IMAGE_TILING_OPTIMAL) { |
| // Linux WSI calls vkGetImageSubresourceLayout() to query the stride for swapchain |
| // support. Similarly, stride is also queried from vkGetImageSubresourceLayout() to |
| // determine the stride for colorBuffer resource creation (guest-side dmabuf resource). |
| // To satisfy valid usage of this API, must call on the linearPeerImage for the VkImage |
| // in question. As long as these two use cases match, the rowPitch won't actually be |
| // used by WSI. |
| VkImageCreateInfo linearPeerImageCreateInfo = { |
| .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = {}, |
| .imageType = VK_IMAGE_TYPE_2D, |
| .format = localCreateInfo.format, |
| .extent = localCreateInfo.extent, |
| .mipLevels = 1, |
| .arrayLayers = 1, |
| .samples = VK_SAMPLE_COUNT_1_BIT, |
| .tiling = VK_IMAGE_TILING_LINEAR, |
| .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, |
| .sharingMode = VK_SHARING_MODE_EXCLUSIVE, |
| .queueFamilyIndexCount = 0, |
| .pQueueFamilyIndices = nullptr, |
| .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, |
| }; |
| res = enc->vkCreateImage(device, &linearPeerImageCreateInfo, pAllocator, |
| &info.linearPeerImage, true /* do lock */); |
| if (res != VK_SUCCESS) return res; |
| } |
| } |
| #endif |
| |
| if (info.baseRequirementsKnown) { |
| transformImageMemoryRequirementsForGuestLocked(*pImage, &memReqs); |
| info.baseRequirements = memReqs; |
| } |
| return res; |
| } |
| |
| VkResult ResourceTracker::on_vkCreateSamplerYcbcrConversion( |
| void* context, VkResult, VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) { |
| VkSamplerYcbcrConversionCreateInfo localCreateInfo = vk_make_orphan_copy(*pCreateInfo); |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| const VkExternalFormatANDROID* extFormatAndroidPtr = |
| vk_find_struct<VkExternalFormatANDROID>(pCreateInfo); |
| if (extFormatAndroidPtr) { |
| if (extFormatAndroidPtr->externalFormat == DRM_FORMAT_RGB565) { |
| // We don't support external formats on host and it causes RGB565 |
| // to fail in CtsGraphicsTestCases android.graphics.cts.BasicVulkanGpuTest |
| // when passed as an external format. |
| // We may consider doing this for all external formats. |
| // See b/134771579. |
| *pYcbcrConversion = VK_YCBCR_CONVERSION_DO_NOTHING; |
| return VK_SUCCESS; |
| } else if (extFormatAndroidPtr->externalFormat) { |
| localCreateInfo.format = vk_format_from_fourcc(extFormatAndroidPtr->externalFormat); |
| } |
| } |
| #endif |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| VkResult res = enc->vkCreateSamplerYcbcrConversion(device, &localCreateInfo, pAllocator, |
| pYcbcrConversion, true /* do lock */); |
| |
| if (*pYcbcrConversion == VK_YCBCR_CONVERSION_DO_NOTHING) { |
| mesa_loge( |
| "FATAL: vkCreateSamplerYcbcrConversion returned a reserved value " |
| "(VK_YCBCR_CONVERSION_DO_NOTHING)"); |
| abort(); |
| } |
| return res; |
| } |
| |
| void ResourceTracker::on_vkDestroySamplerYcbcrConversion(void* context, VkDevice device, |
| VkSamplerYcbcrConversion ycbcrConversion, |
| const VkAllocationCallbacks* pAllocator) { |
| VkEncoder* enc = (VkEncoder*)context; |
| if (ycbcrConversion != VK_YCBCR_CONVERSION_DO_NOTHING) { |
| enc->vkDestroySamplerYcbcrConversion(device, ycbcrConversion, pAllocator, |
| true /* do lock */); |
| } |
| } |
| |
| VkResult ResourceTracker::on_vkCreateSamplerYcbcrConversionKHR( |
| void* context, VkResult, VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) { |
| VkSamplerYcbcrConversionCreateInfo localCreateInfo = vk_make_orphan_copy(*pCreateInfo); |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) |
| const VkExternalFormatANDROID* extFormatAndroidPtr = |
| vk_find_struct<VkExternalFormatANDROID>(pCreateInfo); |
| if (extFormatAndroidPtr) { |
| if (extFormatAndroidPtr->externalFormat == DRM_FORMAT_RGB565) { |
| // We don't support external formats on host and it causes RGB565 |
| // to fail in CtsGraphicsTestCases android.graphics.cts.BasicVulkanGpuTest |
| // when passed as an external format. |
| // We may consider doing this for all external formats. |
| // See b/134771579. |
| *pYcbcrConversion = VK_YCBCR_CONVERSION_DO_NOTHING; |
| return VK_SUCCESS; |
| } else if (extFormatAndroidPtr->externalFormat) { |
| localCreateInfo.format = vk_format_from_fourcc(extFormatAndroidPtr->externalFormat); |
| } |
| } |
| #endif |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| VkResult res = enc->vkCreateSamplerYcbcrConversionKHR(device, &localCreateInfo, pAllocator, |
| pYcbcrConversion, true /* do lock */); |
| |
| if (*pYcbcrConversion == VK_YCBCR_CONVERSION_DO_NOTHING) { |
| mesa_loge( |
| "FATAL: vkCreateSamplerYcbcrConversionKHR returned a reserved value " |
| "(VK_YCBCR_CONVERSION_DO_NOTHING)"); |
| abort(); |
| } |
| return res; |
| } |
| |
| void ResourceTracker::on_vkDestroySamplerYcbcrConversionKHR( |
| void* context, VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, |
| const VkAllocationCallbacks* pAllocator) { |
| VkEncoder* enc = (VkEncoder*)context; |
| if (ycbcrConversion != VK_YCBCR_CONVERSION_DO_NOTHING) { |
| enc->vkDestroySamplerYcbcrConversionKHR(device, ycbcrConversion, pAllocator, |
| true /* do lock */); |
| } |
| } |
| |
| VkResult ResourceTracker::on_vkCreateSampler(void* context, VkResult, VkDevice device, |
| const VkSamplerCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkSampler* pSampler) { |
| VkSamplerCreateInfo localCreateInfo = vk_make_orphan_copy(*pCreateInfo); |
| vk_struct_chain_iterator structChainIter = vk_make_chain_iterator(&localCreateInfo); |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(VK_USE_PLATFORM_FUCHSIA) |
| VkSamplerYcbcrConversionInfo localVkSamplerYcbcrConversionInfo; |
| const VkSamplerYcbcrConversionInfo* samplerYcbcrConversionInfo = |
| vk_find_struct<VkSamplerYcbcrConversionInfo>(pCreateInfo); |
| if (samplerYcbcrConversionInfo) { |
| if (samplerYcbcrConversionInfo->conversion != VK_YCBCR_CONVERSION_DO_NOTHING) { |
| localVkSamplerYcbcrConversionInfo = vk_make_orphan_copy(*samplerYcbcrConversionInfo); |
| vk_append_struct(&structChainIter, &localVkSamplerYcbcrConversionInfo); |
| } |
| } |
| |
| VkSamplerCustomBorderColorCreateInfoEXT localVkSamplerCustomBorderColorCreateInfo; |
| const VkSamplerCustomBorderColorCreateInfoEXT* samplerCustomBorderColorCreateInfo = |
| vk_find_struct<VkSamplerCustomBorderColorCreateInfoEXT>(pCreateInfo); |
| if (samplerCustomBorderColorCreateInfo) { |
| localVkSamplerCustomBorderColorCreateInfo = |
| vk_make_orphan_copy(*samplerCustomBorderColorCreateInfo); |
| vk_append_struct(&structChainIter, &localVkSamplerCustomBorderColorCreateInfo); |
| } |
| #endif |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| return enc->vkCreateSampler(device, &localCreateInfo, pAllocator, pSampler, true /* do lock */); |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceExternalFenceProperties( |
| void* context, VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, |
| VkExternalFenceProperties* pExternalFenceProperties) { |
| (void)context; |
| (void)physicalDevice; |
| |
| pExternalFenceProperties->exportFromImportedHandleTypes = 0; |
| pExternalFenceProperties->compatibleHandleTypes = 0; |
| pExternalFenceProperties->externalFenceFeatures = 0; |
| |
| bool syncFd = pExternalFenceInfo->handleType & VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; |
| |
| if (!syncFd) { |
| return; |
| } |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| pExternalFenceProperties->exportFromImportedHandleTypes = |
| VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; |
| pExternalFenceProperties->compatibleHandleTypes = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; |
| pExternalFenceProperties->externalFenceFeatures = |
| VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT | VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT; |
| #endif |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceExternalFencePropertiesKHR( |
| void* context, VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, |
| VkExternalFenceProperties* pExternalFenceProperties) { |
| on_vkGetPhysicalDeviceExternalFenceProperties(context, physicalDevice, pExternalFenceInfo, |
| pExternalFenceProperties); |
| } |
| |
| VkResult ResourceTracker::on_vkCreateFence(void* context, VkResult input_result, VkDevice device, |
| const VkFenceCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkFence* pFence) { |
| VkEncoder* enc = (VkEncoder*)context; |
| VkFenceCreateInfo finalCreateInfo = *pCreateInfo; |
| |
| const VkExportFenceCreateInfo* exportFenceInfoPtr = |
| vk_find_struct<VkExportFenceCreateInfo>(pCreateInfo); |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| bool exportSyncFd = exportFenceInfoPtr && (exportFenceInfoPtr->handleTypes & |
| VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT); |
| #endif |
| |
| input_result = |
| enc->vkCreateFence(device, &finalCreateInfo, pAllocator, pFence, true /* do lock */); |
| |
| if (input_result != VK_SUCCESS) return input_result; |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| if (exportSyncFd) { |
| if (!mFeatureInfo->hasVirtioGpuNativeSync) { |
| mesa_logi("%s: ensure sync device\n", __func__); |
| ensureSyncDeviceFd(); |
| } |
| |
| mesa_logi("%s: getting fence info\n", __func__); |
| AutoLock<RecursiveLock> lock(mLock); |
| auto it = info_VkFence.find(*pFence); |
| |
| if (it == info_VkFence.end()) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| auto& info = it->second; |
| |
| info.external = true; |
| info.exportFenceCreateInfo = *exportFenceInfoPtr; |
| mesa_logi("%s: info set (fence still -1). fence: %p\n", __func__, (void*)(*pFence)); |
| // syncFd is still -1 because we expect user to explicitly |
| // export it via vkGetFenceFdKHR |
| } |
| #endif |
| |
| return input_result; |
| } |
| |
| void ResourceTracker::on_vkDestroyFence(void* context, VkDevice device, VkFence fence, |
| const VkAllocationCallbacks* pAllocator) { |
| VkEncoder* enc = (VkEncoder*)context; |
| enc->vkDestroyFence(device, fence, pAllocator, true /* do lock */); |
| } |
| |
| VkResult ResourceTracker::on_vkResetFences(void* context, VkResult, VkDevice device, |
| uint32_t fenceCount, const VkFence* pFences) { |
| VkEncoder* enc = (VkEncoder*)context; |
| VkResult res = enc->vkResetFences(device, fenceCount, pFences, true /* do lock */); |
| |
| if (res != VK_SUCCESS) return res; |
| |
| if (!fenceCount) return res; |
| |
| // Permanence: temporary |
| // on fence reset, close the fence fd |
| // and act like we need to GetFenceFdKHR/ImportFenceFdKHR again |
| AutoLock<RecursiveLock> lock(mLock); |
| for (uint32_t i = 0; i < fenceCount; ++i) { |
| VkFence fence = pFences[i]; |
| auto it = info_VkFence.find(fence); |
| auto& info = it->second; |
| if (!info.external) continue; |
| |
| #if GFXSTREAM_ENABLE_GUEST_GOLDFISH |
| if (info.syncFd >= 0) { |
| mesa_logi("%s: resetting fence. make fd -1\n", __func__); |
| goldfish_sync_signal(info.syncFd); |
| auto* syncHelper = |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->syncHelper(); |
| syncHelper->close(info.syncFd); |
| info.syncFd = -1; |
| } |
| #endif |
| } |
| |
| return res; |
| } |
| |
| VkResult ResourceTracker::on_vkImportFenceFdKHR(void* context, VkResult, VkDevice device, |
| const VkImportFenceFdInfoKHR* pImportFenceFdInfo) { |
| (void)context; |
| (void)device; |
| (void)pImportFenceFdInfo; |
| |
| // Transference: copy |
| // meaning dup() the incoming fd |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| bool hasFence = pImportFenceFdInfo->fence != VK_NULL_HANDLE; |
| |
| if (!hasFence) return VK_ERROR_OUT_OF_HOST_MEMORY; |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| |
| bool syncFdImport = pImportFenceFdInfo->handleType & VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; |
| |
| if (!syncFdImport) { |
| mesa_logi("%s: VK_ERROR_OUT_OF_HOST_MEMORY: no sync fd import\n", __func__); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| auto it = info_VkFence.find(pImportFenceFdInfo->fence); |
| if (it == info_VkFence.end()) { |
| mesa_logi("%s: VK_ERROR_OUT_OF_HOST_MEMORY: no fence info\n", __func__); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| auto& info = it->second; |
| |
| auto* syncHelper = ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->syncHelper(); |
| #if GFXSTREAM_ENABLE_GUEST_GOLDFISH |
| if (info.syncFd >= 0) { |
| mesa_logi("%s: previous sync fd exists, close it\n", __func__); |
| goldfish_sync_signal(info.syncFd); |
| syncHelper->close(info.syncFd); |
| } |
| #endif |
| |
| if (pImportFenceFdInfo->fd < 0) { |
| mesa_logi("%s: import -1, set to -1 and exit\n", __func__); |
| info.syncFd = -1; |
| } else { |
| mesa_logi("%s: import actual fd, dup and close()\n", __func__); |
| info.syncFd = syncHelper->dup(pImportFenceFdInfo->fd); |
| syncHelper->close(pImportFenceFdInfo->fd); |
| } |
| return VK_SUCCESS; |
| #else |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| #endif |
| } |
| |
| VkResult ResourceTracker::on_vkGetFenceFdKHR(void* context, VkResult, VkDevice device, |
| const VkFenceGetFdInfoKHR* pGetFdInfo, int* pFd) { |
| // export operation. |
| // first check if fence is signaled |
| // then if so, return -1 |
| // else, queue work |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| bool hasFence = pGetFdInfo->fence != VK_NULL_HANDLE; |
| |
| if (!hasFence) { |
| mesa_logi("%s: VK_ERROR_OUT_OF_HOST_MEMORY: no fence\n", __func__); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| bool syncFdExport = pGetFdInfo->handleType & VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; |
| |
| if (!syncFdExport) { |
| mesa_logi("%s: VK_ERROR_OUT_OF_HOST_MEMORY: no sync fd fence\n", __func__); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| VkResult currentFenceStatus = |
| enc->vkGetFenceStatus(device, pGetFdInfo->fence, true /* do lock */); |
| |
| if (VK_ERROR_DEVICE_LOST == currentFenceStatus) { // Other error |
| mesa_logi("%s: VK_ERROR_DEVICE_LOST: Other error\n", __func__); |
| *pFd = -1; |
| return VK_ERROR_DEVICE_LOST; |
| } |
| |
| if (VK_NOT_READY == currentFenceStatus || VK_SUCCESS == currentFenceStatus) { |
| // Fence is valid. We also create a new sync fd for a signaled |
| // fence, because ANGLE will use the returned fd directly to |
| // implement eglDupNativeFenceFDANDROID, where -1 is only returned |
| // when error occurs. |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkFence.find(pGetFdInfo->fence); |
| if (it == info_VkFence.end()) { |
| mesa_logi("%s: VK_ERROR_OUT_OF_HOST_MEMORY: no fence info\n", __func__); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| auto& info = it->second; |
| |
| bool syncFdCreated = info.external && (info.exportFenceCreateInfo.handleTypes & |
| VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT); |
| |
| if (!syncFdCreated) { |
| mesa_logi("%s: VK_ERROR_OUT_OF_HOST_MEMORY: no sync fd created\n", __func__); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| if (mFeatureInfo->hasVirtioGpuNativeSync) { |
| VkResult result; |
| int64_t osHandle; |
| uint64_t hostFenceHandle = get_host_u64_VkFence(pGetFdInfo->fence); |
| |
| result = createFence(device, hostFenceHandle, osHandle); |
| if (result != VK_SUCCESS) return result; |
| |
| *pFd = osHandle; |
| } else { |
| #if GFXSTREAM_ENABLE_GUEST_GOLDFISH |
| goldfish_sync_queue_work( |
| mSyncDeviceFd, get_host_u64_VkFence(pGetFdInfo->fence) /* the handle */, |
| GOLDFISH_SYNC_VULKAN_SEMAPHORE_SYNC /* thread handle (doubling as type field) */, |
| pFd); |
| #endif |
| } |
| |
| // relinquish ownership |
| info.syncFd = -1; |
| mesa_logi("%s: got fd: %d\n", __func__, *pFd); |
| return VK_SUCCESS; |
| } |
| return VK_ERROR_DEVICE_LOST; |
| #else |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| #endif |
| } |
| |
| VkResult ResourceTracker::on_vkWaitForFences(void* context, VkResult, VkDevice device, |
| uint32_t fenceCount, const VkFence* pFences, |
| VkBool32 waitAll, uint64_t timeout) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| std::vector<VkFence> fencesExternal; |
| std::vector<int> fencesExternalWaitFds; |
| std::vector<VkFence> fencesNonExternal; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| for (uint32_t i = 0; i < fenceCount; ++i) { |
| auto it = info_VkFence.find(pFences[i]); |
| if (it == info_VkFence.end()) continue; |
| const auto& info = it->second; |
| if (info.syncFd >= 0) { |
| fencesExternal.push_back(pFences[i]); |
| fencesExternalWaitFds.push_back(info.syncFd); |
| } else { |
| fencesNonExternal.push_back(pFences[i]); |
| } |
| } |
| |
| lock.unlock(); |
| |
| if (fencesExternal.empty()) { |
| // No need for work pool, just wait with host driver. |
| return enc->vkWaitForFences(device, fenceCount, pFences, waitAll, timeout, |
| true /* do lock */); |
| } else { |
| // Depending on wait any or wait all, |
| // schedule a wait group with waitAny/waitAll |
| std::vector<WorkPool::Task> tasks; |
| |
| mesa_logi("%s: scheduling ext waits\n", __func__); |
| |
| for (auto fd : fencesExternalWaitFds) { |
| mesa_logi("%s: wait on %d\n", __func__, fd); |
| tasks.push_back([fd] { |
| auto* syncHelper = |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->syncHelper(); |
| syncHelper->wait(fd, 3000); |
| mesa_logi("done waiting on fd %d\n", fd); |
| }); |
| } |
| |
| if (!fencesNonExternal.empty()) { |
| tasks.push_back( |
| [this, fencesNonExternal /* copy of vector */, device, waitAll, timeout] { |
| auto hostConn = ResourceTracker::threadingCallbacks.hostConnectionGetFunc(); |
| auto vkEncoder = ResourceTracker::threadingCallbacks.vkEncoderGetFunc(hostConn); |
| mesa_logi("%s: vkWaitForFences to host\n", __func__); |
| vkEncoder->vkWaitForFences(device, fencesNonExternal.size(), |
| fencesNonExternal.data(), waitAll, timeout, |
| true /* do lock */); |
| }); |
| } |
| |
| auto waitGroupHandle = mWorkPool.schedule(tasks); |
| |
| // Convert timeout to microseconds from nanoseconds |
| bool waitRes = false; |
| if (waitAll) { |
| waitRes = mWorkPool.waitAll(waitGroupHandle, timeout / 1000); |
| } else { |
| waitRes = mWorkPool.waitAny(waitGroupHandle, timeout / 1000); |
| } |
| |
| if (waitRes) { |
| mesa_logi("%s: VK_SUCCESS\n", __func__); |
| return VK_SUCCESS; |
| } else { |
| mesa_logi("%s: VK_TIMEOUT\n", __func__); |
| return VK_TIMEOUT; |
| } |
| } |
| #else |
| return enc->vkWaitForFences(device, fenceCount, pFences, waitAll, timeout, true /* do lock */); |
| #endif |
| } |
| |
| VkResult ResourceTracker::on_vkCreateDescriptorPool(void* context, VkResult, VkDevice device, |
| const VkDescriptorPoolCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkDescriptorPool* pDescriptorPool) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| VkResult res = enc->vkCreateDescriptorPool(device, pCreateInfo, pAllocator, pDescriptorPool, |
| true /* do lock */); |
| |
| if (res != VK_SUCCESS) return res; |
| |
| VkDescriptorPool pool = *pDescriptorPool; |
| |
| struct goldfish_VkDescriptorPool* dp = as_goldfish_VkDescriptorPool(pool); |
| dp->allocInfo = new DescriptorPoolAllocationInfo; |
| dp->allocInfo->device = device; |
| dp->allocInfo->createFlags = pCreateInfo->flags; |
| dp->allocInfo->maxSets = pCreateInfo->maxSets; |
| dp->allocInfo->usedSets = 0; |
| |
| for (uint32_t i = 0; i < pCreateInfo->poolSizeCount; ++i) { |
| dp->allocInfo->descriptorCountInfo.push_back({ |
| pCreateInfo->pPoolSizes[i].type, pCreateInfo->pPoolSizes[i].descriptorCount, |
| 0, /* used */ |
| }); |
| } |
| |
| if (mFeatureInfo->hasVulkanBatchedDescriptorSetUpdate) { |
| std::vector<uint64_t> poolIds(pCreateInfo->maxSets); |
| |
| uint32_t count = pCreateInfo->maxSets; |
| enc->vkCollectDescriptorPoolIdsGOOGLE(device, pool, &count, poolIds.data(), |
| true /* do lock */); |
| |
| dp->allocInfo->freePoolIds = poolIds; |
| } |
| |
| return res; |
| } |
| |
| void ResourceTracker::on_vkDestroyDescriptorPool(void* context, VkDevice device, |
| VkDescriptorPool descriptorPool, |
| const VkAllocationCallbacks* pAllocator) { |
| if (!descriptorPool) return; |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| clearDescriptorPoolAndUnregisterDescriptorSets(context, device, descriptorPool); |
| |
| enc->vkDestroyDescriptorPool(device, descriptorPool, pAllocator, true /* do lock */); |
| } |
| |
| VkResult ResourceTracker::on_vkResetDescriptorPool(void* context, VkResult, VkDevice device, |
| VkDescriptorPool descriptorPool, |
| VkDescriptorPoolResetFlags flags) { |
| if (!descriptorPool) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| VkResult res = enc->vkResetDescriptorPool(device, descriptorPool, flags, true /* do lock */); |
| |
| if (res != VK_SUCCESS) return res; |
| |
| clearDescriptorPoolAndUnregisterDescriptorSets(context, device, descriptorPool); |
| return res; |
| } |
| |
| VkResult ResourceTracker::on_vkAllocateDescriptorSets( |
| void* context, VkResult, VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo, |
| VkDescriptorSet* pDescriptorSets) { |
| VkEncoder* enc = (VkEncoder*)context; |
| auto ci = pAllocateInfo; |
| auto sets = pDescriptorSets; |
| if (mFeatureInfo->hasVulkanBatchedDescriptorSetUpdate) { |
| // Using the pool ID's we collected earlier from the host |
| VkResult poolAllocResult = validateAndApplyVirtualDescriptorSetAllocation(ci, sets); |
| |
| if (poolAllocResult != VK_SUCCESS) return poolAllocResult; |
| |
| for (uint32_t i = 0; i < ci->descriptorSetCount; ++i) { |
| register_VkDescriptorSet(sets[i]); |
| VkDescriptorSetLayout setLayout = |
| as_goldfish_VkDescriptorSet(sets[i])->reified->setLayout; |
| |
| // Need to add ref to the set layout in the virtual case |
| // because the set itself might not be realized on host at the |
| // same time |
| struct goldfish_VkDescriptorSetLayout* dsl = |
| as_goldfish_VkDescriptorSetLayout(setLayout); |
| ++dsl->layoutInfo->refcount; |
| } |
| } else { |
| VkResult allocRes = enc->vkAllocateDescriptorSets(device, ci, sets, true /* do lock */); |
| |
| if (allocRes != VK_SUCCESS) return allocRes; |
| |
| for (uint32_t i = 0; i < ci->descriptorSetCount; ++i) { |
| applyDescriptorSetAllocation(ci->descriptorPool, ci->pSetLayouts[i]); |
| fillDescriptorSetInfoForPool(ci->descriptorPool, ci->pSetLayouts[i], sets[i]); |
| } |
| } |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult ResourceTracker::on_vkFreeDescriptorSets(void* context, VkResult, VkDevice device, |
| VkDescriptorPool descriptorPool, |
| uint32_t descriptorSetCount, |
| const VkDescriptorSet* pDescriptorSets) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| // Bit of robustness so that we can double free descriptor sets |
| // and do other invalid usages |
| // https://github.com/KhronosGroup/Vulkan-Docs/issues/1070 |
| // (people expect VK_SUCCESS to always be returned by vkFreeDescriptorSets) |
| std::vector<VkDescriptorSet> toActuallyFree; |
| { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| // Pool was destroyed |
| if (info_VkDescriptorPool.find(descriptorPool) == info_VkDescriptorPool.end()) { |
| return VK_SUCCESS; |
| } |
| |
| if (!descriptorPoolSupportsIndividualFreeLocked(descriptorPool)) return VK_SUCCESS; |
| |
| std::vector<VkDescriptorSet> existingDescriptorSets; |
| ; |
| |
| // Check if this descriptor set was in the pool's set of allocated descriptor sets, |
| // to guard against double free (Double free is allowed by the client) |
| { |
| auto allocedSets = as_goldfish_VkDescriptorPool(descriptorPool)->allocInfo->allocedSets; |
| |
| for (uint32_t i = 0; i < descriptorSetCount; ++i) { |
| if (allocedSets.end() == allocedSets.find(pDescriptorSets[i])) { |
| mesa_logi( |
| "%s: Warning: descriptor set %p not found in pool. Was this " |
| "double-freed?\n", |
| __func__, (void*)pDescriptorSets[i]); |
| continue; |
| } |
| |
| auto it = info_VkDescriptorSet.find(pDescriptorSets[i]); |
| if (it == info_VkDescriptorSet.end()) continue; |
| |
| existingDescriptorSets.push_back(pDescriptorSets[i]); |
| } |
| } |
| |
| for (auto set : existingDescriptorSets) { |
| if (removeDescriptorSetFromPool(set, |
| mFeatureInfo->hasVulkanBatchedDescriptorSetUpdate)) { |
| toActuallyFree.push_back(set); |
| } |
| } |
| |
| if (toActuallyFree.empty()) return VK_SUCCESS; |
| } |
| |
| if (mFeatureInfo->hasVulkanBatchedDescriptorSetUpdate) { |
| // In the batched set update case, decrement refcount on the set layout |
| // and only free on host if we satisfied a pending allocation on the |
| // host. |
| for (uint32_t i = 0; i < toActuallyFree.size(); ++i) { |
| VkDescriptorSetLayout setLayout = |
| as_goldfish_VkDescriptorSet(toActuallyFree[i])->reified->setLayout; |
| decDescriptorSetLayoutRef(context, device, setLayout, nullptr); |
| } |
| freeDescriptorSetsIfHostAllocated(enc, device, (uint32_t)toActuallyFree.size(), |
| toActuallyFree.data()); |
| } else { |
| // In the non-batched set update case, just free them directly. |
| enc->vkFreeDescriptorSets(device, descriptorPool, (uint32_t)toActuallyFree.size(), |
| toActuallyFree.data(), true /* do lock */); |
| } |
| return VK_SUCCESS; |
| } |
| |
| VkResult ResourceTracker::on_vkCreateDescriptorSetLayout( |
| void* context, VkResult, VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, VkDescriptorSetLayout* pSetLayout) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| VkResult res = enc->vkCreateDescriptorSetLayout(device, pCreateInfo, pAllocator, pSetLayout, |
| true /* do lock */); |
| |
| if (res != VK_SUCCESS) return res; |
| |
| struct goldfish_VkDescriptorSetLayout* dsl = as_goldfish_VkDescriptorSetLayout(*pSetLayout); |
| dsl->layoutInfo = new DescriptorSetLayoutInfo; |
| for (uint32_t i = 0; i < pCreateInfo->bindingCount; ++i) { |
| dsl->layoutInfo->bindings.push_back(pCreateInfo->pBindings[i]); |
| } |
| dsl->layoutInfo->refcount = 1; |
| |
| return res; |
| } |
| |
| void ResourceTracker::on_vkUpdateDescriptorSets(void* context, VkDevice device, |
| uint32_t descriptorWriteCount, |
| const VkWriteDescriptorSet* pDescriptorWrites, |
| uint32_t descriptorCopyCount, |
| const VkCopyDescriptorSet* pDescriptorCopies) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| std::vector<VkDescriptorImageInfo> transformedImageInfos; |
| std::vector<VkWriteDescriptorSet> transformedWrites(descriptorWriteCount); |
| |
| memcpy(transformedWrites.data(), pDescriptorWrites, |
| sizeof(VkWriteDescriptorSet) * descriptorWriteCount); |
| |
| size_t imageInfosNeeded = 0; |
| for (uint32_t i = 0; i < descriptorWriteCount; ++i) { |
| if (!isDescriptorTypeImageInfo(transformedWrites[i].descriptorType)) continue; |
| if (!transformedWrites[i].pImageInfo) continue; |
| |
| imageInfosNeeded += transformedWrites[i].descriptorCount; |
| } |
| |
| transformedImageInfos.resize(imageInfosNeeded); |
| |
| size_t imageInfoIndex = 0; |
| for (uint32_t i = 0; i < descriptorWriteCount; ++i) { |
| if (!isDescriptorTypeImageInfo(transformedWrites[i].descriptorType)) continue; |
| if (!transformedWrites[i].pImageInfo) continue; |
| |
| for (uint32_t j = 0; j < transformedWrites[i].descriptorCount; ++j) { |
| transformedImageInfos[imageInfoIndex] = transformedWrites[i].pImageInfo[j]; |
| ++imageInfoIndex; |
| } |
| transformedWrites[i].pImageInfo = |
| &transformedImageInfos[imageInfoIndex - transformedWrites[i].descriptorCount]; |
| } |
| |
| { |
| // Validate and filter samplers |
| AutoLock<RecursiveLock> lock(mLock); |
| size_t imageInfoIndex = 0; |
| for (uint32_t i = 0; i < descriptorWriteCount; ++i) { |
| if (!isDescriptorTypeImageInfo(transformedWrites[i].descriptorType)) continue; |
| if (!transformedWrites[i].pImageInfo) continue; |
| |
| bool isImmutableSampler = descriptorBindingIsImmutableSampler( |
| transformedWrites[i].dstSet, transformedWrites[i].dstBinding); |
| |
| for (uint32_t j = 0; j < transformedWrites[i].descriptorCount; ++j) { |
| if (isImmutableSampler) { |
| transformedImageInfos[imageInfoIndex].sampler = 0; |
| } |
| transformedImageInfos[imageInfoIndex] = |
| filterNonexistentSampler(transformedImageInfos[imageInfoIndex]); |
| ++imageInfoIndex; |
| } |
| } |
| } |
| |
| if (mFeatureInfo->hasVulkanBatchedDescriptorSetUpdate) { |
| for (uint32_t i = 0; i < descriptorWriteCount; ++i) { |
| VkDescriptorSet set = transformedWrites[i].dstSet; |
| doEmulatedDescriptorWrite(&transformedWrites[i], |
| as_goldfish_VkDescriptorSet(set)->reified); |
| } |
| |
| for (uint32_t i = 0; i < descriptorCopyCount; ++i) { |
| doEmulatedDescriptorCopy( |
| &pDescriptorCopies[i], |
| as_goldfish_VkDescriptorSet(pDescriptorCopies[i].srcSet)->reified, |
| as_goldfish_VkDescriptorSet(pDescriptorCopies[i].dstSet)->reified); |
| } |
| } else { |
| enc->vkUpdateDescriptorSets(device, descriptorWriteCount, transformedWrites.data(), |
| descriptorCopyCount, pDescriptorCopies, true /* do lock */); |
| } |
| } |
| |
| void ResourceTracker::on_vkDestroyImage(void* context, VkDevice device, VkImage image, |
| const VkAllocationCallbacks* pAllocator) { |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| auto* syncHelper = ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->syncHelper(); |
| { |
| AutoLock<RecursiveLock> lock(mLock); // do not guard encoder may cause |
| // deadlock b/243339973 |
| |
| // Wait for any pending QSRIs to prevent a race between the Gfxstream host |
| // potentially processing the below `vkDestroyImage()` from the VK encoder |
| // command stream before processing a previously submitted |
| // `VIRTIO_GPU_NATIVE_SYNC_VULKAN_QSRI_EXPORT` from the virtio-gpu command |
| // stream which relies on the image existing. |
| auto imageInfoIt = info_VkImage.find(image); |
| if (imageInfoIt != info_VkImage.end()) { |
| auto& imageInfo = imageInfoIt->second; |
| for (int syncFd : imageInfo.pendingQsriSyncFds) { |
| int syncWaitRet = syncHelper->wait(syncFd, 3000); |
| if (syncWaitRet < 0) { |
| mesa_loge("%s: Failed to wait for pending QSRI sync: sterror: %s errno: %d", |
| __func__, strerror(errno), errno); |
| } |
| syncHelper->close(syncFd); |
| } |
| imageInfo.pendingQsriSyncFds.clear(); |
| } |
| } |
| #endif |
| VkEncoder* enc = (VkEncoder*)context; |
| #if defined(LINUX_GUEST_BUILD) |
| auto imageInfoIt = info_VkImage.find(image); |
| if (imageInfoIt != info_VkImage.end()) { |
| auto& imageInfo = imageInfoIt->second; |
| if (imageInfo.linearPeerImage) { |
| enc->vkDestroyImage(device, imageInfo.linearPeerImage, pAllocator, true /* do lock */); |
| } |
| } |
| #endif |
| enc->vkDestroyImage(device, image, pAllocator, true /* do lock */); |
| } |
| |
| void ResourceTracker::on_vkGetImageMemoryRequirements(void* context, VkDevice device, VkImage image, |
| VkMemoryRequirements* pMemoryRequirements) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkImage.find(image); |
| if (it == info_VkImage.end()) return; |
| |
| auto& info = it->second; |
| |
| if (info.baseRequirementsKnown) { |
| *pMemoryRequirements = info.baseRequirements; |
| return; |
| } |
| |
| lock.unlock(); |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| enc->vkGetImageMemoryRequirements(device, image, pMemoryRequirements, true /* do lock */); |
| |
| lock.lock(); |
| |
| transformImageMemoryRequirementsForGuestLocked(image, pMemoryRequirements); |
| |
| info.baseRequirementsKnown = true; |
| info.baseRequirements = *pMemoryRequirements; |
| } |
| |
| void ResourceTracker::on_vkGetImageMemoryRequirements2(void* context, VkDevice device, |
| const VkImageMemoryRequirementsInfo2* pInfo, |
| VkMemoryRequirements2* pMemoryRequirements) { |
| VkEncoder* enc = (VkEncoder*)context; |
| enc->vkGetImageMemoryRequirements2(device, pInfo, pMemoryRequirements, true /* do lock */); |
| transformImageMemoryRequirements2ForGuest(pInfo->image, pMemoryRequirements); |
| } |
| |
| void ResourceTracker::on_vkGetImageMemoryRequirements2KHR( |
| void* context, VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, |
| VkMemoryRequirements2* pMemoryRequirements) { |
| VkEncoder* enc = (VkEncoder*)context; |
| enc->vkGetImageMemoryRequirements2KHR(device, pInfo, pMemoryRequirements, true /* do lock */); |
| transformImageMemoryRequirements2ForGuest(pInfo->image, pMemoryRequirements); |
| } |
| |
| void ResourceTracker::on_vkGetImageSubresourceLayout(void* context, VkDevice device, VkImage image, |
| const VkImageSubresource* pSubresource, |
| VkSubresourceLayout* pLayout) { |
| VkEncoder* enc = (VkEncoder*)context; |
| VkImage targetImage = image; |
| #if defined(LINUX_GUEST_BUILD) |
| auto it = info_VkImage.find(image); |
| if (it == info_VkImage.end()) return; |
| const auto& info = it->second; |
| if (info.linearPeerImage) { |
| targetImage = info.linearPeerImage; |
| } |
| #endif |
| enc->vkGetImageSubresourceLayout(device, targetImage, pSubresource, pLayout, |
| true /* do lock */); |
| } |
| |
| VkResult ResourceTracker::on_vkBindImageMemory(void* context, VkResult, VkDevice device, |
| VkImage image, VkDeviceMemory memory, |
| VkDeviceSize memoryOffset) { |
| VkEncoder* enc = (VkEncoder*)context; |
| // Do not forward calls with invalid handles to host. |
| if (info_VkDeviceMemory.find(memory) == info_VkDeviceMemory.end() || |
| info_VkImage.find(image) == info_VkImage.end()) { |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| return enc->vkBindImageMemory(device, image, memory, memoryOffset, true /* do lock */); |
| } |
| |
| VkResult ResourceTracker::on_vkBindImageMemory2(void* context, VkResult, VkDevice device, |
| uint32_t bindingCount, |
| const VkBindImageMemoryInfo* pBindInfos) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| if (bindingCount < 1 || !pBindInfos) { |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| |
| for (uint32_t i = 0; i < bindingCount; i++) { |
| const VkBindImageMemoryInfo& bimi = pBindInfos[i]; |
| |
| auto imageIt = info_VkImage.find(bimi.image); |
| if (imageIt == info_VkImage.end()) { |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| |
| if (bimi.memory != VK_NULL_HANDLE) { |
| auto memoryIt = info_VkDeviceMemory.find(bimi.memory); |
| if (memoryIt == info_VkDeviceMemory.end()) { |
| return VK_ERROR_OUT_OF_DEVICE_MEMORY; |
| } |
| } |
| } |
| |
| return enc->vkBindImageMemory2(device, bindingCount, pBindInfos, true /* do lock */); |
| } |
| |
| VkResult ResourceTracker::on_vkBindImageMemory2KHR(void* context, VkResult result, VkDevice device, |
| uint32_t bindingCount, |
| const VkBindImageMemoryInfo* pBindInfos) { |
| return on_vkBindImageMemory2(context, result, device, bindingCount, pBindInfos); |
| } |
| |
| VkResult ResourceTracker::on_vkCreateBuffer(void* context, VkResult, VkDevice device, |
| const VkBufferCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkBuffer* pBuffer) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| VkBufferCreateInfo localCreateInfo = vk_make_orphan_copy(*pCreateInfo); |
| vk_struct_chain_iterator structChainIter = vk_make_chain_iterator(&localCreateInfo); |
| VkExternalMemoryBufferCreateInfo localExtBufCi; |
| |
| const VkExternalMemoryBufferCreateInfo* extBufCiPtr = |
| vk_find_struct<VkExternalMemoryBufferCreateInfo>(pCreateInfo); |
| if (extBufCiPtr) { |
| localExtBufCi = vk_make_orphan_copy(*extBufCiPtr); |
| vk_append_struct(&structChainIter, &localExtBufCi); |
| } |
| |
| VkBufferOpaqueCaptureAddressCreateInfo localCapAddrCi; |
| const VkBufferOpaqueCaptureAddressCreateInfo* pCapAddrCi = |
| vk_find_struct<VkBufferOpaqueCaptureAddressCreateInfo>(pCreateInfo); |
| if (pCapAddrCi) { |
| localCapAddrCi = vk_make_orphan_copy(*pCapAddrCi); |
| vk_append_struct(&structChainIter, &localCapAddrCi); |
| } |
| |
| VkBufferDeviceAddressCreateInfoEXT localDevAddrCi; |
| const VkBufferDeviceAddressCreateInfoEXT* pDevAddrCi = |
| vk_find_struct<VkBufferDeviceAddressCreateInfoEXT>(pCreateInfo); |
| if (pDevAddrCi) { |
| localDevAddrCi = vk_make_orphan_copy(*pDevAddrCi); |
| vk_append_struct(&structChainIter, &localDevAddrCi); |
| } |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| Optional<zx::vmo> vmo; |
| bool isSysmemBackedMemory = false; |
| |
| if (extBufCiPtr && |
| (extBufCiPtr->handleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA)) { |
| isSysmemBackedMemory = true; |
| } |
| |
| const auto* extBufferCollectionPtr = |
| vk_find_struct<VkBufferCollectionBufferCreateInfoFUCHSIA>(pCreateInfo); |
| |
| if (extBufferCollectionPtr) { |
| const auto& collection = |
| *reinterpret_cast<fidl::WireSyncClient<fuchsia_sysmem::BufferCollection>*>( |
| extBufferCollectionPtr->collection); |
| uint32_t index = extBufferCollectionPtr->index; |
| |
| auto result = collection->WaitForBuffersAllocated(); |
| if (result.ok() && result->status == ZX_OK) { |
| auto& info = result->buffer_collection_info; |
| if (index < info.buffer_count) { |
| vmo = gfxstream::guest::makeOptional(std::move(info.buffers[index].vmo)); |
| } |
| } else { |
| mesa_loge("WaitForBuffersAllocated failed: %d %d", result.status(), |
| GET_STATUS_SAFE(result, status)); |
| } |
| |
| if (vmo && vmo->is_valid()) { |
| fidl::Arena arena; |
| fuchsia_hardware_goldfish::wire::CreateBuffer2Params createParams(arena); |
| createParams.set_size(arena, pCreateInfo->size) |
| .set_memory_property(fuchsia_hardware_goldfish::wire::kMemoryPropertyDeviceLocal); |
| |
| auto result = mControlDevice->CreateBuffer2(std::move(*vmo), createParams); |
| if (!result.ok() || |
| (result->is_error() != ZX_OK && result->error_value() != ZX_ERR_ALREADY_EXISTS)) { |
| mesa_loge("CreateBuffer2 failed: %d:%d", result.status(), |
| GET_STATUS_SAFE(result, error_value())); |
| } |
| isSysmemBackedMemory = true; |
| } |
| } |
| #endif // VK_USE_PLATFORM_FUCHSIA |
| |
| VkResult res; |
| VkMemoryRequirements memReqs; |
| |
| if (supportsCreateResourcesWithRequirements()) { |
| res = enc->vkCreateBufferWithRequirementsGOOGLE(device, &localCreateInfo, pAllocator, |
| pBuffer, &memReqs, true /* do lock */); |
| } else { |
| res = |
| enc->vkCreateBuffer(device, &localCreateInfo, pAllocator, pBuffer, true /* do lock */); |
| } |
| |
| if (res != VK_SUCCESS) return res; |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| if (mCaps.vulkanCapset.colorBufferMemoryIndex == 0xFFFFFFFF) { |
| mCaps.vulkanCapset.colorBufferMemoryIndex = getColorBufferMemoryIndex(context, device); |
| } |
| if (extBufCiPtr && |
| ((extBufCiPtr->handleTypes & |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID) || |
| (extBufCiPtr->handleTypes & VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT))) { |
| updateMemoryTypeBits(&memReqs.memoryTypeBits, mCaps.vulkanCapset.colorBufferMemoryIndex); |
| } |
| #endif |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkBuffer.find(*pBuffer); |
| if (it == info_VkBuffer.end()) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| auto& info = it->second; |
| |
| info.createInfo = localCreateInfo; |
| info.createInfo.pNext = nullptr; |
| |
| if (supportsCreateResourcesWithRequirements()) { |
| info.baseRequirementsKnown = true; |
| info.baseRequirements = memReqs; |
| } |
| |
| if (extBufCiPtr) { |
| info.external = true; |
| info.externalCreateInfo = *extBufCiPtr; |
| } |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| if (isSysmemBackedMemory) { |
| info.isSysmemBackedMemory = true; |
| } |
| #endif |
| |
| return res; |
| } |
| |
| void ResourceTracker::on_vkDestroyBuffer(void* context, VkDevice device, VkBuffer buffer, |
| const VkAllocationCallbacks* pAllocator) { |
| VkEncoder* enc = (VkEncoder*)context; |
| enc->vkDestroyBuffer(device, buffer, pAllocator, true /* do lock */); |
| } |
| |
| void ResourceTracker::on_vkGetBufferMemoryRequirements(void* context, VkDevice device, |
| VkBuffer buffer, |
| VkMemoryRequirements* pMemoryRequirements) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkBuffer.find(buffer); |
| if (it == info_VkBuffer.end()) return; |
| |
| auto& info = it->second; |
| |
| if (info.baseRequirementsKnown) { |
| *pMemoryRequirements = info.baseRequirements; |
| return; |
| } |
| |
| lock.unlock(); |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| enc->vkGetBufferMemoryRequirements(device, buffer, pMemoryRequirements, true /* do lock */); |
| |
| lock.lock(); |
| |
| info.baseRequirementsKnown = true; |
| info.baseRequirements = *pMemoryRequirements; |
| } |
| |
| void ResourceTracker::on_vkGetBufferMemoryRequirements2( |
| void* context, VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, |
| VkMemoryRequirements2* pMemoryRequirements) { |
| VkEncoder* enc = (VkEncoder*)context; |
| enc->vkGetBufferMemoryRequirements2(device, pInfo, pMemoryRequirements, true /* do lock */); |
| transformBufferMemoryRequirements2ForGuest(pInfo->buffer, pMemoryRequirements); |
| } |
| |
| void ResourceTracker::on_vkGetBufferMemoryRequirements2KHR( |
| void* context, VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, |
| VkMemoryRequirements2* pMemoryRequirements) { |
| VkEncoder* enc = (VkEncoder*)context; |
| enc->vkGetBufferMemoryRequirements2KHR(device, pInfo, pMemoryRequirements, true /* do lock */); |
| transformBufferMemoryRequirements2ForGuest(pInfo->buffer, pMemoryRequirements); |
| } |
| |
| VkResult ResourceTracker::on_vkBindBufferMemory(void* context, VkResult, VkDevice device, |
| VkBuffer buffer, VkDeviceMemory memory, |
| VkDeviceSize memoryOffset) { |
| VkEncoder* enc = (VkEncoder*)context; |
| return enc->vkBindBufferMemory(device, buffer, memory, memoryOffset, true /* do lock */); |
| } |
| |
| VkResult ResourceTracker::on_vkBindBufferMemory2(void* context, VkResult, VkDevice device, |
| uint32_t bindInfoCount, |
| const VkBindBufferMemoryInfo* pBindInfos) { |
| VkEncoder* enc = (VkEncoder*)context; |
| return enc->vkBindBufferMemory2(device, bindInfoCount, pBindInfos, true /* do lock */); |
| } |
| |
| VkResult ResourceTracker::on_vkBindBufferMemory2KHR(void* context, VkResult, VkDevice device, |
| uint32_t bindInfoCount, |
| const VkBindBufferMemoryInfo* pBindInfos) { |
| VkEncoder* enc = (VkEncoder*)context; |
| return enc->vkBindBufferMemory2KHR(device, bindInfoCount, pBindInfos, true /* do lock */); |
| } |
| |
| VkResult ResourceTracker::on_vkCreateSemaphore(void* context, VkResult input_result, |
| VkDevice device, |
| const VkSemaphoreCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkSemaphore* pSemaphore) { |
| (void)input_result; |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| VkSemaphoreCreateInfo finalCreateInfo = *pCreateInfo; |
| |
| const VkExportSemaphoreCreateInfoKHR* exportSemaphoreInfoPtr = |
| vk_find_struct<VkExportSemaphoreCreateInfoKHR>(pCreateInfo); |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| bool exportEvent = |
| exportSemaphoreInfoPtr && (exportSemaphoreInfoPtr->handleTypes & |
| VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA); |
| |
| if (exportEvent) { |
| finalCreateInfo.pNext = nullptr; |
| // If we have timeline semaphores externally, leave it there. |
| const VkSemaphoreTypeCreateInfo* typeCi = |
| vk_find_struct<VkSemaphoreTypeCreateInfo>(pCreateInfo); |
| if (typeCi) finalCreateInfo.pNext = typeCi; |
| } |
| #endif |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| bool exportSyncFd = exportSemaphoreInfoPtr && (exportSemaphoreInfoPtr->handleTypes & |
| VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT); |
| |
| if (exportSyncFd) { |
| finalCreateInfo.pNext = nullptr; |
| // If we have timeline semaphores externally, leave it there. |
| const VkSemaphoreTypeCreateInfo* typeCi = |
| vk_find_struct<VkSemaphoreTypeCreateInfo>(pCreateInfo); |
| if (typeCi) finalCreateInfo.pNext = typeCi; |
| } |
| #endif |
| input_result = enc->vkCreateSemaphore(device, &finalCreateInfo, pAllocator, pSemaphore, |
| true /* do lock */); |
| |
| zx_handle_t event_handle = ZX_HANDLE_INVALID; |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| if (exportEvent) { |
| zx_event_create(0, &event_handle); |
| } |
| #endif |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkSemaphore.find(*pSemaphore); |
| if (it == info_VkSemaphore.end()) return VK_ERROR_INITIALIZATION_FAILED; |
| |
| auto& info = it->second; |
| |
| info.device = device; |
| info.eventHandle = event_handle; |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| info.eventKoid = getEventKoid(info.eventHandle); |
| #endif |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| if (exportSyncFd) { |
| if (mFeatureInfo->hasVirtioGpuNativeSync) { |
| VkResult result; |
| int64_t osHandle; |
| uint64_t hostFenceHandle = get_host_u64_VkSemaphore(*pSemaphore); |
| |
| result = createFence(device, hostFenceHandle, osHandle); |
| if (result != VK_SUCCESS) return result; |
| |
| info.syncFd.emplace(osHandle); |
| } else { |
| #if GFXSTREAM_ENABLE_GUEST_GOLDFISH |
| ensureSyncDeviceFd(); |
| |
| if (exportSyncFd) { |
| int syncFd = -1; |
| goldfish_sync_queue_work( |
| mSyncDeviceFd, get_host_u64_VkSemaphore(*pSemaphore) /* the handle */, |
| GOLDFISH_SYNC_VULKAN_SEMAPHORE_SYNC /* thread handle (doubling as type field) */ |
| , |
| &syncFd); |
| info.syncFd.emplace(syncFd); |
| } |
| #endif |
| } |
| } |
| #endif |
| |
| return VK_SUCCESS; |
| } |
| |
| void ResourceTracker::on_vkDestroySemaphore(void* context, VkDevice device, VkSemaphore semaphore, |
| const VkAllocationCallbacks* pAllocator) { |
| VkEncoder* enc = (VkEncoder*)context; |
| enc->vkDestroySemaphore(device, semaphore, pAllocator, true /* do lock */); |
| } |
| |
| // https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#vkGetSemaphoreFdKHR |
| // Each call to vkGetSemaphoreFdKHR must create a new file descriptor and transfer ownership |
| // of it to the application. To avoid leaking resources, the application must release ownership |
| // of the file descriptor when it is no longer needed. |
| VkResult ResourceTracker::on_vkGetSemaphoreFdKHR(void* context, VkResult, VkDevice device, |
| const VkSemaphoreGetFdInfoKHR* pGetFdInfo, |
| int* pFd) { |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| VkEncoder* enc = (VkEncoder*)context; |
| bool getSyncFd = pGetFdInfo->handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; |
| |
| if (getSyncFd) { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto it = info_VkSemaphore.find(pGetFdInfo->semaphore); |
| if (it == info_VkSemaphore.end()) return VK_ERROR_OUT_OF_HOST_MEMORY; |
| auto& semInfo = it->second; |
| // syncFd is supposed to have value. |
| auto* syncHelper = |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->syncHelper(); |
| *pFd = syncHelper->dup(semInfo.syncFd.value_or(-1)); |
| return VK_SUCCESS; |
| } else { |
| // opaque fd |
| int hostFd = 0; |
| VkResult result = enc->vkGetSemaphoreFdKHR(device, pGetFdInfo, &hostFd, true /* do lock */); |
| if (result != VK_SUCCESS) { |
| return result; |
| } |
| *pFd = memfd_create("vk_opaque_fd", 0); |
| write(*pFd, &hostFd, sizeof(hostFd)); |
| return VK_SUCCESS; |
| } |
| #else |
| (void)context; |
| (void)device; |
| (void)pGetFdInfo; |
| (void)pFd; |
| return VK_ERROR_INCOMPATIBLE_DRIVER; |
| #endif |
| } |
| |
| VkResult ResourceTracker::on_vkImportSemaphoreFdKHR( |
| void* context, VkResult input_result, VkDevice device, |
| const VkImportSemaphoreFdInfoKHR* pImportSemaphoreFdInfo) { |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| VkEncoder* enc = (VkEncoder*)context; |
| if (input_result != VK_SUCCESS) { |
| return input_result; |
| } |
| |
| auto* syncHelper = ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->syncHelper(); |
| |
| if (pImportSemaphoreFdInfo->handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) { |
| VkImportSemaphoreFdInfoKHR tmpInfo = *pImportSemaphoreFdInfo; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto semaphoreIt = info_VkSemaphore.find(pImportSemaphoreFdInfo->semaphore); |
| auto& info = semaphoreIt->second; |
| |
| if (info.syncFd.value_or(-1) >= 0) { |
| syncHelper->close(info.syncFd.value()); |
| } |
| |
| info.syncFd.emplace(pImportSemaphoreFdInfo->fd); |
| |
| return VK_SUCCESS; |
| } else { |
| int fd = pImportSemaphoreFdInfo->fd; |
| int err = lseek(fd, 0, SEEK_SET); |
| if (err == -1) { |
| mesa_loge("lseek fail on import semaphore"); |
| } |
| int hostFd = 0; |
| read(fd, &hostFd, sizeof(hostFd)); |
| VkImportSemaphoreFdInfoKHR tmpInfo = *pImportSemaphoreFdInfo; |
| tmpInfo.fd = hostFd; |
| VkResult result = enc->vkImportSemaphoreFdKHR(device, &tmpInfo, true /* do lock */); |
| syncHelper->close(fd); |
| return result; |
| } |
| #else |
| (void)context; |
| (void)input_result; |
| (void)device; |
| (void)pImportSemaphoreFdInfo; |
| return VK_ERROR_INCOMPATIBLE_DRIVER; |
| #endif |
| } |
| |
| VkResult ResourceTracker::on_vkGetMemoryFdPropertiesKHR( |
| void* context, VkResult, VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, int fd, |
| VkMemoryFdPropertiesKHR* pMemoryFdProperties) { |
| #if defined(__linux__) && !defined(VK_USE_PLATFORM_ANDROID_KHR) |
| if (!(handleType & VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT)) { |
| mesa_loge("%s: VK_KHR_external_memory_fd behavior not defined for handleType: 0x%x\n", |
| __func__, handleType); |
| return VK_ERROR_INVALID_EXTERNAL_HANDLE; |
| } |
| // Sanity-check device |
| AutoLock<RecursiveLock> lock(mLock); |
| auto deviceIt = info_VkDevice.find(device); |
| if (deviceIt == info_VkDevice.end()) { |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| // TODO: Verify FD valid ? |
| (void)fd; |
| |
| if (mCaps.vulkanCapset.colorBufferMemoryIndex == 0xFFFFFFFF) { |
| mCaps.vulkanCapset.colorBufferMemoryIndex = getColorBufferMemoryIndex(context, device); |
| } |
| |
| updateMemoryTypeBits(&pMemoryFdProperties->memoryTypeBits, |
| mCaps.vulkanCapset.colorBufferMemoryIndex); |
| |
| return VK_SUCCESS; |
| #else |
| (void)context; |
| (void)device; |
| (void)handleType; |
| (void)fd; |
| (void)pMemoryFdProperties; |
| return VK_ERROR_INCOMPATIBLE_DRIVER; |
| #endif |
| } |
| |
| VkResult ResourceTracker::on_vkGetMemoryFdKHR(void* context, VkResult, VkDevice device, |
| const VkMemoryGetFdInfoKHR* pGetFdInfo, int* pFd) { |
| #if defined(__linux__) && !defined(VK_USE_PLATFORM_ANDROID_KHR) |
| if (!pGetFdInfo) return VK_ERROR_OUT_OF_HOST_MEMORY; |
| if (!pGetFdInfo->memory) return VK_ERROR_OUT_OF_HOST_MEMORY; |
| |
| if (!(pGetFdInfo->handleType & (VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT | |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT))) { |
| mesa_loge("%s: Export operation not defined for handleType: 0x%x\n", __func__, |
| pGetFdInfo->handleType); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| // Sanity-check device |
| AutoLock<RecursiveLock> lock(mLock); |
| auto deviceIt = info_VkDevice.find(device); |
| if (deviceIt == info_VkDevice.end()) { |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| auto deviceMemIt = info_VkDeviceMemory.find(pGetFdInfo->memory); |
| if (deviceMemIt == info_VkDeviceMemory.end()) { |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| auto& info = deviceMemIt->second; |
| |
| if (!info.blobPtr) { |
| mesa_loge("%s: VkDeviceMemory does not have a resource available for export.\n", __func__); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| VirtGpuExternalHandle handle{}; |
| int ret = info.blobPtr->exportBlob(handle); |
| if (ret != 0 || handle.osHandle < 0) { |
| mesa_loge("%s: Failed to export host resource to FD.\n", __func__); |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| *pFd = handle.osHandle; |
| return VK_SUCCESS; |
| #else |
| (void)context; |
| (void)device; |
| (void)pGetFdInfo; |
| (void)pFd; |
| return VK_ERROR_INCOMPATIBLE_DRIVER; |
| #endif |
| } |
| |
| void ResourceTracker::flushCommandBufferPendingCommandsBottomUp( |
| void* context, VkQueue queue, const std::vector<VkCommandBuffer>& workingSet) { |
| if (workingSet.empty()) return; |
| |
| std::vector<VkCommandBuffer> nextLevel; |
| for (auto commandBuffer : workingSet) { |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(commandBuffer); |
| forAllObjects(cb->subObjects, [&nextLevel](void* secondary) { |
| nextLevel.push_back((VkCommandBuffer)secondary); |
| }); |
| } |
| |
| flushCommandBufferPendingCommandsBottomUp(context, queue, nextLevel); |
| |
| // After this point, everyone at the previous level has been flushed |
| for (auto cmdbuf : workingSet) { |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(cmdbuf); |
| |
| // There's no pending commands here, skip. (case 1) |
| if (!cb->privateStream) continue; |
| |
| unsigned char* writtenPtr = 0; |
| size_t written = 0; |
| CommandBufferStagingStream* cmdBufStream = |
| static_cast<CommandBufferStagingStream*>(cb->privateStream); |
| cmdBufStream->getWritten(&writtenPtr, &written); |
| |
| // There's no pending commands here, skip. (case 2, stream created but no new recordings) |
| if (!written) continue; |
| |
| // There are pending commands to flush. |
| VkEncoder* enc = (VkEncoder*)context; |
| VkDeviceMemory deviceMemory = cmdBufStream->getDeviceMemory(); |
| VkDeviceSize dataOffset = 0; |
| if (mFeatureInfo->hasVulkanAuxCommandMemory) { |
| // for suballocations, deviceMemory is an alias VkDeviceMemory |
| // get underling VkDeviceMemory for given alias |
| deviceMemoryTransform_tohost(&deviceMemory, 1 /*memoryCount*/, &dataOffset, |
| 1 /*offsetCount*/, nullptr /*size*/, 0 /*sizeCount*/, |
| nullptr /*typeIndex*/, 0 /*typeIndexCount*/, |
| nullptr /*typeBits*/, 0 /*typeBitCounts*/); |
| |
| // mark stream as flushing before flushing commands |
| cmdBufStream->markFlushing(); |
| enc->vkQueueFlushCommandsFromAuxMemoryGOOGLE(queue, cmdbuf, deviceMemory, dataOffset, |
| written, true /*do lock*/); |
| } else { |
| enc->vkQueueFlushCommandsGOOGLE(queue, cmdbuf, written, (const void*)writtenPtr, |
| true /* do lock */); |
| } |
| // Reset this stream. |
| // flushing happens on vkQueueSubmit |
| // vulkan api states that on queue submit, |
| // applications MUST not attempt to modify the command buffer in any way |
| // -as the device may be processing the commands recorded to it. |
| // It is safe to call reset() here for this reason. |
| // Command Buffer associated with this stream will only leave pending state |
| // after queue submit is complete and host has read the data |
| cmdBufStream->reset(); |
| } |
| } |
| |
| uint32_t ResourceTracker::syncEncodersForQueue(VkQueue queue, VkEncoder* currentEncoder) { |
| if (!supportsAsyncQueueSubmit()) { |
| return 0; |
| } |
| |
| struct goldfish_VkQueue* q = as_goldfish_VkQueue(queue); |
| if (!q) return 0; |
| |
| auto lastEncoder = q->lastUsedEncoder; |
| |
| if (lastEncoder == currentEncoder) return 0; |
| |
| currentEncoder->incRef(); |
| |
| q->lastUsedEncoder = currentEncoder; |
| |
| if (!lastEncoder) return 0; |
| |
| auto oldSeq = q->sequenceNumber; |
| q->sequenceNumber += 2; |
| lastEncoder->vkQueueHostSyncGOOGLE(queue, false, oldSeq + 1, true /* do lock */); |
| lastEncoder->flush(); |
| currentEncoder->vkQueueHostSyncGOOGLE(queue, true, oldSeq + 2, true /* do lock */); |
| |
| if (lastEncoder->decRef()) { |
| q->lastUsedEncoder = nullptr; |
| } |
| |
| return 0; |
| } |
| |
| template <class VkSubmitInfoType> |
| void ResourceTracker::flushStagingStreams(void* context, VkQueue queue, uint32_t submitCount, |
| const VkSubmitInfoType* pSubmits) { |
| std::vector<VkCommandBuffer> toFlush; |
| for (uint32_t i = 0; i < submitCount; ++i) { |
| for (uint32_t j = 0; j < getCommandBufferCount(pSubmits[i]); ++j) { |
| toFlush.push_back(getCommandBuffer(pSubmits[i], j)); |
| } |
| } |
| |
| std::unordered_set<VkDescriptorSet> pendingSets; |
| collectAllPendingDescriptorSetsBottomUp(toFlush, pendingSets); |
| commitDescriptorSetUpdates(context, queue, pendingSets); |
| |
| flushCommandBufferPendingCommandsBottomUp(context, queue, toFlush); |
| |
| for (auto cb : toFlush) { |
| resetCommandBufferPendingTopology(cb); |
| } |
| } |
| |
| VkResult ResourceTracker::on_vkQueueSubmit(void* context, VkResult input_result, VkQueue queue, |
| uint32_t submitCount, const VkSubmitInfo* pSubmits, |
| VkFence fence) { |
| AEMU_SCOPED_TRACE("on_vkQueueSubmit"); |
| return on_vkQueueSubmitTemplate<VkSubmitInfo>(context, input_result, queue, submitCount, |
| pSubmits, fence); |
| } |
| |
| VkResult ResourceTracker::on_vkQueueSubmit2(void* context, VkResult input_result, VkQueue queue, |
| uint32_t submitCount, const VkSubmitInfo2* pSubmits, |
| VkFence fence) { |
| AEMU_SCOPED_TRACE("on_vkQueueSubmit2"); |
| return on_vkQueueSubmitTemplate<VkSubmitInfo2>(context, input_result, queue, submitCount, |
| pSubmits, fence); |
| } |
| |
| VkResult ResourceTracker::vkQueueSubmitEnc(VkEncoder* enc, VkQueue queue, uint32_t submitCount, |
| const VkSubmitInfo* pSubmits, VkFence fence) { |
| if (supportsAsyncQueueSubmit()) { |
| enc->vkQueueSubmitAsyncGOOGLE(queue, submitCount, pSubmits, fence, true /* do lock */); |
| return VK_SUCCESS; |
| } else { |
| return enc->vkQueueSubmit(queue, submitCount, pSubmits, fence, true /* do lock */); |
| } |
| } |
| |
| VkResult ResourceTracker::vkQueueSubmitEnc(VkEncoder* enc, VkQueue queue, uint32_t submitCount, |
| const VkSubmitInfo2* pSubmits, VkFence fence) { |
| if (supportsAsyncQueueSubmit()) { |
| enc->vkQueueSubmitAsync2GOOGLE(queue, submitCount, pSubmits, fence, true /* do lock */); |
| return VK_SUCCESS; |
| } else { |
| return enc->vkQueueSubmit2(queue, submitCount, pSubmits, fence, true /* do lock */); |
| } |
| } |
| |
| template <typename VkSubmitInfoType> |
| VkResult ResourceTracker::on_vkQueueSubmitTemplate(void* context, VkResult input_result, |
| VkQueue queue, uint32_t submitCount, |
| const VkSubmitInfoType* pSubmits, |
| VkFence fence) { |
| flushStagingStreams(context, queue, submitCount, pSubmits); |
| |
| std::vector<VkSemaphore> pre_signal_semaphores; |
| std::vector<zx_handle_t> pre_signal_events; |
| std::vector<int> pre_signal_sync_fds; |
| std::vector<std::pair<zx_handle_t, zx_koid_t>> post_wait_events; |
| std::vector<int> post_wait_sync_fds; |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| for (uint32_t i = 0; i < submitCount; ++i) { |
| for (uint32_t j = 0; j < getWaitSemaphoreCount(pSubmits[i]); ++j) { |
| VkSemaphore semaphore = getWaitSemaphore(pSubmits[i], j); |
| auto it = info_VkSemaphore.find(semaphore); |
| if (it != info_VkSemaphore.end()) { |
| auto& semInfo = it->second; |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| if (semInfo.eventHandle) { |
| pre_signal_events.push_back(semInfo.eventHandle); |
| pre_signal_semaphores.push_back(semaphore); |
| } |
| #endif |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| if (semInfo.syncFd.has_value()) { |
| pre_signal_sync_fds.push_back(semInfo.syncFd.value()); |
| pre_signal_semaphores.push_back(semaphore); |
| } |
| #endif |
| } |
| } |
| for (uint32_t j = 0; j < getSignalSemaphoreCount(pSubmits[i]); ++j) { |
| auto it = info_VkSemaphore.find(getSignalSemaphore(pSubmits[i], j)); |
| if (it != info_VkSemaphore.end()) { |
| auto& semInfo = it->second; |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| if (semInfo.eventHandle) { |
| post_wait_events.push_back({semInfo.eventHandle, semInfo.eventKoid}); |
| #ifndef FUCHSIA_NO_TRACE |
| if (semInfo.eventKoid != ZX_KOID_INVALID) { |
| // TODO(fxbug.dev/42144867): Remove the "semaphore" |
| // FLOW_END events once it is removed from clients |
| // (for example, gfx Engine). |
| TRACE_FLOW_END("gfx", "semaphore", semInfo.eventKoid); |
| TRACE_FLOW_BEGIN("gfx", "goldfish_post_wait_event", semInfo.eventKoid); |
| } |
| #endif |
| } |
| #endif |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| if (semInfo.syncFd.value_or(-1) >= 0) { |
| post_wait_sync_fds.push_back(semInfo.syncFd.value()); |
| } |
| #endif |
| } |
| } |
| } |
| lock.unlock(); |
| |
| if (pre_signal_semaphores.empty()) { |
| input_result = vkQueueSubmitEnc(enc, queue, submitCount, pSubmits, fence); |
| if (input_result != VK_SUCCESS) return input_result; |
| } else { |
| // Schedule waits on the OS external objects and |
| // signal the wait semaphores |
| // in a separate thread. |
| std::vector<WorkPool::Task> preSignalTasks; |
| std::vector<WorkPool::Task> preSignalQueueSubmitTasks; |
| ; |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| for (auto event : pre_signal_events) { |
| preSignalTasks.push_back([event] { |
| zx_object_wait_one(event, ZX_EVENT_SIGNALED, ZX_TIME_INFINITE, nullptr); |
| }); |
| } |
| #endif |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| for (auto fd : pre_signal_sync_fds) { |
| // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkImportSemaphoreFdInfoKHR.html |
| // fd == -1 is treated as already signaled |
| if (fd != -1) { |
| preSignalTasks.push_back([fd] { |
| auto* syncHelper = |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->syncHelper(); |
| syncHelper->wait(fd, 3000); |
| }); |
| } |
| } |
| #endif |
| if (!preSignalTasks.empty()) { |
| auto waitGroupHandle = mWorkPool.schedule(preSignalTasks); |
| mWorkPool.waitAll(waitGroupHandle); |
| } |
| |
| // Use the old version of VkSubmitInfo |
| VkSubmitInfo submit_info = { |
| .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, |
| .waitSemaphoreCount = 0, |
| .pWaitSemaphores = nullptr, |
| .pWaitDstStageMask = nullptr, |
| .signalSemaphoreCount = static_cast<uint32_t>(pre_signal_semaphores.size()), |
| .pSignalSemaphores = pre_signal_semaphores.data()}; |
| vkQueueSubmitEnc(enc, queue, 1, &submit_info, VK_NULL_HANDLE); |
| input_result = vkQueueSubmitEnc(enc, queue, submitCount, pSubmits, fence); |
| if (input_result != VK_SUCCESS) return input_result; |
| } |
| lock.lock(); |
| int externalFenceFdToSignal = -1; |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) || defined(__linux__) |
| if (fence != VK_NULL_HANDLE) { |
| auto it = info_VkFence.find(fence); |
| if (it != info_VkFence.end()) { |
| const auto& info = it->second; |
| if (info.syncFd >= 0) { |
| externalFenceFdToSignal = info.syncFd; |
| } |
| } |
| } |
| #endif |
| if (externalFenceFdToSignal >= 0 || !post_wait_events.empty() || !post_wait_sync_fds.empty()) { |
| std::vector<WorkPool::Task> tasks; |
| |
| tasks.push_back([queue, externalFenceFdToSignal, post_wait_events /* copy of zx handles */, |
| post_wait_sync_fds /* copy of sync fds */] { |
| auto hostConn = ResourceTracker::threadingCallbacks.hostConnectionGetFunc(); |
| auto vkEncoder = ResourceTracker::threadingCallbacks.vkEncoderGetFunc(hostConn); |
| auto waitIdleRes = vkEncoder->vkQueueWaitIdle(queue, true /* do lock */); |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| AEMU_SCOPED_TRACE("on_vkQueueSubmit::SignalSemaphores"); |
| (void)externalFenceFdToSignal; |
| for (auto& [event, koid] : post_wait_events) { |
| #ifndef FUCHSIA_NO_TRACE |
| if (koid != ZX_KOID_INVALID) { |
| TRACE_FLOW_END("gfx", "goldfish_post_wait_event", koid); |
| TRACE_FLOW_BEGIN("gfx", "event_signal", koid); |
| } |
| #endif |
| zx_object_signal(event, 0, ZX_EVENT_SIGNALED); |
| } |
| #endif |
| #if GFXSTREAM_ENABLE_GUEST_GOLDFISH |
| for (auto& fd : post_wait_sync_fds) { |
| goldfish_sync_signal(fd); |
| } |
| |
| if (externalFenceFdToSignal >= 0) { |
| mesa_logi("%s: external fence real signal: %d\n", __func__, externalFenceFdToSignal); |
| goldfish_sync_signal(externalFenceFdToSignal); |
| } |
| #endif |
| }); |
| auto queueAsyncWaitHandle = mWorkPool.schedule(tasks); |
| auto& queueWorkItems = mQueueSensitiveWorkPoolItems[queue]; |
| queueWorkItems.push_back(queueAsyncWaitHandle); |
| } |
| return VK_SUCCESS; |
| } |
| |
| VkResult ResourceTracker::on_vkQueueWaitIdle(void* context, VkResult, VkQueue queue) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| std::vector<WorkPool::WaitGroupHandle> toWait = mQueueSensitiveWorkPoolItems[queue]; |
| mQueueSensitiveWorkPoolItems[queue].clear(); |
| lock.unlock(); |
| |
| if (toWait.empty()) { |
| mesa_logi("%s: No queue-specific work pool items\n", __func__); |
| return enc->vkQueueWaitIdle(queue, true /* do lock */); |
| } |
| |
| for (auto handle : toWait) { |
| mesa_logi("%s: waiting on work group item: %llu\n", __func__, (unsigned long long)handle); |
| mWorkPool.waitAll(handle); |
| } |
| |
| // now done waiting, get the host's opinion |
| return enc->vkQueueWaitIdle(queue, true /* do lock */); |
| } |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| void ResourceTracker::unwrap_VkNativeBufferANDROID(const VkNativeBufferANDROID* inputNativeInfo, |
| VkNativeBufferANDROID* outputNativeInfo) { |
| if (!inputNativeInfo || !inputNativeInfo->handle) { |
| return; |
| } |
| |
| if (!outputNativeInfo || !outputNativeInfo) { |
| mesa_loge("FATAL: Local native buffer info not properly allocated!"); |
| abort(); |
| } |
| |
| auto* gralloc = ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->grallocHelper(); |
| const native_handle_t* nativeHandle = (const native_handle_t*)inputNativeInfo->handle; |
| *(uint32_t*)(outputNativeInfo->handle) = gralloc->getHostHandle(nativeHandle); |
| } |
| |
| void ResourceTracker::unwrap_VkBindImageMemorySwapchainInfoKHR( |
| const VkBindImageMemorySwapchainInfoKHR* inputBimsi, |
| VkBindImageMemorySwapchainInfoKHR* outputBimsi) { |
| if (!inputBimsi || !inputBimsi->swapchain) { |
| return; |
| } |
| |
| if (!outputBimsi || !outputBimsi->swapchain) { |
| return; |
| } |
| |
| // Android based swapchains are implemented by the Android framework's |
| // libvulkan. The only exist within the guest and should not be sent to |
| // the host. |
| outputBimsi->swapchain = VK_NULL_HANDLE; |
| } |
| #endif |
| |
| void ResourceTracker::unwrap_vkCreateImage_pCreateInfo(const VkImageCreateInfo* pCreateInfo, |
| VkImageCreateInfo* local_pCreateInfo) { |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| const VkNativeBufferANDROID* inputNativeInfo = |
| vk_find_struct<VkNativeBufferANDROID>(pCreateInfo); |
| |
| VkNativeBufferANDROID* outputNativeInfo = const_cast<VkNativeBufferANDROID*>( |
| vk_find_struct<VkNativeBufferANDROID>(local_pCreateInfo)); |
| |
| unwrap_VkNativeBufferANDROID(inputNativeInfo, outputNativeInfo); |
| #endif |
| } |
| |
| void ResourceTracker::unwrap_vkAcquireImageANDROID_nativeFenceFd(int fd, int* fd_out) { |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| (void)fd_out; |
| if (fd != -1) { |
| AEMU_SCOPED_TRACE("waitNativeFenceInAcquire"); |
| // Implicit Synchronization |
| auto* syncHelper = |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->syncHelper(); |
| syncHelper->wait(fd, 3000); |
| // From libvulkan's swapchain.cpp: |
| // """ |
| // NOTE: we're relying on AcquireImageANDROID to close fence_clone, |
| // even if the call fails. We could close it ourselves on failure, but |
| // that would create a race condition if the driver closes it on a |
| // failure path: some other thread might create an fd with the same |
| // number between the time the driver closes it and the time we close |
| // it. We must assume one of: the driver *always* closes it even on |
| // failure, or *never* closes it on failure. |
| // """ |
| // Therefore, assume contract where we need to close fd in this driver |
| syncHelper->close(fd); |
| } |
| #endif |
| } |
| |
| void ResourceTracker::unwrap_VkBindImageMemory2_pBindInfos( |
| uint32_t bindInfoCount, const VkBindImageMemoryInfo* inputBindInfos, |
| VkBindImageMemoryInfo* outputBindInfos) { |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| for (uint32_t i = 0; i < bindInfoCount; ++i) { |
| const VkBindImageMemoryInfo* inputBindInfo = &inputBindInfos[i]; |
| VkBindImageMemoryInfo* outputBindInfo = &outputBindInfos[i]; |
| |
| const VkNativeBufferANDROID* inputNativeInfo = |
| vk_find_struct<VkNativeBufferANDROID>(inputBindInfo); |
| |
| VkNativeBufferANDROID* outputNativeInfo = const_cast<VkNativeBufferANDROID*>( |
| vk_find_struct<VkNativeBufferANDROID>(outputBindInfo)); |
| |
| unwrap_VkNativeBufferANDROID(inputNativeInfo, outputNativeInfo); |
| |
| const VkBindImageMemorySwapchainInfoKHR* inputBimsi = |
| vk_find_struct<VkBindImageMemorySwapchainInfoKHR>(inputBindInfo); |
| |
| VkBindImageMemorySwapchainInfoKHR* outputBimsi = |
| const_cast<VkBindImageMemorySwapchainInfoKHR*>( |
| vk_find_struct<VkBindImageMemorySwapchainInfoKHR>(outputBindInfo)); |
| |
| unwrap_VkBindImageMemorySwapchainInfoKHR(inputBimsi, outputBimsi); |
| } |
| #endif |
| } |
| |
| // Action of vkMapMemoryIntoAddressSpaceGOOGLE: |
| // 1. preprocess (on_vkMapMemoryIntoAddressSpaceGOOGLE_pre): |
| // uses address space device to reserve the right size of |
| // memory. |
| // 2. the reservation results in a physical address. the physical |
| // address is set as |*pAddress|. |
| // 3. after pre, the API call is encoded to the host, where the |
| // value of pAddress is also sent (the physical address). |
| // 4. the host will obtain the actual gpu pointer and send it |
| // back out in |*pAddress|. |
| // 5. postprocess (on_vkMapMemoryIntoAddressSpaceGOOGLE) will run, |
| // using the mmap() method of GoldfishAddressSpaceBlock to obtain |
| // a pointer in guest userspace corresponding to the host pointer. |
| VkResult ResourceTracker::on_vkMapMemoryIntoAddressSpaceGOOGLE_pre(void*, VkResult, VkDevice, |
| VkDeviceMemory memory, |
| uint64_t* pAddress) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkDeviceMemory.find(memory); |
| if (it == info_VkDeviceMemory.end()) { |
| return VK_ERROR_OUT_OF_HOST_MEMORY; |
| } |
| |
| #if defined(__ANDROID__) |
| auto& memInfo = it->second; |
| |
| GoldfishAddressSpaceBlockPtr block = std::make_shared<GoldfishAddressSpaceBlock>(); |
| block->allocate(mGoldfishAddressSpaceBlockProvider.get(), memInfo.coherentMemorySize); |
| |
| memInfo.goldfishBlock = block; |
| *pAddress = block->physAddr(); |
| |
| return VK_SUCCESS; |
| #else |
| (void)pAddress; |
| return VK_ERROR_MEMORY_MAP_FAILED; |
| #endif |
| } |
| |
| VkResult ResourceTracker::on_vkMapMemoryIntoAddressSpaceGOOGLE(void*, VkResult input_result, |
| VkDevice, VkDeviceMemory memory, |
| uint64_t* pAddress) { |
| (void)memory; |
| (void)pAddress; |
| |
| if (input_result != VK_SUCCESS) { |
| return input_result; |
| } |
| |
| return input_result; |
| } |
| |
| VkResult ResourceTracker::initDescriptorUpdateTemplateBuffers( |
| const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, |
| VkDescriptorUpdateTemplate descriptorUpdateTemplate) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkDescriptorUpdateTemplate.find(descriptorUpdateTemplate); |
| if (it == info_VkDescriptorUpdateTemplate.end()) { |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| |
| auto& info = it->second; |
| uint32_t inlineUniformBlockBufferSize = 0; |
| |
| for (uint32_t i = 0; i < pCreateInfo->descriptorUpdateEntryCount; ++i) { |
| const auto& entry = pCreateInfo->pDescriptorUpdateEntries[i]; |
| uint32_t descCount = entry.descriptorCount; |
| VkDescriptorType descType = entry.descriptorType; |
| ++info.templateEntryCount; |
| if (isDescriptorTypeInlineUniformBlock(descType)) { |
| inlineUniformBlockBufferSize += descCount; |
| ++info.inlineUniformBlockCount; |
| } else { |
| for (uint32_t j = 0; j < descCount; ++j) { |
| if (isDescriptorTypeImageInfo(descType)) { |
| ++info.imageInfoCount; |
| } else if (isDescriptorTypeBufferInfo(descType)) { |
| ++info.bufferInfoCount; |
| } else if (isDescriptorTypeBufferView(descType)) { |
| ++info.bufferViewCount; |
| } else { |
| mesa_loge("%s: FATAL: Unknown descriptor type %d\n", __func__, descType); |
| // abort(); |
| } |
| } |
| } |
| } |
| |
| if (info.templateEntryCount) |
| info.templateEntries = new VkDescriptorUpdateTemplateEntry[info.templateEntryCount]; |
| |
| if (info.imageInfoCount) { |
| info.imageInfoIndices = new uint32_t[info.imageInfoCount]; |
| info.imageInfos = new VkDescriptorImageInfo[info.imageInfoCount]; |
| } |
| |
| if (info.bufferInfoCount) { |
| info.bufferInfoIndices = new uint32_t[info.bufferInfoCount]; |
| info.bufferInfos = new VkDescriptorBufferInfo[info.bufferInfoCount]; |
| } |
| |
| if (info.bufferViewCount) { |
| info.bufferViewIndices = new uint32_t[info.bufferViewCount]; |
| info.bufferViews = new VkBufferView[info.bufferViewCount]; |
| } |
| |
| if (info.inlineUniformBlockCount) { |
| info.inlineUniformBlockBuffer.resize(inlineUniformBlockBufferSize); |
| info.inlineUniformBlockBytesPerBlocks.resize(info.inlineUniformBlockCount); |
| } |
| |
| uint32_t imageInfoIndex = 0; |
| uint32_t bufferInfoIndex = 0; |
| uint32_t bufferViewIndex = 0; |
| uint32_t inlineUniformBlockIndex = 0; |
| |
| for (uint32_t i = 0; i < pCreateInfo->descriptorUpdateEntryCount; ++i) { |
| const auto& entry = pCreateInfo->pDescriptorUpdateEntries[i]; |
| uint32_t descCount = entry.descriptorCount; |
| VkDescriptorType descType = entry.descriptorType; |
| |
| info.templateEntries[i] = entry; |
| |
| if (isDescriptorTypeInlineUniformBlock(descType)) { |
| info.inlineUniformBlockBytesPerBlocks[inlineUniformBlockIndex] = descCount; |
| ++inlineUniformBlockIndex; |
| } else { |
| for (uint32_t j = 0; j < descCount; ++j) { |
| if (isDescriptorTypeImageInfo(descType)) { |
| info.imageInfoIndices[imageInfoIndex] = i; |
| ++imageInfoIndex; |
| } else if (isDescriptorTypeBufferInfo(descType)) { |
| info.bufferInfoIndices[bufferInfoIndex] = i; |
| ++bufferInfoIndex; |
| } else if (isDescriptorTypeBufferView(descType)) { |
| info.bufferViewIndices[bufferViewIndex] = i; |
| ++bufferViewIndex; |
| } else { |
| mesa_loge("%s: FATAL: Unknown descriptor type %d\n", __func__, descType); |
| // abort(); |
| } |
| } |
| } |
| } |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult ResourceTracker::on_vkCreateDescriptorUpdateTemplate( |
| void* context, VkResult input_result, VkDevice device, |
| const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate) { |
| (void)context; |
| (void)device; |
| (void)pAllocator; |
| |
| if (input_result != VK_SUCCESS) return input_result; |
| |
| return initDescriptorUpdateTemplateBuffers(pCreateInfo, *pDescriptorUpdateTemplate); |
| } |
| |
| VkResult ResourceTracker::on_vkCreateDescriptorUpdateTemplateKHR( |
| void* context, VkResult input_result, VkDevice device, |
| const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate) { |
| (void)context; |
| (void)device; |
| (void)pAllocator; |
| |
| if (input_result != VK_SUCCESS) return input_result; |
| |
| return initDescriptorUpdateTemplateBuffers(pCreateInfo, *pDescriptorUpdateTemplate); |
| } |
| |
| void ResourceTracker::on_vkUpdateDescriptorSetWithTemplate( |
| void* context, VkDevice device, VkDescriptorSet descriptorSet, |
| VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| uint8_t* userBuffer = (uint8_t*)pData; |
| if (!userBuffer) return; |
| |
| // TODO: Make this thread safe |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkDescriptorUpdateTemplate.find(descriptorUpdateTemplate); |
| if (it == info_VkDescriptorUpdateTemplate.end()) { |
| return; |
| } |
| |
| auto& info = it->second; |
| |
| uint32_t templateEntryCount = info.templateEntryCount; |
| VkDescriptorUpdateTemplateEntry* templateEntries = info.templateEntries; |
| |
| uint32_t imageInfoCount = info.imageInfoCount; |
| uint32_t bufferInfoCount = info.bufferInfoCount; |
| uint32_t bufferViewCount = info.bufferViewCount; |
| uint32_t inlineUniformBlockCount = info.inlineUniformBlockCount; |
| uint32_t* imageInfoIndices = info.imageInfoIndices; |
| uint32_t* bufferInfoIndices = info.bufferInfoIndices; |
| uint32_t* bufferViewIndices = info.bufferViewIndices; |
| VkDescriptorImageInfo* imageInfos = info.imageInfos; |
| VkDescriptorBufferInfo* bufferInfos = info.bufferInfos; |
| VkBufferView* bufferViews = info.bufferViews; |
| uint8_t* inlineUniformBlockBuffer = info.inlineUniformBlockBuffer.data(); |
| uint32_t* inlineUniformBlockBytesPerBlocks = info.inlineUniformBlockBytesPerBlocks.data(); |
| |
| lock.unlock(); |
| |
| size_t currImageInfoOffset = 0; |
| size_t currBufferInfoOffset = 0; |
| size_t currBufferViewOffset = 0; |
| size_t inlineUniformBlockOffset = 0; |
| size_t inlineUniformBlockIdx = 0; |
| |
| struct goldfish_VkDescriptorSet* ds = as_goldfish_VkDescriptorSet(descriptorSet); |
| ReifiedDescriptorSet* reified = ds->reified; |
| |
| bool batched = mFeatureInfo->hasVulkanBatchedDescriptorSetUpdate; |
| |
| for (uint32_t i = 0; i < templateEntryCount; ++i) { |
| const auto& entry = templateEntries[i]; |
| VkDescriptorType descType = entry.descriptorType; |
| uint32_t dstBinding = entry.dstBinding; |
| |
| auto offset = entry.offset; |
| auto stride = entry.stride; |
| auto dstArrayElement = entry.dstArrayElement; |
| |
| uint32_t descCount = entry.descriptorCount; |
| |
| if (isDescriptorTypeImageInfo(descType)) { |
| if (!stride) stride = sizeof(VkDescriptorImageInfo); |
| |
| const VkDescriptorImageInfo* currImageInfoBegin = |
| (const VkDescriptorImageInfo*)((uint8_t*)imageInfos + currImageInfoOffset); |
| |
| for (uint32_t j = 0; j < descCount; ++j) { |
| const VkDescriptorImageInfo* user = |
| (const VkDescriptorImageInfo*)(userBuffer + offset + j * stride); |
| |
| memcpy(((uint8_t*)imageInfos) + currImageInfoOffset, user, |
| sizeof(VkDescriptorImageInfo)); |
| currImageInfoOffset += sizeof(VkDescriptorImageInfo); |
| } |
| |
| if (batched) { |
| doEmulatedDescriptorImageInfoWriteFromTemplate( |
| descType, dstBinding, dstArrayElement, descCount, currImageInfoBegin, reified); |
| } |
| } else if (isDescriptorTypeBufferInfo(descType)) { |
| if (!stride) stride = sizeof(VkDescriptorBufferInfo); |
| |
| const VkDescriptorBufferInfo* currBufferInfoBegin = |
| (const VkDescriptorBufferInfo*)((uint8_t*)bufferInfos + currBufferInfoOffset); |
| |
| for (uint32_t j = 0; j < descCount; ++j) { |
| const VkDescriptorBufferInfo* user = |
| (const VkDescriptorBufferInfo*)(userBuffer + offset + j * stride); |
| |
| memcpy(((uint8_t*)bufferInfos) + currBufferInfoOffset, user, |
| sizeof(VkDescriptorBufferInfo)); |
| #if defined(__linux__) && !defined(VK_USE_PLATFORM_ANDROID_KHR) |
| // Convert mesa to internal for objects in the user buffer |
| VkDescriptorBufferInfo* internalBufferInfo = |
| (VkDescriptorBufferInfo*)(((uint8_t*)bufferInfos) + currBufferInfoOffset); |
| VK_FROM_HANDLE(gfxstream_vk_buffer, gfxstream_buffer, internalBufferInfo->buffer); |
| internalBufferInfo->buffer = gfxstream_buffer->internal_object; |
| #endif |
| currBufferInfoOffset += sizeof(VkDescriptorBufferInfo); |
| } |
| |
| if (batched) { |
| doEmulatedDescriptorBufferInfoWriteFromTemplate( |
| descType, dstBinding, dstArrayElement, descCount, currBufferInfoBegin, reified); |
| } |
| |
| } else if (isDescriptorTypeBufferView(descType)) { |
| if (!stride) stride = sizeof(VkBufferView); |
| |
| const VkBufferView* currBufferViewBegin = |
| (const VkBufferView*)((uint8_t*)bufferViews + currBufferViewOffset); |
| |
| for (uint32_t j = 0; j < descCount; ++j) { |
| const VkBufferView* user = (const VkBufferView*)(userBuffer + offset + j * stride); |
| |
| memcpy(((uint8_t*)bufferViews) + currBufferViewOffset, user, sizeof(VkBufferView)); |
| currBufferViewOffset += sizeof(VkBufferView); |
| } |
| |
| if (batched) { |
| doEmulatedDescriptorBufferViewWriteFromTemplate( |
| descType, dstBinding, dstArrayElement, descCount, currBufferViewBegin, reified); |
| } |
| } else if (isDescriptorTypeInlineUniformBlock(descType)) { |
| uint32_t inlineUniformBlockBytesPerBlock = |
| inlineUniformBlockBytesPerBlocks[inlineUniformBlockIdx]; |
| uint8_t* currInlineUniformBlockBufferBegin = |
| inlineUniformBlockBuffer + inlineUniformBlockOffset; |
| memcpy(currInlineUniformBlockBufferBegin, userBuffer + offset, |
| inlineUniformBlockBytesPerBlock); |
| inlineUniformBlockIdx++; |
| inlineUniformBlockOffset += inlineUniformBlockBytesPerBlock; |
| |
| if (batched) { |
| doEmulatedDescriptorInlineUniformBlockFromTemplate( |
| descType, dstBinding, dstArrayElement, descCount, |
| currInlineUniformBlockBufferBegin, reified); |
| } |
| } else { |
| mesa_loge("%s: FATAL: Unknown descriptor type %d\n", __func__, descType); |
| abort(); |
| } |
| } |
| |
| if (batched) return; |
| |
| enc->vkUpdateDescriptorSetWithTemplateSized2GOOGLE( |
| device, descriptorSet, descriptorUpdateTemplate, imageInfoCount, bufferInfoCount, |
| bufferViewCount, static_cast<uint32_t>(info.inlineUniformBlockBuffer.size()), |
| imageInfoIndices, bufferInfoIndices, bufferViewIndices, imageInfos, bufferInfos, |
| bufferViews, inlineUniformBlockBuffer, true /* do lock */); |
| } |
| |
| void ResourceTracker::on_vkUpdateDescriptorSetWithTemplateKHR( |
| void* context, VkDevice device, VkDescriptorSet descriptorSet, |
| VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData) { |
| on_vkUpdateDescriptorSetWithTemplate(context, device, descriptorSet, descriptorUpdateTemplate, |
| pData); |
| } |
| |
| VkResult ResourceTracker::on_vkGetPhysicalDeviceImageFormatProperties2_common( |
| bool isKhr, void* context, VkResult input_result, VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, |
| VkImageFormatProperties2* pImageFormatProperties) { |
| VkEncoder* enc = (VkEncoder*)context; |
| (void)input_result; |
| |
| uint32_t supportedHandleType = 0; |
| VkExternalImageFormatProperties* ext_img_properties = |
| vk_find_struct<VkExternalImageFormatProperties>(pImageFormatProperties); |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| |
| constexpr VkFormat kExternalImageSupportedFormats[] = { |
| VK_FORMAT_B8G8R8A8_SINT, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB, |
| VK_FORMAT_B8G8R8A8_SNORM, VK_FORMAT_B8G8R8A8_SSCALED, VK_FORMAT_B8G8R8A8_USCALED, |
| VK_FORMAT_R8G8B8A8_SINT, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SRGB, |
| VK_FORMAT_R8G8B8A8_SNORM, VK_FORMAT_R8G8B8A8_SSCALED, VK_FORMAT_R8G8B8A8_USCALED, |
| VK_FORMAT_R8_UNORM, VK_FORMAT_R8_UINT, VK_FORMAT_R8_USCALED, |
| VK_FORMAT_R8_SNORM, VK_FORMAT_R8_SINT, VK_FORMAT_R8_SSCALED, |
| VK_FORMAT_R8_SRGB, VK_FORMAT_R8G8_UNORM, VK_FORMAT_R8G8_UINT, |
| VK_FORMAT_R8G8_USCALED, VK_FORMAT_R8G8_SNORM, VK_FORMAT_R8G8_SINT, |
| VK_FORMAT_R8G8_SSCALED, VK_FORMAT_R8G8_SRGB, |
| }; |
| |
| if (ext_img_properties) { |
| if (std::find(std::begin(kExternalImageSupportedFormats), |
| std::end(kExternalImageSupportedFormats), |
| pImageFormatInfo->format) == std::end(kExternalImageSupportedFormats)) { |
| return VK_ERROR_FORMAT_NOT_SUPPORTED; |
| } |
| } |
| supportedHandleType |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA; |
| #endif |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| VkAndroidHardwareBufferUsageANDROID* output_ahw_usage = |
| vk_find_struct<VkAndroidHardwareBufferUsageANDROID>(pImageFormatProperties); |
| supportedHandleType |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT | |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID; |
| #endif |
| const VkPhysicalDeviceExternalImageFormatInfo* ext_img_info = |
| vk_find_struct<VkPhysicalDeviceExternalImageFormatInfo>(pImageFormatInfo); |
| if (supportedHandleType && ext_img_info) { |
| // 0 is a valid handleType so we don't check against 0 |
| if (ext_img_info->handleType != (ext_img_info->handleType & supportedHandleType)) { |
| return VK_ERROR_FORMAT_NOT_SUPPORTED; |
| } |
| } |
| |
| VkResult hostRes; |
| |
| if (isKhr) { |
| hostRes = enc->vkGetPhysicalDeviceImageFormatProperties2KHR( |
| physicalDevice, pImageFormatInfo, pImageFormatProperties, true /* do lock */); |
| } else { |
| hostRes = enc->vkGetPhysicalDeviceImageFormatProperties2( |
| physicalDevice, pImageFormatInfo, pImageFormatProperties, true /* do lock */); |
| } |
| |
| if (hostRes != VK_SUCCESS) return hostRes; |
| |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| if (ext_img_properties) { |
| if (ext_img_info) { |
| if (static_cast<uint32_t>(ext_img_info->handleType) == |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA) { |
| ext_img_properties->externalMemoryProperties = { |
| .externalMemoryFeatures = VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | |
| VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT, |
| .exportFromImportedHandleTypes = |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA, |
| .compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA, |
| }; |
| } |
| } |
| } |
| #endif |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| if (output_ahw_usage) { |
| output_ahw_usage->androidHardwareBufferUsage = getAndroidHardwareBufferUsageFromVkUsage( |
| pImageFormatInfo->flags, pImageFormatInfo->usage); |
| } |
| #endif |
| if (ext_img_properties) { |
| transformImpl_VkExternalMemoryProperties_fromhost( |
| &ext_img_properties->externalMemoryProperties, 0); |
| } |
| return hostRes; |
| } |
| |
| VkResult ResourceTracker::on_vkGetPhysicalDeviceImageFormatProperties2( |
| void* context, VkResult input_result, VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, |
| VkImageFormatProperties2* pImageFormatProperties) { |
| return on_vkGetPhysicalDeviceImageFormatProperties2_common( |
| false /* not KHR */, context, input_result, physicalDevice, pImageFormatInfo, |
| pImageFormatProperties); |
| } |
| |
| VkResult ResourceTracker::on_vkGetPhysicalDeviceImageFormatProperties2KHR( |
| void* context, VkResult input_result, VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, |
| VkImageFormatProperties2* pImageFormatProperties) { |
| return on_vkGetPhysicalDeviceImageFormatProperties2_common( |
| true /* is KHR */, context, input_result, physicalDevice, pImageFormatInfo, |
| pImageFormatProperties); |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceExternalBufferProperties_common( |
| bool isKhr, void* context, VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, |
| VkExternalBufferProperties* pExternalBufferProperties) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| #if defined(ANDROID) |
| // Older versions of Goldfish's Gralloc did not support allocating AHARDWAREBUFFER_FORMAT_BLOB |
| // with GPU usage (b/299520213). |
| if (ResourceTracker::threadingCallbacks.hostConnectionGetFunc() |
| ->grallocHelper() |
| ->treatBlobAsImage() && |
| pExternalBufferInfo->handleType == |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID) { |
| pExternalBufferProperties->externalMemoryProperties.externalMemoryFeatures = 0; |
| pExternalBufferProperties->externalMemoryProperties.exportFromImportedHandleTypes = 0; |
| pExternalBufferProperties->externalMemoryProperties.compatibleHandleTypes = 0; |
| return; |
| } |
| #endif |
| |
| uint32_t supportedHandleType = 0; |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| supportedHandleType |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA; |
| #endif |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| supportedHandleType |= VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT | |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID; |
| #endif |
| if (supportedHandleType) { |
| // 0 is a valid handleType so we can't check against 0 |
| if (pExternalBufferInfo->handleType != |
| (pExternalBufferInfo->handleType & supportedHandleType)) { |
| return; |
| } |
| } |
| |
| if (isKhr) { |
| enc->vkGetPhysicalDeviceExternalBufferPropertiesKHR( |
| physicalDevice, pExternalBufferInfo, pExternalBufferProperties, true /* do lock */); |
| } else { |
| enc->vkGetPhysicalDeviceExternalBufferProperties( |
| physicalDevice, pExternalBufferInfo, pExternalBufferProperties, true /* do lock */); |
| } |
| transformImpl_VkExternalMemoryProperties_fromhost( |
| &pExternalBufferProperties->externalMemoryProperties, 0); |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceExternalBufferProperties( |
| void* context, VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, |
| VkExternalBufferProperties* pExternalBufferProperties) { |
| return on_vkGetPhysicalDeviceExternalBufferProperties_common( |
| false /* not KHR */, context, physicalDevice, pExternalBufferInfo, |
| pExternalBufferProperties); |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceExternalBufferPropertiesKHR( |
| void* context, VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceExternalBufferInfoKHR* pExternalBufferInfo, |
| VkExternalBufferPropertiesKHR* pExternalBufferProperties) { |
| return on_vkGetPhysicalDeviceExternalBufferProperties_common( |
| true /* is KHR */, context, physicalDevice, pExternalBufferInfo, pExternalBufferProperties); |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceExternalSemaphoreProperties( |
| void*, VkPhysicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, |
| VkExternalSemaphoreProperties* pExternalSemaphoreProperties) { |
| (void)pExternalSemaphoreInfo; |
| (void)pExternalSemaphoreProperties; |
| #ifdef VK_USE_PLATFORM_FUCHSIA |
| if (pExternalSemaphoreInfo->handleType == |
| static_cast<uint32_t>(VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA)) { |
| pExternalSemaphoreProperties->compatibleHandleTypes |= |
| VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA; |
| pExternalSemaphoreProperties->exportFromImportedHandleTypes |= |
| VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA; |
| pExternalSemaphoreProperties->externalSemaphoreFeatures |= |
| VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | |
| VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT; |
| } |
| #else |
| const VkSemaphoreTypeCreateInfo* semaphoreTypeCi = |
| vk_find_struct<VkSemaphoreTypeCreateInfo>(pExternalSemaphoreInfo); |
| bool isSemaphoreTimeline = |
| semaphoreTypeCi != nullptr && semaphoreTypeCi->semaphoreType == VK_SEMAPHORE_TYPE_TIMELINE; |
| if (isSemaphoreTimeline) { |
| // b/304373623 |
| // dEQP-VK.api.external.semaphore.sync_fd#info_timeline |
| pExternalSemaphoreProperties->compatibleHandleTypes = 0; |
| pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0; |
| pExternalSemaphoreProperties->externalSemaphoreFeatures = 0; |
| } else if (pExternalSemaphoreInfo->handleType == |
| VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) { |
| pExternalSemaphoreProperties->compatibleHandleTypes |= |
| VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; |
| pExternalSemaphoreProperties->exportFromImportedHandleTypes |= |
| VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; |
| pExternalSemaphoreProperties->externalSemaphoreFeatures |= |
| VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | |
| VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT; |
| } |
| #endif // VK_USE_PLATFORM_FUCHSIA |
| } |
| |
| void ResourceTracker::on_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR( |
| void* context, VkPhysicalDevice physicalDevice, |
| const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, |
| VkExternalSemaphoreProperties* pExternalSemaphoreProperties) { |
| on_vkGetPhysicalDeviceExternalSemaphoreProperties( |
| context, physicalDevice, pExternalSemaphoreInfo, pExternalSemaphoreProperties); |
| } |
| |
| void ResourceTracker::registerEncoderCleanupCallback(const VkEncoder* encoder, void* object, |
| CleanupCallback callback) { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto& callbacks = mEncoderCleanupCallbacks[encoder]; |
| callbacks[object] = callback; |
| } |
| |
| void ResourceTracker::unregisterEncoderCleanupCallback(const VkEncoder* encoder, void* object) { |
| AutoLock<RecursiveLock> lock(mLock); |
| mEncoderCleanupCallbacks[encoder].erase(object); |
| } |
| |
| void ResourceTracker::onEncoderDeleted(const VkEncoder* encoder) { |
| AutoLock<RecursiveLock> lock(mLock); |
| if (mEncoderCleanupCallbacks.find(encoder) == mEncoderCleanupCallbacks.end()) return; |
| |
| std::unordered_map<void*, CleanupCallback> callbackCopies = mEncoderCleanupCallbacks[encoder]; |
| |
| mEncoderCleanupCallbacks.erase(encoder); |
| lock.unlock(); |
| |
| for (auto it : callbackCopies) { |
| it.second(); |
| } |
| } |
| |
| CommandBufferStagingStream::Alloc ResourceTracker::getAlloc() { |
| if (mFeatureInfo->hasVulkanAuxCommandMemory) { |
| return [this](size_t size) -> CommandBufferStagingStream::Memory { |
| VkMemoryAllocateInfo info{ |
| .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, |
| .pNext = nullptr, |
| .allocationSize = size, |
| .memoryTypeIndex = VK_MAX_MEMORY_TYPES // indicates auxiliary memory |
| }; |
| |
| auto enc = ResourceTracker::getThreadLocalEncoder(); |
| VkDevice device = VK_NULL_HANDLE; |
| VkDeviceMemory vkDeviceMem = VK_NULL_HANDLE; |
| VkResult result = getCoherentMemory(&info, enc, device, &vkDeviceMem); |
| if (result != VK_SUCCESS) { |
| mesa_loge("Failed to get coherent memory %u", result); |
| return {.deviceMemory = VK_NULL_HANDLE, .ptr = nullptr}; |
| } |
| |
| // getCoherentMemory() uses suballocations. |
| // To retrieve the suballocated memory address, look up |
| // VkDeviceMemory filled in by getCoherentMemory() |
| // scope of mLock |
| { |
| AutoLock<RecursiveLock> lock(mLock); |
| const auto it = info_VkDeviceMemory.find(vkDeviceMem); |
| if (it == info_VkDeviceMemory.end()) { |
| mesa_loge("Coherent memory allocated %u not found", result); |
| return {.deviceMemory = VK_NULL_HANDLE, .ptr = nullptr}; |
| }; |
| |
| const auto& info = it->second; |
| return {.deviceMemory = vkDeviceMem, .ptr = info.ptr}; |
| } |
| }; |
| } |
| return nullptr; |
| } |
| |
| CommandBufferStagingStream::Free ResourceTracker::getFree() { |
| if (mFeatureInfo->hasVulkanAuxCommandMemory) { |
| return [this](const CommandBufferStagingStream::Memory& memory) { |
| // deviceMemory may not be the actual backing auxiliary VkDeviceMemory |
| // for suballocations, deviceMemory is a alias VkDeviceMemory hand; |
| // freeCoherentMemoryLocked maps the alias to the backing VkDeviceMemory |
| VkDeviceMemory deviceMemory = memory.deviceMemory; |
| AutoLock<RecursiveLock> lock(mLock); |
| auto it = info_VkDeviceMemory.find(deviceMemory); |
| if (it == info_VkDeviceMemory.end()) { |
| mesa_loge("Device memory to free not found"); |
| return; |
| } |
| auto coherentMemory = freeCoherentMemoryLocked(deviceMemory, it->second); |
| // We have to release the lock before we could possibly free a |
| // CoherentMemory, because that will call into VkEncoder, which |
| // shouldn't be called when the lock is held. |
| lock.unlock(); |
| coherentMemory = nullptr; |
| }; |
| } |
| return nullptr; |
| } |
| |
| VkResult ResourceTracker::on_vkBeginCommandBuffer(void* context, VkResult input_result, |
| VkCommandBuffer commandBuffer, |
| const VkCommandBufferBeginInfo* pBeginInfo) { |
| (void)context; |
| |
| resetCommandBufferStagingInfo(commandBuffer, true /* also reset primaries */, |
| true /* also clear pending descriptor sets */); |
| |
| VkEncoder* enc = ResourceTracker::getCommandBufferEncoder(commandBuffer); |
| (void)input_result; |
| |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(commandBuffer); |
| cb->flags = pBeginInfo->flags; |
| |
| VkCommandBufferBeginInfo modifiedBeginInfo; |
| |
| if (pBeginInfo->pInheritanceInfo && !cb->isSecondary) { |
| modifiedBeginInfo = *pBeginInfo; |
| modifiedBeginInfo.pInheritanceInfo = nullptr; |
| pBeginInfo = &modifiedBeginInfo; |
| } |
| |
| if (!supportsDeferredCommands()) { |
| return enc->vkBeginCommandBuffer(commandBuffer, pBeginInfo, true /* do lock */); |
| } |
| |
| enc->vkBeginCommandBufferAsyncGOOGLE(commandBuffer, pBeginInfo, true /* do lock */); |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult ResourceTracker::on_vkEndCommandBuffer(void* context, VkResult input_result, |
| VkCommandBuffer commandBuffer) { |
| VkEncoder* enc = (VkEncoder*)context; |
| (void)input_result; |
| |
| if (!supportsDeferredCommands()) { |
| return enc->vkEndCommandBuffer(commandBuffer, true /* do lock */); |
| } |
| |
| enc->vkEndCommandBufferAsyncGOOGLE(commandBuffer, true /* do lock */); |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult ResourceTracker::on_vkResetCommandBuffer(void* context, VkResult input_result, |
| VkCommandBuffer commandBuffer, |
| VkCommandBufferResetFlags flags) { |
| VkEncoder* enc = (VkEncoder*)context; |
| (void)input_result; |
| |
| if (!supportsDeferredCommands()) { |
| VkResult res = enc->vkResetCommandBuffer(commandBuffer, flags, true /* do lock */); |
| resetCommandBufferStagingInfo(commandBuffer, true /* also reset primaries */, |
| true /* also clear pending descriptor sets */); |
| return res; |
| } |
| |
| enc->vkResetCommandBufferAsyncGOOGLE(commandBuffer, flags, true /* do lock */); |
| resetCommandBufferStagingInfo(commandBuffer, true /* also reset primaries */, |
| true /* also clear pending descriptor sets */); |
| return VK_SUCCESS; |
| } |
| |
| VkResult ResourceTracker::on_vkCreateImageView(void* context, VkResult input_result, |
| VkDevice device, |
| const VkImageViewCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkImageView* pView) { |
| VkEncoder* enc = (VkEncoder*)context; |
| (void)input_result; |
| |
| VkImageViewCreateInfo localCreateInfo = vk_make_orphan_copy(*pCreateInfo); |
| vk_struct_chain_iterator structChainIter = vk_make_chain_iterator(&localCreateInfo); |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) |
| if (pCreateInfo->format == VK_FORMAT_UNDEFINED) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkImage.find(pCreateInfo->image); |
| if (it != info_VkImage.end() && it->second.hasExternalFormat) { |
| localCreateInfo.format = vk_format_from_fourcc(it->second.externalFourccFormat); |
| } |
| } |
| VkSamplerYcbcrConversionInfo localVkSamplerYcbcrConversionInfo; |
| const VkSamplerYcbcrConversionInfo* samplerYcbcrConversionInfo = |
| vk_find_struct<VkSamplerYcbcrConversionInfo>(pCreateInfo); |
| if (samplerYcbcrConversionInfo) { |
| if (samplerYcbcrConversionInfo->conversion != VK_YCBCR_CONVERSION_DO_NOTHING) { |
| localVkSamplerYcbcrConversionInfo = vk_make_orphan_copy(*samplerYcbcrConversionInfo); |
| vk_append_struct(&structChainIter, &localVkSamplerYcbcrConversionInfo); |
| } |
| } |
| #endif |
| |
| return enc->vkCreateImageView(device, &localCreateInfo, pAllocator, pView, true /* do lock */); |
| } |
| |
| void ResourceTracker::on_vkCmdExecuteCommands(void* context, VkCommandBuffer commandBuffer, |
| uint32_t commandBufferCount, |
| const VkCommandBuffer* pCommandBuffers) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| if (!mFeatureInfo->hasVulkanQueueSubmitWithCommands) { |
| enc->vkCmdExecuteCommands(commandBuffer, commandBufferCount, pCommandBuffers, |
| true /* do lock */); |
| return; |
| } |
| |
| struct goldfish_VkCommandBuffer* primary = as_goldfish_VkCommandBuffer(commandBuffer); |
| for (uint32_t i = 0; i < commandBufferCount; ++i) { |
| struct goldfish_VkCommandBuffer* secondary = |
| as_goldfish_VkCommandBuffer(pCommandBuffers[i]); |
| appendObject(&secondary->superObjects, primary); |
| appendObject(&primary->subObjects, secondary); |
| } |
| |
| enc->vkCmdExecuteCommands(commandBuffer, commandBufferCount, pCommandBuffers, |
| true /* do lock */); |
| } |
| |
| void ResourceTracker::on_vkCmdBindDescriptorSets(void* context, VkCommandBuffer commandBuffer, |
| VkPipelineBindPoint pipelineBindPoint, |
| VkPipelineLayout layout, uint32_t firstSet, |
| uint32_t descriptorSetCount, |
| const VkDescriptorSet* pDescriptorSets, |
| uint32_t dynamicOffsetCount, |
| const uint32_t* pDynamicOffsets) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| if (mFeatureInfo->hasVulkanBatchedDescriptorSetUpdate) |
| addPendingDescriptorSets(commandBuffer, descriptorSetCount, pDescriptorSets); |
| |
| enc->vkCmdBindDescriptorSets(commandBuffer, pipelineBindPoint, layout, firstSet, |
| descriptorSetCount, pDescriptorSets, dynamicOffsetCount, |
| pDynamicOffsets, true /* do lock */); |
| } |
| |
| void ResourceTracker::on_vkCmdPipelineBarrier( |
| void* context, VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, |
| VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, |
| uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, |
| uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, |
| uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers) { |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| std::vector<VkImageMemoryBarrier> updatedImageMemoryBarriers; |
| updatedImageMemoryBarriers.reserve(imageMemoryBarrierCount); |
| for (uint32_t i = 0; i < imageMemoryBarrierCount; i++) { |
| VkImageMemoryBarrier barrier = pImageMemoryBarriers[i]; |
| |
| #ifdef VK_USE_PLATFORM_ANDROID_KHR |
| // Unfortunetly, Android does not yet have a mechanism for sharing the expected |
| // VkImageLayout when passing around AHardwareBuffer-s so many existing users |
| // that import AHardwareBuffer-s into VkImage-s/VkDeviceMemory-s simply use |
| // VK_IMAGE_LAYOUT_UNDEFINED. However, the Vulkan spec's image layout transition |
| // sections says "If the old layout is VK_IMAGE_LAYOUT_UNDEFINED, the contents of |
| // that range may be discarded." Some Vulkan drivers have been observed to actually |
| // perform the discard which leads to AHardwareBuffer-s being unintentionally |
| // cleared. See go/ahb-vkimagelayout for more information. |
| if (barrier.srcQueueFamilyIndex != barrier.dstQueueFamilyIndex && |
| (barrier.srcQueueFamilyIndex == VK_QUEUE_FAMILY_EXTERNAL || |
| barrier.srcQueueFamilyIndex == VK_QUEUE_FAMILY_FOREIGN_EXT) && |
| barrier.oldLayout == VK_IMAGE_LAYOUT_UNDEFINED) { |
| // This is not a complete solution as the Vulkan spec does not require that |
| // Vulkan drivers perform a no-op in the case when oldLayout equals newLayout |
| // but this has been observed to be enough to work for now to avoid clearing |
| // out images. |
| // TODO(b/236179843): figure out long term solution. |
| barrier.oldLayout = barrier.newLayout; |
| } |
| #endif |
| |
| updatedImageMemoryBarriers.push_back(barrier); |
| } |
| |
| enc->vkCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, |
| memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, |
| pBufferMemoryBarriers, updatedImageMemoryBarriers.size(), |
| updatedImageMemoryBarriers.data(), true /* do lock */); |
| } |
| |
| void ResourceTracker::on_vkDestroyDescriptorSetLayout(void* context, VkDevice device, |
| VkDescriptorSetLayout descriptorSetLayout, |
| const VkAllocationCallbacks* pAllocator) { |
| decDescriptorSetLayoutRef(context, device, descriptorSetLayout, pAllocator); |
| } |
| |
| VkResult ResourceTracker::on_vkAllocateCommandBuffers( |
| void* context, VkResult input_result, VkDevice device, |
| const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers) { |
| (void)input_result; |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| VkResult res = |
| enc->vkAllocateCommandBuffers(device, pAllocateInfo, pCommandBuffers, true /* do lock */); |
| if (VK_SUCCESS != res) return res; |
| |
| for (uint32_t i = 0; i < pAllocateInfo->commandBufferCount; ++i) { |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(pCommandBuffers[i]); |
| cb->isSecondary = pAllocateInfo->level == VK_COMMAND_BUFFER_LEVEL_SECONDARY; |
| cb->device = device; |
| } |
| |
| return res; |
| } |
| |
| #if defined(VK_USE_PLATFORM_ANDROID_KHR) |
| VkResult ResourceTracker::exportSyncFdForQSRILocked(VkImage image, int* fd) { |
| mesa_logi("%s: call for image %p hos timage handle 0x%llx\n", __func__, (void*)image, |
| (unsigned long long)get_host_u64_VkImage(image)); |
| |
| if (mFeatureInfo->hasVirtioGpuNativeSync) { |
| struct VirtGpuExecBuffer exec = {}; |
| struct gfxstreamCreateQSRIExportVK exportQSRI = {}; |
| VirtGpuDevice* instance = VirtGpuDevice::getInstance(); |
| |
| uint64_t hostImageHandle = get_host_u64_VkImage(image); |
| |
| exportQSRI.hdr.opCode = GFXSTREAM_CREATE_QSRI_EXPORT_VK; |
| exportQSRI.imageHandleLo = (uint32_t)hostImageHandle; |
| exportQSRI.imageHandleHi = (uint32_t)(hostImageHandle >> 32); |
| |
| exec.command = static_cast<void*>(&exportQSRI); |
| exec.command_size = sizeof(exportQSRI); |
| exec.flags = kFenceOut | kRingIdx; |
| if (instance->execBuffer(exec, nullptr)) return VK_ERROR_OUT_OF_HOST_MEMORY; |
| |
| *fd = exec.handle.osHandle; |
| } else { |
| #if GFXSTREAM_ENABLE_GUEST_GOLDFISH |
| ensureSyncDeviceFd(); |
| goldfish_sync_queue_work( |
| mSyncDeviceFd, get_host_u64_VkImage(image) /* the handle */, |
| GOLDFISH_SYNC_VULKAN_QSRI /* thread handle (doubling as type field) */, fd); |
| #endif |
| } |
| |
| mesa_logi("%s: got fd: %d\n", __func__, *fd); |
| auto imageInfoIt = info_VkImage.find(image); |
| if (imageInfoIt != info_VkImage.end()) { |
| auto& imageInfo = imageInfoIt->second; |
| |
| auto* syncHelper = |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->syncHelper(); |
| |
| // Remove any pending QSRI sync fds that are already signaled. |
| auto syncFdIt = imageInfo.pendingQsriSyncFds.begin(); |
| while (syncFdIt != imageInfo.pendingQsriSyncFds.end()) { |
| int syncFd = *syncFdIt; |
| int syncWaitRet = syncHelper->wait(syncFd, /*timeout msecs*/ 0); |
| if (syncWaitRet == 0) { |
| // Sync fd is signaled. |
| syncFdIt = imageInfo.pendingQsriSyncFds.erase(syncFdIt); |
| syncHelper->close(syncFd); |
| } else { |
| if (errno != ETIME) { |
| mesa_loge("%s: Failed to wait for pending QSRI sync: sterror: %s errno: %d", |
| __func__, strerror(errno), errno); |
| } |
| break; |
| } |
| } |
| |
| int syncFdDup = syncHelper->dup(*fd); |
| if (syncFdDup < 0) { |
| mesa_loge("%s: Failed to dup() QSRI sync fd : sterror: %s errno: %d", __func__, |
| strerror(errno), errno); |
| } else { |
| imageInfo.pendingQsriSyncFds.push_back(syncFdDup); |
| } |
| } |
| |
| return VK_SUCCESS; |
| } |
| |
| VkResult ResourceTracker::on_vkQueueSignalReleaseImageANDROID(void* context, VkResult input_result, |
| VkQueue queue, |
| uint32_t waitSemaphoreCount, |
| const VkSemaphore* pWaitSemaphores, |
| VkImage image, int* pNativeFenceFd) { |
| (void)input_result; |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| if (!mFeatureInfo->hasVulkanAsyncQsri) { |
| return enc->vkQueueSignalReleaseImageANDROID(queue, waitSemaphoreCount, pWaitSemaphores, |
| image, pNativeFenceFd, true /* lock */); |
| } |
| |
| { |
| AutoLock<RecursiveLock> lock(mLock); |
| auto it = info_VkImage.find(image); |
| if (it == info_VkImage.end()) { |
| if (pNativeFenceFd) *pNativeFenceFd = -1; |
| return VK_ERROR_INITIALIZATION_FAILED; |
| } |
| } |
| |
| enc->vkQueueSignalReleaseImageANDROIDAsyncGOOGLE(queue, waitSemaphoreCount, pWaitSemaphores, |
| image, true /* lock */); |
| |
| AutoLock<RecursiveLock> lock(mLock); |
| VkResult result; |
| if (pNativeFenceFd) { |
| result = exportSyncFdForQSRILocked(image, pNativeFenceFd); |
| } else { |
| int syncFd; |
| result = exportSyncFdForQSRILocked(image, &syncFd); |
| |
| if (syncFd >= 0) { |
| auto* syncHelper = |
| ResourceTracker::threadingCallbacks.hostConnectionGetFunc()->syncHelper(); |
| syncHelper->close(syncFd); |
| } |
| } |
| |
| return result; |
| } |
| #endif |
| |
| VkResult ResourceTracker::on_vkCreateGraphicsPipelines( |
| void* context, VkResult input_result, VkDevice device, VkPipelineCache pipelineCache, |
| uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo* pCreateInfos, |
| const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines) { |
| (void)input_result; |
| VkEncoder* enc = (VkEncoder*)context; |
| std::vector<VkGraphicsPipelineCreateInfo> localCreateInfos(pCreateInfos, |
| pCreateInfos + createInfoCount); |
| for (VkGraphicsPipelineCreateInfo& graphicsPipelineCreateInfo : localCreateInfos) { |
| // dEQP-VK.api.pipeline.pipeline_invalid_pointers_unused_structs#graphics |
| bool requireViewportState = false; |
| // VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00750 |
| requireViewportState |= |
| graphicsPipelineCreateInfo.pRasterizationState != nullptr && |
| graphicsPipelineCreateInfo.pRasterizationState->rasterizerDiscardEnable == VK_FALSE; |
| // VUID-VkGraphicsPipelineCreateInfo-pViewportState-04892 |
| #ifdef VK_EXT_extended_dynamic_state2 |
| if (!requireViewportState && graphicsPipelineCreateInfo.pDynamicState) { |
| for (uint32_t i = 0; i < graphicsPipelineCreateInfo.pDynamicState->dynamicStateCount; |
| i++) { |
| if (VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT == |
| graphicsPipelineCreateInfo.pDynamicState->pDynamicStates[i]) { |
| requireViewportState = true; |
| break; |
| } |
| } |
| } |
| #endif // VK_EXT_extended_dynamic_state2 |
| if (!requireViewportState) { |
| graphicsPipelineCreateInfo.pViewportState = nullptr; |
| } |
| |
| // It has the same requirement as for pViewportState. |
| bool shouldIncludeFragmentShaderState = requireViewportState; |
| |
| // VUID-VkGraphicsPipelineCreateInfo-rasterizerDiscardEnable-00751 |
| if (!shouldIncludeFragmentShaderState) { |
| graphicsPipelineCreateInfo.pMultisampleState = nullptr; |
| } |
| |
| bool forceDepthStencilState = false; |
| bool forceColorBlendState = false; |
| |
| const VkPipelineRenderingCreateInfo* pipelineRenderingInfo = |
| vk_find_struct<VkPipelineRenderingCreateInfo>(&graphicsPipelineCreateInfo); |
| |
| if (pipelineRenderingInfo) { |
| forceDepthStencilState |= |
| pipelineRenderingInfo->depthAttachmentFormat != VK_FORMAT_UNDEFINED; |
| forceDepthStencilState |= |
| pipelineRenderingInfo->stencilAttachmentFormat != VK_FORMAT_UNDEFINED; |
| forceColorBlendState |= pipelineRenderingInfo->colorAttachmentCount != 0; |
| } |
| |
| // VUID-VkGraphicsPipelineCreateInfo-renderPass-06043 |
| // VUID-VkGraphicsPipelineCreateInfo-renderPass-06044 |
| if (graphicsPipelineCreateInfo.renderPass == VK_NULL_HANDLE || |
| !shouldIncludeFragmentShaderState) { |
| // VUID-VkGraphicsPipelineCreateInfo-renderPass-06053 |
| if (!forceDepthStencilState) { |
| graphicsPipelineCreateInfo.pDepthStencilState = nullptr; |
| } |
| if (!forceColorBlendState) { |
| graphicsPipelineCreateInfo.pColorBlendState = nullptr; |
| } |
| } |
| } |
| return enc->vkCreateGraphicsPipelines(device, pipelineCache, localCreateInfos.size(), |
| localCreateInfos.data(), pAllocator, pPipelines, |
| true /* do lock */); |
| } |
| |
| uint32_t ResourceTracker::getApiVersionFromInstance(VkInstance instance) const { |
| AutoLock<RecursiveLock> lock(mLock); |
| uint32_t api = kDefaultApiVersion; |
| |
| auto it = info_VkInstance.find(instance); |
| if (it == info_VkInstance.end()) return api; |
| |
| api = it->second.highestApiVersion; |
| |
| return api; |
| } |
| |
| uint32_t ResourceTracker::getApiVersionFromDevice(VkDevice device) const { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| uint32_t api = kDefaultApiVersion; |
| |
| auto it = info_VkDevice.find(device); |
| if (it == info_VkDevice.end()) return api; |
| |
| api = it->second.apiVersion; |
| |
| return api; |
| } |
| |
| bool ResourceTracker::hasInstanceExtension(VkInstance instance, const std::string& name) const { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkInstance.find(instance); |
| if (it == info_VkInstance.end()) return false; |
| |
| return it->second.enabledExtensions.find(name) != it->second.enabledExtensions.end(); |
| } |
| |
| bool ResourceTracker::hasDeviceExtension(VkDevice device, const std::string& name) const { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto it = info_VkDevice.find(device); |
| if (it == info_VkDevice.end()) return false; |
| |
| return it->second.enabledExtensions.find(name) != it->second.enabledExtensions.end(); |
| } |
| |
| VkDevice ResourceTracker::getDevice(VkCommandBuffer commandBuffer) const { |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(commandBuffer); |
| if (!cb) { |
| return nullptr; |
| } |
| return cb->device; |
| } |
| |
| // Resets staging stream for this command buffer and primary command buffers |
| // where this command buffer has been recorded. If requested, also clears the pending |
| // descriptor sets. |
| void ResourceTracker::resetCommandBufferStagingInfo(VkCommandBuffer commandBuffer, |
| bool alsoResetPrimaries, |
| bool alsoClearPendingDescriptorSets) { |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(commandBuffer); |
| if (!cb) { |
| return; |
| } |
| if (cb->privateEncoder) { |
| sStaging.pushStaging((CommandBufferStagingStream*)cb->privateStream, cb->privateEncoder); |
| cb->privateEncoder = nullptr; |
| cb->privateStream = nullptr; |
| } |
| |
| if (alsoClearPendingDescriptorSets && cb->userPtr) { |
| CommandBufferPendingDescriptorSets* pendingSets = |
| (CommandBufferPendingDescriptorSets*)cb->userPtr; |
| pendingSets->sets.clear(); |
| } |
| |
| if (alsoResetPrimaries) { |
| forAllObjects(cb->superObjects, [this, alsoResetPrimaries, |
| alsoClearPendingDescriptorSets](void* obj) { |
| VkCommandBuffer superCommandBuffer = (VkCommandBuffer)obj; |
| struct goldfish_VkCommandBuffer* superCb = |
| as_goldfish_VkCommandBuffer(superCommandBuffer); |
| this->resetCommandBufferStagingInfo(superCommandBuffer, alsoResetPrimaries, |
| alsoClearPendingDescriptorSets); |
| }); |
| eraseObjects(&cb->superObjects); |
| } |
| |
| forAllObjects(cb->subObjects, [cb](void* obj) { |
| VkCommandBuffer subCommandBuffer = (VkCommandBuffer)obj; |
| struct goldfish_VkCommandBuffer* subCb = as_goldfish_VkCommandBuffer(subCommandBuffer); |
| // We don't do resetCommandBufferStagingInfo(subCommandBuffer) |
| // since the user still might have submittable stuff pending there. |
| eraseObject(&subCb->superObjects, (void*)cb); |
| }); |
| |
| eraseObjects(&cb->subObjects); |
| } |
| |
| // Unlike resetCommandBufferStagingInfo, this does not always erase its |
| // superObjects pointers because the command buffer has merely been |
| // submitted, not reset. However, if the command buffer was recorded with |
| // ONE_TIME_SUBMIT_BIT, then it will also reset its primaries. |
| // |
| // Also, we save the set of descriptor sets referenced by this command |
| // buffer because we only submitted the command buffer and it's possible to |
| // update the descriptor set again and re-submit the same command without |
| // recording it (Update-after-bind descriptor sets) |
| void ResourceTracker::resetCommandBufferPendingTopology(VkCommandBuffer commandBuffer) { |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(commandBuffer); |
| if (cb->flags & VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) { |
| resetCommandBufferStagingInfo(commandBuffer, true /* reset primaries */, |
| true /* clear pending descriptor sets */); |
| } else { |
| resetCommandBufferStagingInfo(commandBuffer, false /* Don't reset primaries */, |
| false /* Don't clear pending descriptor sets */); |
| } |
| } |
| |
| void ResourceTracker::resetCommandPoolStagingInfo(VkCommandPool commandPool) { |
| struct goldfish_VkCommandPool* p = as_goldfish_VkCommandPool(commandPool); |
| |
| if (!p) return; |
| |
| forAllObjects(p->subObjects, [this](void* commandBuffer) { |
| this->resetCommandBufferStagingInfo((VkCommandBuffer)commandBuffer, |
| true /* also reset primaries */, |
| true /* also clear pending descriptor sets */); |
| }); |
| } |
| |
| void ResourceTracker::addToCommandPool(VkCommandPool commandPool, uint32_t commandBufferCount, |
| VkCommandBuffer* pCommandBuffers) { |
| for (uint32_t i = 0; i < commandBufferCount; ++i) { |
| struct goldfish_VkCommandPool* p = as_goldfish_VkCommandPool(commandPool); |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(pCommandBuffers[i]); |
| appendObject(&p->subObjects, (void*)(pCommandBuffers[i])); |
| appendObject(&cb->poolObjects, (void*)commandPool); |
| } |
| } |
| |
| void ResourceTracker::clearCommandPool(VkCommandPool commandPool) { |
| resetCommandPoolStagingInfo(commandPool); |
| struct goldfish_VkCommandPool* p = as_goldfish_VkCommandPool(commandPool); |
| forAllObjects(p->subObjects, [this](void* commandBuffer) { |
| this->unregister_VkCommandBuffer((VkCommandBuffer)commandBuffer); |
| }); |
| eraseObjects(&p->subObjects); |
| } |
| |
| const VkPhysicalDeviceMemoryProperties& ResourceTracker::getPhysicalDeviceMemoryProperties( |
| void* context, VkDevice device, VkPhysicalDevice physicalDevice) { |
| if (!mCachedPhysicalDeviceMemoryProps) { |
| if (physicalDevice == VK_NULL_HANDLE) { |
| AutoLock<RecursiveLock> lock(mLock); |
| |
| auto deviceInfoIt = info_VkDevice.find(device); |
| if (deviceInfoIt == info_VkDevice.end()) { |
| mesa_loge("Failed to pass device or physical device."); |
| abort(); |
| } |
| const auto& deviceInfo = deviceInfoIt->second; |
| physicalDevice = deviceInfo.physdev; |
| } |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| VkPhysicalDeviceMemoryProperties properties; |
| enc->vkGetPhysicalDeviceMemoryProperties(physicalDevice, &properties, true /* no lock */); |
| |
| mCachedPhysicalDeviceMemoryProps.emplace(std::move(properties)); |
| } |
| return *mCachedPhysicalDeviceMemoryProps; |
| } |
| |
| static ResourceTracker* sTracker = nullptr; |
| |
| ResourceTracker::ResourceTracker() { |
| mCreateMapping = new CreateMapping(); |
| mDestroyMapping = new DestroyMapping(); |
| // nothing to do |
| } |
| |
| ResourceTracker::~ResourceTracker() { |
| delete mCreateMapping; |
| delete mDestroyMapping; |
| } |
| |
| VulkanHandleMapping* ResourceTracker::createMapping() { return mCreateMapping; } |
| |
| VulkanHandleMapping* ResourceTracker::destroyMapping() { return mDestroyMapping; } |
| |
| // static |
| ResourceTracker* ResourceTracker::get() { |
| if (!sTracker) { |
| // To be initialized once on vulkan device open. |
| sTracker = new ResourceTracker; |
| } |
| return sTracker; |
| } |
| |
| // static |
| ALWAYS_INLINE_GFXSTREAM VkEncoder* ResourceTracker::getCommandBufferEncoder( |
| VkCommandBuffer commandBuffer) { |
| if (!(ResourceTracker::streamFeatureBits & |
| VULKAN_STREAM_FEATURE_QUEUE_SUBMIT_WITH_COMMANDS_BIT)) { |
| auto enc = ResourceTracker::getThreadLocalEncoder(); |
| ResourceTracker::get()->syncEncodersForCommandBuffer(commandBuffer, enc); |
| return enc; |
| } |
| |
| struct goldfish_VkCommandBuffer* cb = as_goldfish_VkCommandBuffer(commandBuffer); |
| if (!cb->privateEncoder) { |
| sStaging.setAllocFree(ResourceTracker::get()->getAlloc(), |
| ResourceTracker::get()->getFree()); |
| sStaging.popStaging((CommandBufferStagingStream**)&cb->privateStream, &cb->privateEncoder); |
| } |
| uint8_t* writtenPtr; |
| size_t written; |
| ((CommandBufferStagingStream*)cb->privateStream)->getWritten(&writtenPtr, &written); |
| return cb->privateEncoder; |
| } |
| |
| // static |
| ALWAYS_INLINE_GFXSTREAM VkEncoder* ResourceTracker::getQueueEncoder(VkQueue queue) { |
| auto enc = ResourceTracker::getThreadLocalEncoder(); |
| if (!(ResourceTracker::streamFeatureBits & |
| VULKAN_STREAM_FEATURE_QUEUE_SUBMIT_WITH_COMMANDS_BIT)) { |
| ResourceTracker::get()->syncEncodersForQueue(queue, enc); |
| } |
| return enc; |
| } |
| |
| // static |
| ALWAYS_INLINE_GFXSTREAM VkEncoder* ResourceTracker::getThreadLocalEncoder() { |
| auto hostConn = ResourceTracker::threadingCallbacks.hostConnectionGetFunc(); |
| auto vkEncoder = ResourceTracker::threadingCallbacks.vkEncoderGetFunc(hostConn); |
| return vkEncoder; |
| } |
| |
| // static |
| void ResourceTracker::setSeqnoPtr(uint32_t* seqnoptr) { sSeqnoPtr = seqnoptr; } |
| |
| // static |
| ALWAYS_INLINE_GFXSTREAM uint32_t ResourceTracker::nextSeqno() { |
| uint32_t res = __atomic_add_fetch(sSeqnoPtr, 1, __ATOMIC_SEQ_CST); |
| return res; |
| } |
| |
| // static |
| ALWAYS_INLINE_GFXSTREAM uint32_t ResourceTracker::getSeqno() { |
| uint32_t res = __atomic_load_n(sSeqnoPtr, __ATOMIC_SEQ_CST); |
| return res; |
| } |
| |
| void ResourceTracker::transformImpl_VkExternalMemoryProperties_tohost(VkExternalMemoryProperties*, |
| uint32_t) {} |
| |
| void ResourceTracker::transformImpl_VkImageCreateInfo_fromhost(const VkImageCreateInfo*, uint32_t) { |
| } |
| void ResourceTracker::transformImpl_VkImageCreateInfo_tohost(const VkImageCreateInfo*, uint32_t) {} |
| |
| #define DEFINE_TRANSFORMED_TYPE_IMPL(type) \ |
| void ResourceTracker::transformImpl_##type##_tohost(type*, uint32_t) {} \ |
| void ResourceTracker::transformImpl_##type##_fromhost(type*, uint32_t) {} |
| |
| LIST_TRIVIAL_TRANSFORMED_TYPES(DEFINE_TRANSFORMED_TYPE_IMPL) |
| |
| } // namespace vk |
| } // namespace gfxstream |