/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef GrVkResourceProvider_DEFINED #define GrVkResourceProvider_DEFINED #include "include/gpu/vk/GrVkTypes.h" #include "include/private/SkMutex.h" #include "include/private/SkTArray.h" #include "src/core/SkLRUCache.h" #include "src/core/SkTDynamicHash.h" #include "src/core/SkTInternalLList.h" #include "src/gpu/GrGpu.h" #include "src/gpu/GrManagedResource.h" #include "src/gpu/GrProgramDesc.h" #include "src/gpu/GrResourceHandle.h" #include "src/gpu/GrThreadSafePipelineBuilder.h" #include "src/gpu/vk/GrVkDescriptorPool.h" #include "src/gpu/vk/GrVkDescriptorSetManager.h" #include "src/gpu/vk/GrVkPipelineStateBuilder.h" #include "src/gpu/vk/GrVkRenderPass.h" #include "src/gpu/vk/GrVkSampler.h" #include "src/gpu/vk/GrVkSamplerYcbcrConversion.h" #include "src/gpu/vk/GrVkUtil.h" class GrVkCommandPool; class GrVkGpu; class GrVkPipeline; class GrVkPipelineState; class GrVkPrimaryCommandBuffer; class GrVkRenderTarget; class GrVkSecondaryCommandBuffer; class GrVkUniformHandler; class GrVkResourceProvider { public: GrVkResourceProvider(GrVkGpu* gpu); ~GrVkResourceProvider(); GrThreadSafePipelineBuilder* pipelineStateCache() { return fPipelineStateCache.get(); } sk_sp refPipelineStateCache() { return fPipelineStateCache; } // Set up any initial vk objects void init(); sk_sp makePipeline(const GrProgramInfo&, VkPipelineShaderStageCreateInfo* shaderStageInfo, int shaderStageCount, VkRenderPass compatibleRenderPass, VkPipelineLayout layout, uint32_t subpass); GR_DEFINE_RESOURCE_HANDLE_CLASS(CompatibleRPHandle); using SelfDependencyFlags = GrVkRenderPass::SelfDependencyFlags; using LoadFromResolve = GrVkRenderPass::LoadFromResolve; // Finds or creates a simple render pass that matches the target, increments the refcount, // and returns. The caller can optionally pass in a pointer to a CompatibleRPHandle. If this is // non null it will be set to a handle that can be used in the furutre to quickly return a // compatible GrVkRenderPasses without the need inspecting a GrVkRenderTarget. const GrVkRenderPass* findCompatibleRenderPass(GrVkRenderTarget* target, CompatibleRPHandle* compatibleHandle, bool withResolve, bool withStencil, SelfDependencyFlags selfDepFlags, LoadFromResolve); const GrVkRenderPass* findCompatibleRenderPass(GrVkRenderPass::AttachmentsDescriptor*, GrVkRenderPass::AttachmentFlags, SelfDependencyFlags selfDepFlags, LoadFromResolve, CompatibleRPHandle* compatibleHandle = nullptr); const GrVkRenderPass* findCompatibleExternalRenderPass(VkRenderPass, uint32_t colorAttachmentIndex); // Finds or creates a render pass that matches the target and LoadStoreOps, increments the // refcount, and returns. The caller can optionally pass in a pointer to a CompatibleRPHandle. // If this is non null it will be set to a handle that can be used in the future to quickly // return a GrVkRenderPass without the need to inspect a GrVkRenderTarget. // TODO: sk_sp? const GrVkRenderPass* findRenderPass(GrVkRenderTarget* target, const GrVkRenderPass::LoadStoreOps& colorOps, const GrVkRenderPass::LoadStoreOps& resolveOps, const GrVkRenderPass::LoadStoreOps& stencilOps, CompatibleRPHandle* compatibleHandle, bool withResolve, bool withStencil, SelfDependencyFlags selfDepFlags, LoadFromResolve); // The CompatibleRPHandle must be a valid handle previously set by a call to findRenderPass or // findCompatibleRenderPass. const GrVkRenderPass* findRenderPass(const CompatibleRPHandle& compatibleHandle, const GrVkRenderPass::LoadStoreOps& colorOps, const GrVkRenderPass::LoadStoreOps& resolveOps, const GrVkRenderPass::LoadStoreOps& stencilOps); GrVkCommandPool* findOrCreateCommandPool(); void checkCommandBuffers(); void forceSyncAllCommandBuffers(); // We must add the finishedProc to all active command buffers since we may have flushed work // that the client cares about before they explicitly called flush and the GPU may reorder // command execution. So we make sure all previously submitted work finishes before we call the // finishedProc. void addFinishedProcToActiveCommandBuffers(sk_sp finishedCallback); // Finds or creates a compatible GrVkDescriptorPool for the requested type and count. // The refcount is incremented and a pointer returned. // TODO: Currently this will just create a descriptor pool without holding onto a ref itself // so we currently do not reuse them. Rquires knowing if another draw is currently using // the GrVkDescriptorPool, the ability to reset pools, and the ability to purge pools out // of our cache of GrVkDescriptorPools. GrVkDescriptorPool* findOrCreateCompatibleDescriptorPool(VkDescriptorType type, uint32_t count); // Finds or creates a compatible GrVkSampler based on the GrSamplerState and // GrVkYcbcrConversionInfo. The refcount is incremented and a pointer returned. GrVkSampler* findOrCreateCompatibleSampler(GrSamplerState, const GrVkYcbcrConversionInfo& ycbcrInfo); // Finds or creates a compatible GrVkSamplerYcbcrConversion based on the GrSamplerState and // GrVkYcbcrConversionInfo. The refcount is incremented and a pointer returned. GrVkSamplerYcbcrConversion* findOrCreateCompatibleSamplerYcbcrConversion( const GrVkYcbcrConversionInfo& ycbcrInfo); GrVkPipelineState* findOrCreateCompatiblePipelineState( GrRenderTarget*, const GrProgramInfo&, VkRenderPass compatibleRenderPass, bool overrideSubpassForResolveLoad); GrVkPipelineState* findOrCreateCompatiblePipelineState( const GrProgramDesc&, const GrProgramInfo&, VkRenderPass compatibleRenderPass, GrThreadSafePipelineBuilder::Stats::ProgramCacheResult* stat); sk_sp findOrCreateMSAALoadPipeline( const GrVkRenderPass& renderPass, int numSamples, VkPipelineShaderStageCreateInfo*, VkPipelineLayout); void getSamplerDescriptorSetHandle(VkDescriptorType type, const GrVkUniformHandler&, GrVkDescriptorSetManager::Handle* handle); // This is a convenience function to return a descriptor set for zero sammples. When making a // VkPipelineLayout we must pass in an array of valid descriptor set handles. However, we have // set up our system to have the descriptor sets be in the order uniform, sampler, input. So // if we have a uniform and input we will need to have a valid handle for the sampler as well. // When using the GrVkMSAALoadManager this is the case, but we also don't have a // GrVkUniformHandler to pass into the more general function. Thus we use this call instead. void getZeroSamplerDescriptorSetHandle(GrVkDescriptorSetManager::Handle* handle); // Returns the compatible VkDescriptorSetLayout to use for uniform buffers. The caller does not // own the VkDescriptorSetLayout and thus should not delete it. This function should be used // when the caller needs the layout to create a VkPipelineLayout. VkDescriptorSetLayout getUniformDSLayout() const; // Returns the compatible VkDescriptorSetLayout to use for input attachments. The caller does // not own the VkDescriptorSetLayout and thus should not delete it. This function should be used // when the caller needs the layout to create a VkPipelineLayout. VkDescriptorSetLayout getInputDSLayout() const; // Returns the compatible VkDescriptorSetLayout to use for a specific sampler handle. The caller // does not own the VkDescriptorSetLayout and thus should not delete it. This function should be // used when the caller needs the layout to create a VkPipelineLayout. VkDescriptorSetLayout getSamplerDSLayout(const GrVkDescriptorSetManager::Handle&) const; // Returns a GrVkDescriptorSet that can be used for uniform buffers. The GrVkDescriptorSet // is already reffed for the caller. const GrVkDescriptorSet* getUniformDescriptorSet(); // Returns a GrVkDescriptorSet that can be used for sampler descriptors that are compatible with // the GrVkDescriptorSetManager::Handle passed in. The GrVkDescriptorSet is already reffed for // the caller. const GrVkDescriptorSet* getSamplerDescriptorSet(const GrVkDescriptorSetManager::Handle&); // Returns a GrVkDescriptorSet that can be used for input attachments. The GrVkDescriptorSet // is already reffed for the caller. const GrVkDescriptorSet* getInputDescriptorSet(); // Signals that the descriptor set passed it, which is compatible with the passed in handle, // can be reused by the next allocation request. void recycleDescriptorSet(const GrVkDescriptorSet* descSet, const GrVkDescriptorSetManager::Handle&); void storePipelineCacheData(); // Destroy any cached resources. To be called before destroying the VkDevice. // The assumption is that all queues are idle and all command buffers are finished. // For resource tracing to work properly, this should be called after unrefing all other // resource usages. void destroyResources(); // Currently we just release available command pools (which also releases their buffers). The // command buffers and pools take up the most memory. Other objects (e.g. samples, // ycbcr conversions, etc.) tend to be fairly light weight and not worth the effort to remove // them and then possibly remake them. Additionally many of those objects have refs/handles that // are held by other objects that aren't deleted here. Thus the memory wins for removing these // objects from the cache are probably not worth the complexity of safely releasing them. void releaseUnlockedBackendObjects(); void backgroundReset(GrVkCommandPool* pool); void reset(GrVkCommandPool* pool); #if GR_TEST_UTILS void resetShaderCacheForTesting() const { fPipelineStateCache->release(); } #endif private: class PipelineStateCache : public GrThreadSafePipelineBuilder { public: PipelineStateCache(GrVkGpu* gpu); ~PipelineStateCache() override; void release(); GrVkPipelineState* findOrCreatePipelineState(GrRenderTarget*, const GrProgramInfo&, VkRenderPass compatibleRenderPass, bool overrideSubpassForResolveLoad); GrVkPipelineState* findOrCreatePipelineState(const GrProgramDesc& desc, const GrProgramInfo& programInfo, VkRenderPass compatibleRenderPass, Stats::ProgramCacheResult* stat) { return this->findOrCreatePipelineStateImpl(desc, programInfo, compatibleRenderPass, false, stat); } private: struct Entry; GrVkPipelineState* findOrCreatePipelineStateImpl(const GrProgramDesc&, const GrProgramInfo&, VkRenderPass compatibleRenderPass, bool overrideSubpassForResolveLoad, Stats::ProgramCacheResult*); struct DescHash { uint32_t operator()(const GrProgramDesc& desc) const { return SkOpts::hash_fn(desc.asKey(), desc.keyLength(), 0); } }; SkLRUCache, DescHash> fMap; GrVkGpu* fGpu; }; class CompatibleRenderPassSet { public: // This will always construct the basic load store render pass (all attachments load and // store their data) so that there is at least one compatible VkRenderPass that can be used // with this set. CompatibleRenderPassSet(GrVkRenderPass* renderPass); bool isCompatible(const GrVkRenderPass::AttachmentsDescriptor&, GrVkRenderPass::AttachmentFlags, SelfDependencyFlags selfDepFlags, LoadFromResolve) const; const GrVkRenderPass* getCompatibleRenderPass() const { // The first GrVkRenderpass should always exist since we create the basic load store // render pass on create SkASSERT(fRenderPasses[0]); return fRenderPasses[0]; } GrVkRenderPass* getRenderPass(GrVkGpu* gpu, const GrVkRenderPass::LoadStoreOps& colorOps, const GrVkRenderPass::LoadStoreOps& resolveOps, const GrVkRenderPass::LoadStoreOps& stencilOps); void releaseResources(); private: SkSTArray<4, GrVkRenderPass*> fRenderPasses; int fLastReturnedIndex; }; VkPipelineCache pipelineCache(); GrVkGpu* fGpu; // Central cache for creating pipelines VkPipelineCache fPipelineCache; //CAUTION: should only used in GrVkResourceProvider size_t fPipelineCacheSize; struct MSAALoadPipeline { sk_sp fPipeline; const GrVkRenderPass* fRenderPass; }; // Cache of previously created msaa load pipelines SkTArray fMSAALoadPipelines; SkSTArray<4, CompatibleRenderPassSet> fRenderPassArray; SkTArray fExternalRenderPasses; // Array of command pools that we are waiting on SkSTArray<4, GrVkCommandPool*, true> fActiveCommandPools; SkMutex fBackgroundMutex; // Array of available command pools that are not in flight SkSTArray<4, GrVkCommandPool*, true> fAvailableCommandPools SK_GUARDED_BY(fBackgroundMutex); // Stores GrVkSampler objects that we've already created so we can reuse them across multiple // GrVkPipelineStates SkTDynamicHash fSamplers; // Stores GrVkSamplerYcbcrConversion objects that we've already created so we can reuse them. SkTDynamicHash fYcbcrConversions; // Cache of GrVkPipelineStates sk_sp fPipelineStateCache; SkSTArray<4, std::unique_ptr> fDescriptorSetManagers; GrVkDescriptorSetManager::Handle fUniformDSHandle; GrVkDescriptorSetManager::Handle fInputDSHandle; }; #endif