blob: 66e9c943eb6d9ba0cadbaa01b1ac4e8f824353fc [file] [log] [blame] [edit]
/*
* Copyright 2022 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/graphite/vk/VulkanSharedContext.h"
#include "include/gpu/graphite/ContextOptions.h"
#include "include/gpu/vk/VulkanBackendContext.h"
#include "include/private/base/SkMutex.h"
#include "src/gpu/graphite/Log.h"
#include "src/gpu/graphite/ResourceTypes.h"
#include "src/gpu/graphite/vk/VulkanBuffer.h"
#include "src/gpu/graphite/vk/VulkanCaps.h"
#include "src/gpu/graphite/vk/VulkanResourceProvider.h"
#include "src/gpu/vk/VulkanAMDMemoryAllocator.h"
#include "src/gpu/vk/VulkanInterface.h"
#include "src/gpu/vk/VulkanUtilsPriv.h"
namespace skgpu::graphite {
sk_sp<SharedContext> VulkanSharedContext::Make(const VulkanBackendContext& context,
const ContextOptions& options) {
if (context.fInstance == VK_NULL_HANDLE ||
context.fPhysicalDevice == VK_NULL_HANDLE ||
context.fDevice == VK_NULL_HANDLE ||
context.fQueue == VK_NULL_HANDLE) {
SKGPU_LOG_E("Failed to create VulkanSharedContext because either fInstance,"
"fPhysicalDevice, fDevice, or fQueue in the VulkanBackendContext is"
"VK_NULL_HANDLE.");
return nullptr;
}
if (!context.fGetProc) {
SKGPU_LOG_E("Failed to create VulkanSharedContext because there is no valid VulkanGetProc"
"on the VulkanBackendContext");
return nullptr;
}
PFN_vkEnumerateInstanceVersion localEnumerateInstanceVersion =
reinterpret_cast<PFN_vkEnumerateInstanceVersion>(
context.fGetProc("vkEnumerateInstanceVersion", VK_NULL_HANDLE, VK_NULL_HANDLE));
uint32_t instanceVersion = 0;
if (!localEnumerateInstanceVersion) {
instanceVersion = VK_MAKE_VERSION(1, 0, 0);
} else {
VkResult err = localEnumerateInstanceVersion(&instanceVersion);
if (err) {
SKGPU_LOG_E("Failed to enumerate instance version. Err: %d\n", err);
return nullptr;
}
}
PFN_vkGetPhysicalDeviceProperties localGetPhysicalDeviceProperties =
reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(
context.fGetProc("vkGetPhysicalDeviceProperties",
context.fInstance,
VK_NULL_HANDLE));
if (!localGetPhysicalDeviceProperties) {
SKGPU_LOG_E("Failed to get function pointer to vkGetPhysicalDeviceProperties.");
return nullptr;
}
VkPhysicalDeviceProperties physDeviceProperties;
localGetPhysicalDeviceProperties(context.fPhysicalDevice, &physDeviceProperties);
uint32_t physDevVersion = physDeviceProperties.apiVersion;
uint32_t apiVersion = context.fMaxAPIVersion ? context.fMaxAPIVersion : instanceVersion;
instanceVersion = std::min(instanceVersion, apiVersion);
physDevVersion = std::min(physDevVersion, apiVersion);
sk_sp<const skgpu::VulkanInterface> interface(
new skgpu::VulkanInterface(context.fGetProc,
context.fInstance,
context.fDevice,
instanceVersion,
physDevVersion,
context.fVkExtensions));
if (!interface->validate(instanceVersion, physDevVersion, context.fVkExtensions)) {
SKGPU_LOG_E("Failed to validate VulkanInterface.");
return nullptr;
}
VkPhysicalDeviceFeatures2 features;
const VkPhysicalDeviceFeatures2* featuresPtr;
// If fDeviceFeatures2 is not null, then we ignore fDeviceFeatures. If both are null, we assume
// no features are enabled.
if (!context.fDeviceFeatures2 && context.fDeviceFeatures) {
features.pNext = nullptr;
features.features = *context.fDeviceFeatures;
featuresPtr = &features;
} else {
featuresPtr = context.fDeviceFeatures2;
}
std::unique_ptr<const VulkanCaps> caps(new VulkanCaps(options,
interface.get(),
context.fPhysicalDevice,
physDevVersion,
featuresPtr,
context.fVkExtensions,
context.fProtectedContext));
sk_sp<skgpu::VulkanMemoryAllocator> memoryAllocator = context.fMemoryAllocator;
if (!memoryAllocator) {
// We were not given a memory allocator at creation
bool threadSafe = !options.fClientWillExternallySynchronizeAllThreads;
memoryAllocator = skgpu::VulkanAMDMemoryAllocator::Make(context.fInstance,
context.fPhysicalDevice,
context.fDevice,
physDevVersion,
context.fVkExtensions,
interface.get(),
threadSafe);
}
if (!memoryAllocator) {
SKGPU_LOG_E("No supplied vulkan memory allocator and unable to create one internally.");
return nullptr;
}
return sk_sp<SharedContext>(new VulkanSharedContext(context,
std::move(interface),
std::move(memoryAllocator),
std::move(caps)));
}
VulkanSharedContext::VulkanSharedContext(const VulkanBackendContext& backendContext,
sk_sp<const skgpu::VulkanInterface> interface,
sk_sp<skgpu::VulkanMemoryAllocator> memoryAllocator,
std::unique_ptr<const VulkanCaps> caps)
: skgpu::graphite::SharedContext(std::move(caps), BackendApi::kVulkan)
, fInterface(std::move(interface))
, fMemoryAllocator(std::move(memoryAllocator))
, fDevice(std::move(backendContext.fDevice))
, fQueueIndex(backendContext.fGraphicsQueueIndex)
, fDeviceLostContext(backendContext.fDeviceLostContext)
, fDeviceLostProc(backendContext.fDeviceLostProc) {}
VulkanSharedContext::~VulkanSharedContext() {
// need to clear out resources before the allocator is removed
this->globalCache()->deleteResources();
}
std::unique_ptr<ResourceProvider> VulkanSharedContext::makeResourceProvider(
SingleOwner* singleOwner,
uint32_t recorderID,
size_t resourceBudget) {
// Establish a uniform buffer that can be updated across multiple render passes and cmd buffers
size_t alignedIntrinsicConstantSize =
std::max(VulkanResourceProvider::kIntrinsicConstantSize,
this->vulkanCaps().requiredUniformBufferAlignment());
sk_sp<Buffer> intrinsicConstantBuffer = VulkanBuffer::Make(this,
alignedIntrinsicConstantSize,
BufferType::kUniform,
AccessPattern::kGpuOnly);
if (!intrinsicConstantBuffer) {
SKGPU_LOG_E("Failed to create intrinsic constant uniform buffer");
return nullptr;
}
SkASSERT(static_cast<VulkanBuffer*>(intrinsicConstantBuffer.get())->bufferUsageFlags()
& VK_BUFFER_USAGE_TRANSFER_DST_BIT);
// Establish a vertex buffer that can be updated across multiple render passes and cmd buffers
// for loading MSAA from resolve
sk_sp<Buffer> loadMSAAVertexBuffer =
VulkanBuffer::Make(this,
VulkanResourceProvider::kLoadMSAAVertexBufferSize,
BufferType::kVertex,
AccessPattern::kGpuOnly);
if (!loadMSAAVertexBuffer) {
SKGPU_LOG_E("Failed to create vertex buffer for loading MSAA from resolve");
return nullptr;
}
SkASSERT(static_cast<VulkanBuffer*>(loadMSAAVertexBuffer.get())->bufferUsageFlags()
& VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
return std::unique_ptr<ResourceProvider>(
new VulkanResourceProvider(this,
singleOwner,
recorderID,
resourceBudget,
std::move(intrinsicConstantBuffer),
std::move(loadMSAAVertexBuffer)));
}
bool VulkanSharedContext::checkVkResult(VkResult result) const {
switch (result) {
case VK_SUCCESS:
return true;
case VK_ERROR_DEVICE_LOST:
{
SkAutoMutexExclusive lock(fDeviceIsLostMutex);
if (fDeviceIsLost) {
return false;
}
fDeviceIsLost = true;
// Fall through to InvokeDeviceLostCallback (on first VK_ERROR_DEVICE_LOST) only afer
// releasing fDeviceIsLostMutex, otherwise clients might cause deadlock by checking
// isDeviceLost() from the callback.
}
skgpu::InvokeDeviceLostCallback(interface(),
device(),
fDeviceLostContext,
fDeviceLostProc,
vulkanCaps().supportsDeviceFaultInfo());
return false;
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
case VK_ERROR_OUT_OF_HOST_MEMORY:
// TODO: determine how we'll track this in a thread-safe manner
//this->setOOMed();
return false;
default:
return false;
}
}
} // namespace skgpu::graphite