blob: e92e4fa5083275e8a62086b268173408ded8342b [file] [log] [blame]
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// RendererVk.h:
// Defines the class interface for RendererVk.
//
#ifndef LIBANGLE_RENDERER_VULKAN_RENDERERVK_H_
#define LIBANGLE_RENDERER_VULKAN_RENDERERVK_H_
#include <condition_variable>
#include <deque>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include "common/vulkan/vk_ext_provoking_vertex.h"
#include "common/PackedEnums.h"
#include "common/PoolAlloc.h"
#include "common/angleutils.h"
#include "common/vulkan/vk_headers.h"
#include "common/vulkan/vulkan_icd.h"
#include "libANGLE/BlobCache.h"
#include "libANGLE/Caps.h"
#include "libANGLE/renderer/vulkan/CommandProcessor.h"
#include "libANGLE/renderer/vulkan/DebugAnnotatorVk.h"
#include "libANGLE/renderer/vulkan/QueryVk.h"
#include "libANGLE/renderer/vulkan/ResourceVk.h"
#include "libANGLE/renderer/vulkan/UtilsVk.h"
#include "libANGLE/renderer/vulkan/vk_format_utils.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
#include "libANGLE/renderer/vulkan/vk_internal_shaders_autogen.h"
#include "libANGLE/renderer/vulkan/vk_mem_alloc_wrapper.h"
namespace egl
{
class Display;
class BlobCache;
} // namespace egl
namespace rx
{
class DisplayVk;
class FramebufferVk;
namespace vk
{
struct Format;
static constexpr size_t kMaxExtensionNames = 200;
using ExtensionNameList = angle::FixedVector<const char *, kMaxExtensionNames>;
} // namespace vk
// Supports one semaphore from current surface, and one semaphore passed to
// glSignalSemaphoreEXT.
using SignalSemaphoreVector = angle::FixedVector<VkSemaphore, 2>;
inline void CollectGarbage(std::vector<vk::GarbageObject> *garbageOut) {}
template <typename ArgT, typename... ArgsT>
void CollectGarbage(std::vector<vk::GarbageObject> *garbageOut, ArgT object, ArgsT... objectsIn)
{
if (object->valid())
{
garbageOut->emplace_back(vk::GarbageObject::Get(object));
}
CollectGarbage(garbageOut, objectsIn...);
}
class RendererVk : angle::NonCopyable
{
public:
RendererVk();
~RendererVk();
angle::Result initialize(DisplayVk *displayVk,
egl::Display *display,
const char *wsiExtension,
const char *wsiLayer);
// Reload volk vk* function ptrs if needed for an already initialized RendererVk
void reloadVolkIfNeeded() const;
void onDestroy();
void notifyDeviceLost();
bool isDeviceLost() const;
bool hasSharedGarbage();
void releaseSharedResources(vk::ResourceUseList *resourceList);
std::string getVendorString() const;
std::string getRendererDescription() const;
gl::Version getMaxSupportedESVersion() const;
gl::Version getMaxConformantESVersion() const;
VkInstance getInstance() const { return mInstance; }
VkPhysicalDevice getPhysicalDevice() const { return mPhysicalDevice; }
const VkPhysicalDeviceProperties &getPhysicalDeviceProperties() const
{
return mPhysicalDeviceProperties;
}
const VkPhysicalDeviceSubgroupProperties &getPhysicalDeviceSubgroupProperties() const
{
return mSubgroupProperties;
}
const VkPhysicalDeviceFeatures &getPhysicalDeviceFeatures() const
{
return mPhysicalDeviceFeatures;
}
VkDevice getDevice() const { return mDevice; }
const vk::Allocator &getAllocator() const { return mAllocator; }
angle::Result selectPresentQueueForSurface(DisplayVk *displayVk,
VkSurfaceKHR surface,
uint32_t *presentQueueOut);
const gl::Caps &getNativeCaps() const;
const gl::TextureCapsMap &getNativeTextureCaps() const;
const gl::Extensions &getNativeExtensions() const;
const gl::Limitations &getNativeLimitations() const;
uint32_t getQueueFamilyIndex() const { return mCurrentQueueFamilyIndex; }
const VkQueueFamilyProperties &getQueueFamilyProperties() const
{
return mQueueFamilyProperties[mCurrentQueueFamilyIndex];
}
const vk::MemoryProperties &getMemoryProperties() const { return mMemoryProperties; }
const vk::Format &getFormat(GLenum internalFormat) const
{
return mFormatTable[internalFormat];
}
const vk::Format &getFormat(angle::FormatID formatID) const { return mFormatTable[formatID]; }
angle::Result getPipelineCacheSize(DisplayVk *displayVk, size_t *pipelineCacheSizeOut);
angle::Result syncPipelineCacheVk(DisplayVk *displayVk);
// Issues a new serial for linked shader modules. Used in the pipeline cache.
Serial issueShaderSerial();
const angle::FeaturesVk &getFeatures() const { return mFeatures; }
uint32_t getMaxVertexAttribDivisor() const { return mMaxVertexAttribDivisor; }
VkDeviceSize getMaxVertexAttribStride() const { return mMaxVertexAttribStride; }
VkDeviceSize getMinImportedHostPointerAlignment() const
{
return mMinImportedHostPointerAlignment;
}
uint32_t getDefaultUniformBufferSize() const { return mDefaultUniformBufferSize; }
bool isMockICDEnabled() const { return mEnabledICD == angle::vk::ICD::Mock; }
// Query the format properties for select bits (linearTilingFeatures, optimalTilingFeatures and
// bufferFeatures). Looks through mandatory features first, and falls back to querying the
// device (first time only).
bool hasLinearImageFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits) const;
VkFormatFeatureFlags getImageFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits) const;
bool hasImageFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits) const;
bool hasBufferFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits) const;
ANGLE_INLINE egl::ContextPriority getDriverPriority(egl::ContextPriority priority)
{
return mPriorities[priority];
}
// Queue submit that originates from the main thread
angle::Result queueSubmit(vk::Context *context,
egl::ContextPriority priority,
const VkSubmitInfo &submitInfo,
vk::ResourceUseList *resourceList,
const vk::Fence *fence,
Serial *serialOut);
angle::Result queueWaitIdle(vk::Context *context, egl::ContextPriority priority);
angle::Result deviceWaitIdle(vk::Context *context);
VkResult queuePresent(egl::ContextPriority priority, const VkPresentInfoKHR &presentInfo);
// This command buffer should be submitted immediately via queueSubmitOneOff.
angle::Result getCommandBufferOneOff(vk::Context *context,
vk::PrimaryCommandBuffer *commandBufferOut);
// Fire off a single command buffer immediately with default priority.
// Command buffer must be allocated with getCommandBufferOneOff and is reclaimed.
angle::Result queueSubmitOneOff(vk::Context *context,
vk::PrimaryCommandBuffer &&primary,
egl::ContextPriority priority,
const vk::Fence *fence,
Serial *serialOut);
angle::Result newSharedFence(vk::Context *context, vk::Shared<vk::Fence> *sharedFenceOut);
inline void resetSharedFence(vk::Shared<vk::Fence> *sharedFenceIn)
{
std::lock_guard<std::mutex> lock(mFenceRecyclerMutex);
sharedFenceIn->resetAndRecycle(&mFenceRecycler);
}
angle::Result getNextSubmitFence(vk::Shared<vk::Fence> *sharedFenceOut, bool reset);
template <typename... ArgsT>
void collectGarbageAndReinit(vk::SharedResourceUse *use, ArgsT... garbageIn)
{
std::vector<vk::GarbageObject> sharedGarbage;
CollectGarbage(&sharedGarbage, garbageIn...);
if (!sharedGarbage.empty())
{
collectGarbage(std::move(*use), std::move(sharedGarbage));
}
else
{
// Force releasing "use" even if no garbage was created.
use->release();
}
// Keep "use" valid.
use->init();
}
void collectGarbage(vk::SharedResourceUse &&use, std::vector<vk::GarbageObject> &&sharedGarbage)
{
if (!sharedGarbage.empty())
{
std::lock_guard<std::mutex> lock(mGarbageMutex);
mSharedGarbage.emplace_back(std::move(use), std::move(sharedGarbage));
}
}
vk::Shared<vk::Fence> getLastSubmittedFence(const vk::Context *context) const
{
return mCommandProcessor.getLastSubmittedFence(context);
}
void handleDeviceLost() { mCommandProcessor.handleDeviceLost(); }
angle::Result getPipelineCache(vk::PipelineCache **pipelineCache);
void onNewGraphicsPipeline()
{
std::lock_guard<std::mutex> lock(mPipelineCacheMutex);
mPipelineCacheDirty = true;
}
void onNewValidationMessage(const std::string &message);
std::string getAndClearLastValidationMessage(uint32_t *countSinceLastClear);
uint64_t getMaxFenceWaitTimeNs() const;
ANGLE_INLINE Serial getCurrentQueueSerial()
{
if (getFeatures().commandProcessor.enabled)
{
return mCommandProcessor.getCurrentQueueSerial();
}
std::lock_guard<std::mutex> lock(mQueueSerialMutex);
return mCurrentQueueSerial;
}
ANGLE_INLINE Serial getLastSubmittedQueueSerial()
{
if (getFeatures().commandProcessor.enabled)
{
return mCommandProcessor.getLastSubmittedSerial();
}
std::lock_guard<std::mutex> lock(mQueueSerialMutex);
return mLastSubmittedQueueSerial;
}
ANGLE_INLINE Serial getLastCompletedQueueSerial()
{
std::lock_guard<std::mutex> lock(mQueueSerialMutex);
return mLastCompletedQueueSerial;
}
void onCompletedSerial(Serial serial);
VkResult getLastPresentResult(VkSwapchainKHR swapchain)
{
return mCommandProcessor.getLastPresentResult(swapchain);
}
bool enableDebugUtils() const { return mEnableDebugUtils; }
SamplerCache &getSamplerCache() { return mSamplerCache; }
SamplerYcbcrConversionCache &getYuvConversionCache() { return mYuvConversionCache; }
vk::ActiveHandleCounter &getActiveHandleCounts() { return mActiveHandleCounts; }
// Queue commands to worker thread for processing
void queueCommand(vk::Context *context, vk::CommandProcessorTask *command)
{
mCommandProcessor.queueCommand(context, command);
}
bool hasPendingError() const { return mCommandProcessor.hasPendingError(); }
vk::Error getAndClearPendingError() { return mCommandProcessor.getAndClearPendingError(); }
void waitForCommandProcessorIdle(vk::Context *context)
{
ASSERT(getFeatures().asynchronousCommandProcessing.enabled);
mCommandProcessor.waitForWorkComplete(context);
}
void finishToSerial(vk::Context *context, Serial serial)
{
mCommandProcessor.finishToSerial(context, serial);
}
void checkCompletedCommands(vk::Context *context)
{
mCommandProcessor.checkCompletedCommands(context);
}
void finishAllWork(vk::Context *context) { mCommandProcessor.finishAllWork(context); }
VkQueue getVkQueue(egl::ContextPriority priority) const { return mQueues[priority]; }
bool getEnableValidationLayers() const { return mEnableValidationLayers; }
vk::ResourceSerialFactory &getResourceSerialFactory() { return mResourceSerialFactory; }
void setGlobalDebugAnnotator();
void outputVmaStatString();
angle::Result cleanupGarbage(bool block);
private:
angle::Result initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex);
void ensureCapsInitialized() const;
void queryDeviceExtensionFeatures(const vk::ExtensionNameList &deviceExtensionNames);
void initFeatures(DisplayVk *display, const vk::ExtensionNameList &extensions);
void initPipelineCacheVkKey();
angle::Result initPipelineCache(DisplayVk *display,
vk::PipelineCache *pipelineCache,
bool *success);
template <VkFormatFeatureFlags VkFormatProperties::*features>
VkFormatFeatureFlags getFormatFeatureBits(VkFormat format,
const VkFormatFeatureFlags featureBits) const;
template <VkFormatFeatureFlags VkFormatProperties::*features>
bool hasFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits) const;
egl::Display *mDisplay;
mutable bool mCapsInitialized;
mutable gl::Caps mNativeCaps;
mutable gl::TextureCapsMap mNativeTextureCaps;
mutable gl::Extensions mNativeExtensions;
mutable gl::Limitations mNativeLimitations;
mutable angle::FeaturesVk mFeatures;
VkInstance mInstance;
bool mEnableValidationLayers;
bool mEnableDebugUtils;
angle::vk::ICD mEnabledICD;
VkDebugUtilsMessengerEXT mDebugUtilsMessenger;
VkDebugReportCallbackEXT mDebugReportCallback;
VkPhysicalDevice mPhysicalDevice;
VkPhysicalDeviceProperties mPhysicalDeviceProperties;
VkPhysicalDeviceFeatures mPhysicalDeviceFeatures;
VkPhysicalDeviceLineRasterizationFeaturesEXT mLineRasterizationFeatures;
VkPhysicalDeviceProvokingVertexFeaturesEXT mProvokingVertexFeatures;
VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT mVertexAttributeDivisorFeatures;
VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT mVertexAttributeDivisorProperties;
VkPhysicalDeviceTransformFeedbackFeaturesEXT mTransformFeedbackFeatures;
VkPhysicalDeviceIndexTypeUint8FeaturesEXT mIndexTypeUint8Features;
VkPhysicalDeviceSubgroupProperties mSubgroupProperties;
VkPhysicalDeviceExternalMemoryHostPropertiesEXT mExternalMemoryHostProperties;
VkPhysicalDeviceShaderFloat16Int8FeaturesKHR mShaderFloat16Int8Features;
VkPhysicalDeviceShaderAtomicFloatFeaturesEXT mShaderAtomicFloatFeature;
VkPhysicalDeviceDepthStencilResolvePropertiesKHR mDepthStencilResolveProperties;
VkExternalFenceProperties mExternalFenceProperties;
VkExternalSemaphoreProperties mExternalSemaphoreProperties;
VkPhysicalDeviceSamplerYcbcrConversionFeatures mSamplerYcbcrConversionFeatures;
std::vector<VkQueueFamilyProperties> mQueueFamilyProperties;
std::mutex mQueueMutex;
angle::PackedEnumMap<egl::ContextPriority, VkQueue> mQueues;
angle::PackedEnumMap<egl::ContextPriority, egl::ContextPriority> mPriorities;
uint32_t mCurrentQueueFamilyIndex;
uint32_t mMaxVertexAttribDivisor;
VkDeviceSize mMaxVertexAttribStride;
VkDeviceSize mMinImportedHostPointerAlignment;
uint32_t mDefaultUniformBufferSize;
VkDevice mDevice;
AtomicSerialFactory mQueueSerialFactory;
AtomicSerialFactory mShaderSerialFactory;
std::mutex mQueueSerialMutex;
Serial mLastCompletedQueueSerial;
Serial mLastSubmittedQueueSerial;
Serial mCurrentQueueSerial;
bool mDeviceLost;
std::mutex mFenceRecyclerMutex;
vk::Recycler<vk::Fence> mFenceRecycler;
std::mutex mGarbageMutex;
vk::SharedGarbageList mSharedGarbage;
vk::MemoryProperties mMemoryProperties;
vk::FormatTable mFormatTable;
// All access to the pipeline cache is done through EGL objects so it is thread safe to not use
// a lock.
std::mutex mPipelineCacheMutex;
vk::PipelineCache mPipelineCache;
egl::BlobCache::Key mPipelineCacheVkBlobKey;
uint32_t mPipelineCacheVkUpdateTimeout;
bool mPipelineCacheDirty;
bool mPipelineCacheInitialized;
// A cache of VkFormatProperties as queried from the device over time.
mutable std::array<VkFormatProperties, vk::kNumVkFormats> mFormatProperties;
// Latest validation data for debug overlay.
std::string mLastValidationMessage;
uint32_t mValidationMessageCount;
DebugAnnotatorVk mAnnotator;
// How close to VkPhysicalDeviceLimits::maxMemoryAllocationCount we allow ourselves to get
static constexpr double kPercentMaxMemoryAllocationCount = 0.3;
// How many objects to garbage collect before issuing a flush()
uint32_t mGarbageCollectionFlushThreshold;
// Only used for "one off" command buffers.
vk::CommandPool mOneOffCommandPool;
struct PendingOneOffCommands
{
Serial serial;
vk::PrimaryCommandBuffer commandBuffer;
};
std::deque<PendingOneOffCommands> mPendingOneOffCommands;
// Command Processor Thread
vk::CommandProcessor mCommandProcessor;
std::thread mCommandProcessorThread;
// mNextSubmitFence is the fence that's going to be signaled at the next submission. This is
// used to support SyncVk objects, which may outlive the context (as EGLSync objects).
vk::Shared<vk::Fence> mNextSubmitFence;
std::mutex mNextSubmitFenceMutex;
// track whether we initialized (or released) glslang
bool mGlslangInitialized;
vk::Allocator mAllocator;
SamplerCache mSamplerCache;
SamplerYcbcrConversionCache mYuvConversionCache;
vk::ActiveHandleCounter mActiveHandleCounts;
// Tracks resource serials.
vk::ResourceSerialFactory mResourceSerialFactory;
};
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_RENDERERVK_H_