| // Copyright 2024 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expresso or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #pragma once |
| |
| #include <vulkan/vulkan.h> |
| |
| #ifdef _WIN32 |
| #include <malloc.h> |
| #endif |
| |
| #include <stdlib.h> |
| |
| #include <set> |
| #include <string> |
| |
| #include "DeviceOpTracker.h" |
| #include "Handle.h" |
| #include "VkEmulatedPhysicalDeviceMemory.h" |
| #include "aemu/base/files/Stream.h" |
| #include "aemu/base/memory/SharedMemory.h" |
| #include "aemu/base/synchronization/ConditionVariable.h" |
| #include "aemu/base/synchronization/Lock.h" |
| #include "common/goldfish_vk_deepcopy.h" |
| #include "vulkan/VkAndroidNativeBuffer.h" |
| #include "vulkan/VkFormatUtils.h" |
| #include "vulkan/emulated_textures/CompressedImageInfo.h" |
| |
| namespace gfxstream { |
| namespace vk { |
| |
| template <class TDispatch> |
| class ExternalFencePool { |
| public: |
| ExternalFencePool(TDispatch* dispatch, VkDevice device) |
| : m_vk(dispatch), mDevice(device), mMaxSize(5) {} |
| |
| ~ExternalFencePool() { |
| if (!mPool.empty()) { |
| GFXSTREAM_ABORT(emugl::FatalError(emugl::ABORT_REASON_OTHER)) |
| << "External fence pool for device " << static_cast<void*>(mDevice) |
| << " destroyed but " << mPool.size() << " fences still not destroyed."; |
| } |
| } |
| |
| void add(VkFence fence) { |
| android::base::AutoLock lock(mLock); |
| mPool.push_back(fence); |
| if (mPool.size() > mMaxSize) { |
| INFO("External fence pool for %p has increased to size %d", mDevice, mPool.size()); |
| mMaxSize = mPool.size(); |
| } |
| } |
| |
| VkFence pop(const VkFenceCreateInfo* pCreateInfo) { |
| VkFence fence = VK_NULL_HANDLE; |
| { |
| android::base::AutoLock lock(mLock); |
| auto it = std::find_if(mPool.begin(), mPool.end(), [this](const VkFence& fence) { |
| VkResult status = m_vk->vkGetFenceStatus(mDevice, fence); |
| if (status != VK_SUCCESS) { |
| if (status != VK_NOT_READY) { |
| VK_CHECK(status); |
| } |
| |
| // Status is valid, but fence is not yet signaled |
| return false; |
| } |
| return true; |
| }); |
| if (it == mPool.end()) { |
| return VK_NULL_HANDLE; |
| } |
| |
| fence = *it; |
| mPool.erase(it); |
| } |
| |
| if (!(pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT)) { |
| VK_CHECK(m_vk->vkResetFences(mDevice, 1, &fence)); |
| } |
| |
| return fence; |
| } |
| |
| std::vector<VkFence> popAll() { |
| android::base::AutoLock lock(mLock); |
| std::vector<VkFence> popped = mPool; |
| mPool.clear(); |
| return popped; |
| } |
| |
| private: |
| TDispatch* m_vk; |
| VkDevice mDevice; |
| android::base::Lock mLock; |
| std::vector<VkFence> mPool; |
| int mMaxSize; |
| }; |
| |
| class PrivateMemory { |
| public: |
| PrivateMemory(size_t alignment, size_t size) { |
| #ifdef _WIN32 |
| mAddr = _aligned_malloc(size, alignment); |
| #else |
| mAddr = aligned_alloc(alignment, size); |
| #endif |
| } |
| ~PrivateMemory() { |
| if (mAddr) { |
| #ifdef _WIN32 |
| _aligned_free(mAddr); |
| #else |
| free(mAddr); |
| #endif |
| mAddr = nullptr; |
| } |
| } |
| void* getAddr() { |
| return mAddr; |
| } |
| private: |
| void* mAddr{nullptr}; |
| }; |
| |
| // We always map the whole size on host. |
| // This makes it much easier to implement |
| // the memory map API. |
| struct MemoryInfo { |
| // This indicates whether the VkDecoderGlobalState needs to clean up |
| // and unmap the mapped memory; only the owner of the mapped memory |
| // should call unmap. |
| bool needUnmap = false; |
| // When ptr is null, it means the VkDeviceMemory object |
| // was not allocated with the HOST_VISIBLE property. |
| void* ptr = nullptr; |
| VkDeviceSize size; |
| // GLDirectMem info |
| bool directMapped = false; |
| bool virtioGpuMapped = false; |
| uint32_t caching = 0; |
| uint64_t guestPhysAddr = 0; |
| void* pageAlignedHva = nullptr; |
| uint64_t sizeToPage = 0; |
| uint64_t hostmemId = 0; |
| VkDevice device = VK_NULL_HANDLE; |
| uint32_t memoryIndex = 0; |
| // Set if the memory is backed by shared memory. |
| std::optional<android::base::SharedMemory> sharedMemory; |
| |
| std::shared_ptr<PrivateMemory> privateMemory; |
| // virtio-gpu blobs |
| uint64_t blobId = 0; |
| |
| // Buffer, provided via vkAllocateMemory(). |
| std::optional<HandleType> boundBuffer; |
| // ColorBuffer, provided via vkAllocateMemory(). |
| std::optional<HandleType> boundColorBuffer; |
| }; |
| |
| struct InstanceInfo { |
| std::vector<std::string> enabledExtensionNames; |
| uint32_t apiVersion = VK_MAKE_VERSION(1, 0, 0); |
| VkInstance boxed = nullptr; |
| bool isAngle = false; |
| std::string applicationName; |
| std::string engineName; |
| }; |
| |
| struct PhysicalDeviceInfo { |
| VkInstance instance = VK_NULL_HANDLE; |
| VkPhysicalDeviceProperties props; |
| std::unique_ptr<EmulatedPhysicalDeviceMemoryProperties> memoryPropertiesHelper; |
| std::vector<VkQueueFamilyProperties> queueFamilyProperties; |
| VkPhysicalDevice boxed = nullptr; |
| }; |
| |
| struct ExternalFenceInfo { |
| VkExternalSemaphoreHandleTypeFlagBits supportedBinarySemaphoreHandleTypes; |
| VkExternalFenceHandleTypeFlagBits supportedFenceHandleTypes; |
| }; |
| |
| struct DeviceInfo { |
| std::unordered_map<uint32_t, std::vector<VkQueue>> queues; |
| std::vector<std::string> enabledExtensionNames; |
| bool emulateTextureEtc2 = false; |
| bool emulateTextureAstc = false; |
| bool useAstcCpuDecompression = false; |
| |
| ExternalFenceInfo externalFenceInfo; |
| VkPhysicalDevice physicalDevice; |
| VkDevice boxed = nullptr; |
| DebugUtilsHelper debugUtilsHelper = DebugUtilsHelper::withUtilsDisabled(); |
| std::unique_ptr<ExternalFencePool<VulkanDispatch>> externalFencePool = nullptr; |
| std::set<VkFormat> imageFormats = {}; // image formats used on this device |
| std::unique_ptr<GpuDecompressionPipelineManager> decompPipelines = nullptr; |
| DeviceOpTrackerPtr deviceOpTracker = nullptr; |
| |
| // True if this is a compressed image that needs to be decompressed on the GPU (with our |
| // compute shader) |
| bool needGpuDecompression(const CompressedImageInfo& cmpInfo) { |
| return ((cmpInfo.isEtc2() && emulateTextureEtc2) || |
| (cmpInfo.isAstc() && emulateTextureAstc && !useAstcCpuDecompression)); |
| } |
| bool needEmulatedDecompression(const CompressedImageInfo& cmpInfo) { |
| return ((cmpInfo.isEtc2() && emulateTextureEtc2) || |
| (cmpInfo.isAstc() && emulateTextureAstc)); |
| } |
| bool needEmulatedDecompression(VkFormat format) { |
| return (gfxstream::vk::isEtc2(format) && emulateTextureEtc2) || |
| (gfxstream::vk::isAstc(format) && emulateTextureAstc); |
| } |
| }; |
| |
| struct QueueInfo { |
| android::base::Lock* lock = nullptr; |
| VkDevice device; |
| uint32_t queueFamilyIndex; |
| VkQueue boxed = nullptr; |
| uint32_t sequenceNumber = 0; |
| }; |
| |
| struct BufferInfo { |
| VkDevice device; |
| VkBufferUsageFlags usage; |
| VkDeviceMemory memory = 0; |
| VkDeviceSize memoryOffset = 0; |
| VkDeviceSize size; |
| std::shared_ptr<bool> alive{new bool(true)}; |
| }; |
| |
| struct ImageInfo { |
| VkDevice device; |
| VkImageCreateInfo imageCreateInfoShallow; |
| std::shared_ptr<AndroidNativeBufferInfo> anbInfo; |
| CompressedImageInfo cmpInfo; |
| // ColorBuffer, provided via vkAllocateMemory(). |
| std::optional<HandleType> boundColorBuffer; |
| // TODO: might need to use an array of layouts to represent each sub resource |
| VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; |
| VkDeviceMemory memory = VK_NULL_HANDLE; |
| }; |
| |
| struct ImageViewInfo { |
| VkDevice device; |
| bool needEmulatedAlpha = false; |
| |
| // Color buffer, provided via vkAllocateMemory(). |
| std::optional<HandleType> boundColorBuffer; |
| std::shared_ptr<bool> alive{new bool(true)}; |
| }; |
| |
| struct SamplerInfo { |
| VkDevice device; |
| bool needEmulatedAlpha = false; |
| VkSamplerCreateInfo createInfo = {}; |
| VkSampler emulatedborderSampler = VK_NULL_HANDLE; |
| android::base::BumpPool pool = android::base::BumpPool(256); |
| SamplerInfo() = default; |
| SamplerInfo& operator=(const SamplerInfo& other) { |
| deepcopy_VkSamplerCreateInfo(&pool, VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, |
| &other.createInfo, &createInfo); |
| device = other.device; |
| needEmulatedAlpha = other.needEmulatedAlpha; |
| emulatedborderSampler = other.emulatedborderSampler; |
| return *this; |
| } |
| SamplerInfo(const SamplerInfo& other) { *this = other; } |
| SamplerInfo(SamplerInfo&& other) = delete; |
| SamplerInfo& operator=(SamplerInfo&& other) = delete; |
| std::shared_ptr<bool> alive{new bool(true)}; |
| }; |
| |
| struct FenceInfo { |
| VkDevice device = VK_NULL_HANDLE; |
| VkFence boxed = VK_NULL_HANDLE; |
| VulkanDispatch* vk = nullptr; |
| |
| android::base::StaticLock lock; |
| android::base::ConditionVariable cv; |
| |
| enum class State { |
| kWaitable, |
| kNotWaitable, |
| kWaiting, |
| }; |
| State state = State::kNotWaitable; |
| |
| bool external = false; |
| |
| // If this fence was used in an additional host operation that must be waited |
| // upon before destruction (e.g. as part of a vkAcquireImageANDROID() call), |
| // the waitable that tracking that host operation. |
| std::optional<DeviceOpWaitable> latestUse; |
| }; |
| |
| struct SemaphoreInfo { |
| VkDevice device; |
| int externalHandleId = 0; |
| VK_EXT_SYNC_HANDLE externalHandle = VK_EXT_SYNC_HANDLE_INVALID; |
| // If this fence was used in an additional host operation that must be waited |
| // upon before destruction (e.g. as part of a vkAcquireImageANDROID() call), |
| // the waitable that tracking that host operation. |
| std::optional<DeviceOpWaitable> latestUse; |
| }; |
| struct DescriptorSetLayoutInfo { |
| VkDevice device = 0; |
| VkDescriptorSetLayout boxed = 0; |
| VkDescriptorSetLayoutCreateInfo createInfo; |
| std::vector<VkDescriptorSetLayoutBinding> bindings; |
| }; |
| |
| struct DescriptorPoolInfo { |
| VkDevice device = 0; |
| VkDescriptorPool boxed = 0; |
| struct PoolState { |
| VkDescriptorType type; |
| uint32_t descriptorCount; |
| uint32_t used; |
| }; |
| |
| VkDescriptorPoolCreateInfo createInfo; |
| uint32_t maxSets; |
| uint32_t usedSets; |
| std::vector<PoolState> pools; |
| |
| std::unordered_map<VkDescriptorSet, VkDescriptorSet> allocedSetsToBoxed; |
| std::vector<uint64_t> poolIds; |
| }; |
| |
| struct DescriptorSetInfo { |
| enum DescriptorWriteType { |
| Empty = 0, |
| ImageInfo = 1, |
| BufferInfo = 2, |
| BufferView = 3, |
| InlineUniformBlock = 4, |
| AccelerationStructure = 5, |
| }; |
| |
| struct DescriptorWrite { |
| VkDescriptorType descriptorType; |
| DescriptorWriteType writeType = DescriptorWriteType::Empty; |
| uint32_t dstArrayElement; // Only used for inlineUniformBlock and accelerationStructure. |
| |
| union { |
| VkDescriptorImageInfo imageInfo; |
| VkDescriptorBufferInfo bufferInfo; |
| VkBufferView bufferView; |
| VkWriteDescriptorSetInlineUniformBlockEXT inlineUniformBlock; |
| VkWriteDescriptorSetAccelerationStructureKHR accelerationStructure; |
| }; |
| |
| std::vector<uint8_t> inlineUniformBlockBuffer; |
| // Weak pointer(s) to detect if all objects on dependency chain are alive. |
| std::vector<std::weak_ptr<bool>> alives; |
| std::optional<HandleType> boundColorBuffer; |
| }; |
| |
| VkDescriptorPool pool; |
| VkDescriptorSetLayout unboxedLayout = 0; |
| std::vector<std::vector<DescriptorWrite>> allWrites; |
| std::vector<VkDescriptorSetLayoutBinding> bindings; |
| }; |
| |
| struct ShaderModuleInfo { |
| VkDevice device; |
| }; |
| |
| struct PipelineCacheInfo { |
| VkDevice device; |
| }; |
| |
| struct PipelineInfo { |
| VkDevice device; |
| }; |
| |
| struct RenderPassInfo { |
| VkDevice device; |
| }; |
| |
| struct FramebufferInfo { |
| VkDevice device; |
| std::vector<HandleType> attachedColorBuffers; |
| }; |
| } // namespace vk |
| } // namespace gfxstream |