Initial Commit
This commit is contained in:
commit
6088fc29ed
978 changed files with 353647 additions and 0 deletions
641
src/VulkanContext/GraphiteNativeVulkanWindowContext.cpp
Normal file
641
src/VulkanContext/GraphiteNativeVulkanWindowContext.cpp
Normal file
|
|
@ -0,0 +1,641 @@
|
|||
// This file has been copied from the Skia library, and may be slightly modified. Please check the NOTICE file for more details
|
||||
|
||||
/*
|
||||
* Copyright 2023 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "GraphiteNativeVulkanWindowContext.h"
|
||||
|
||||
#include <include/core/SkSurface.h>
|
||||
#include <include/gpu/MutableTextureState.h>
|
||||
#include <include/gpu/graphite/BackendSemaphore.h>
|
||||
#include <include/gpu/graphite/BackendTexture.h>
|
||||
#include <include/gpu/graphite/Context.h>
|
||||
#include <include/gpu/graphite/ContextOptions.h>
|
||||
#include <include/gpu/graphite/GraphiteTypes.h>
|
||||
#include <include/gpu/graphite/Recorder.h>
|
||||
#include <include/gpu/graphite/Surface.h>
|
||||
#include <include/gpu/graphite/TextureInfo.h>
|
||||
#include <include/gpu/graphite/vk/VulkanGraphiteContext.h>
|
||||
#include <include/gpu/graphite/vk/VulkanGraphiteTypes.h>
|
||||
#include <include/gpu/vk/VulkanExtensions.h>
|
||||
#include <include/gpu/vk/VulkanMutableTextureState.h>
|
||||
#include <include/gpu/vk/VulkanTypes.h>
|
||||
#include <src/base/SkAutoMalloc.h>
|
||||
#include <src/gpu/graphite/ContextOptionsPriv.h>
|
||||
#include <src/gpu/graphite/TextureFormat.h>
|
||||
#include <src/gpu/graphite/vk/VulkanGraphiteUtils.h>
|
||||
#include <src/gpu/vk/VulkanInterface.h>
|
||||
#include <src/gpu/vk/vulkanmemoryallocator/VulkanAMDMemoryAllocator.h>
|
||||
#include <tools/ToolUtils.h>
|
||||
#include "GraphiteToolUtils.h"
|
||||
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
// windows wants to define this as CreateSemaphoreA or CreateSemaphoreW
|
||||
#undef CreateSemaphore
|
||||
#endif
|
||||
|
||||
#define GET_PROC(F) f##F = (PFN_vk##F)backendContext.fGetProc("vk" #F, fInstance, VK_NULL_HANDLE)
|
||||
#define GET_DEV_PROC(F) f##F = (PFN_vk##F)backendContext.fGetProc("vk" #F, VK_NULL_HANDLE, fDevice)
|
||||
|
||||
namespace skwindow::internal {
|
||||
|
||||
GraphiteVulkanWindowContext::GraphiteVulkanWindowContext(
|
||||
std::unique_ptr<const DisplayParams> params,
|
||||
CreateVkSurfaceFn createVkSurface,
|
||||
CanPresentFn canPresent,
|
||||
PFN_vkGetInstanceProcAddr instProc)
|
||||
: WindowContext(std::move(params))
|
||||
, fCreateVkSurfaceFn(std::move(createVkSurface))
|
||||
, fCanPresentFn(std::move(canPresent))
|
||||
, fSurface(VK_NULL_HANDLE)
|
||||
, fSwapchain(VK_NULL_HANDLE)
|
||||
, fImages(nullptr)
|
||||
, fImageLayouts(nullptr)
|
||||
, fSurfaces(nullptr)
|
||||
, fBackbuffers(nullptr) {
|
||||
fGetInstanceProcAddr = instProc;
|
||||
this->initializeContext();
|
||||
}
|
||||
|
||||
void GraphiteVulkanWindowContext::initializeContext() {
|
||||
SkASSERT(!fGraphiteContext && !fGraphiteRecorder);
|
||||
// any config code here (particularly for msaa)?
|
||||
|
||||
PFN_vkGetInstanceProcAddr getInstanceProc = fGetInstanceProcAddr;
|
||||
skgpu::VulkanBackendContext backendContext;
|
||||
skgpu::VulkanExtensions extensions;
|
||||
VkPhysicalDeviceFeatures2 features;
|
||||
if (!sk_gpu_test::CreateVkBackendContext(getInstanceProc,
|
||||
&backendContext,
|
||||
&extensions,
|
||||
&features,
|
||||
&fDebugCallback,
|
||||
&fPresentQueueIndex,
|
||||
fCanPresentFn,
|
||||
fDisplayParams->createProtectedNativeBackend())) {
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!extensions.hasExtension(VK_KHR_SURFACE_EXTENSION_NAME, 25) ||
|
||||
!extensions.hasExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, 68)) {
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
return;
|
||||
}
|
||||
|
||||
fInstance = backendContext.fInstance;
|
||||
fPhysicalDevice = backendContext.fPhysicalDevice;
|
||||
fDevice = backendContext.fDevice;
|
||||
fGraphicsQueueIndex = backendContext.fGraphicsQueueIndex;
|
||||
fGraphicsQueue = backendContext.fQueue;
|
||||
|
||||
PFN_vkGetPhysicalDeviceProperties localGetPhysicalDeviceProperties =
|
||||
reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(backendContext.fGetProc(
|
||||
"vkGetPhysicalDeviceProperties", backendContext.fInstance, VK_NULL_HANDLE));
|
||||
if (!localGetPhysicalDeviceProperties) {
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
return;
|
||||
}
|
||||
VkPhysicalDeviceProperties physDeviceProperties;
|
||||
localGetPhysicalDeviceProperties(backendContext.fPhysicalDevice, &physDeviceProperties);
|
||||
uint32_t physDevVersion = physDeviceProperties.apiVersion;
|
||||
|
||||
fInterface.reset(new skgpu::VulkanInterface(backendContext.fGetProc,
|
||||
fInstance,
|
||||
fDevice,
|
||||
backendContext.fMaxAPIVersion,
|
||||
physDevVersion,
|
||||
&extensions));
|
||||
|
||||
GET_PROC(DestroyInstance);
|
||||
if (fDebugCallback != VK_NULL_HANDLE) {
|
||||
GET_PROC(DestroyDebugReportCallbackEXT);
|
||||
}
|
||||
GET_PROC(DestroySurfaceKHR);
|
||||
GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
|
||||
GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
|
||||
GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
|
||||
GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
|
||||
GET_DEV_PROC(DeviceWaitIdle);
|
||||
GET_DEV_PROC(QueueWaitIdle);
|
||||
GET_DEV_PROC(DestroyDevice);
|
||||
GET_DEV_PROC(CreateSwapchainKHR);
|
||||
GET_DEV_PROC(DestroySwapchainKHR);
|
||||
GET_DEV_PROC(GetSwapchainImagesKHR);
|
||||
GET_DEV_PROC(AcquireNextImageKHR);
|
||||
GET_DEV_PROC(QueuePresentKHR);
|
||||
GET_DEV_PROC(GetDeviceQueue);
|
||||
|
||||
skgpu::graphite::ContextOptions contextOptions;
|
||||
skgpu::graphite::ContextOptionsPriv contextOptionsPriv;
|
||||
// Needed to make synchronous readPixels work
|
||||
contextOptionsPriv.fStoreContextRefInRecorder = true;
|
||||
contextOptions.fOptionsPriv = &contextOptionsPriv;
|
||||
fGraphiteContext = skgpu::graphite::ContextFactory::MakeVulkan(backendContext, contextOptions);
|
||||
fGraphiteRecorder = fGraphiteContext->makeRecorder(ToolUtils::CreateTestingRecorderOptions());
|
||||
|
||||
fSurface = fCreateVkSurfaceFn(fInstance);
|
||||
if (VK_NULL_HANDLE == fSurface) {
|
||||
this->destroyContext();
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
return;
|
||||
}
|
||||
|
||||
VkBool32 supported;
|
||||
VkResult res = fGetPhysicalDeviceSurfaceSupportKHR(
|
||||
fPhysicalDevice, fPresentQueueIndex, fSurface, &supported);
|
||||
if (VK_SUCCESS != res) {
|
||||
this->destroyContext();
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->createSwapchain(-1, -1)) {
|
||||
this->destroyContext();
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
return;
|
||||
}
|
||||
|
||||
// create presentQueue
|
||||
fGetDeviceQueue(fDevice, fPresentQueueIndex, 0, &fPresentQueue);
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
}
|
||||
|
||||
bool GraphiteVulkanWindowContext::createSwapchain(int width, int height) {
|
||||
// check for capabilities
|
||||
VkSurfaceCapabilitiesKHR caps;
|
||||
VkResult res = fGetPhysicalDeviceSurfaceCapabilitiesKHR(fPhysicalDevice, fSurface, &caps);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t surfaceFormatCount;
|
||||
res = fGetPhysicalDeviceSurfaceFormatsKHR(
|
||||
fPhysicalDevice, fSurface, &surfaceFormatCount, nullptr);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR));
|
||||
VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get();
|
||||
res = fGetPhysicalDeviceSurfaceFormatsKHR(
|
||||
fPhysicalDevice, fSurface, &surfaceFormatCount, surfaceFormats);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t presentModeCount;
|
||||
res = fGetPhysicalDeviceSurfacePresentModesKHR(
|
||||
fPhysicalDevice, fSurface, &presentModeCount, nullptr);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR));
|
||||
VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get();
|
||||
res = fGetPhysicalDeviceSurfacePresentModesKHR(
|
||||
fPhysicalDevice, fSurface, &presentModeCount, presentModes);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VkExtent2D extent = caps.currentExtent;
|
||||
// use the hints
|
||||
if (extent.width == (uint32_t)-1) {
|
||||
extent.width = width;
|
||||
extent.height = height;
|
||||
}
|
||||
|
||||
// clamp width; to protect us from broken hints
|
||||
if (extent.width < caps.minImageExtent.width) {
|
||||
extent.width = caps.minImageExtent.width;
|
||||
} else if (extent.width > caps.maxImageExtent.width) {
|
||||
extent.width = caps.maxImageExtent.width;
|
||||
}
|
||||
// clamp height
|
||||
if (extent.height < caps.minImageExtent.height) {
|
||||
extent.height = caps.minImageExtent.height;
|
||||
} else if (extent.height > caps.maxImageExtent.height) {
|
||||
extent.height = caps.maxImageExtent.height;
|
||||
}
|
||||
|
||||
fWidth = (int)extent.width;
|
||||
fHeight = (int)extent.height;
|
||||
|
||||
uint32_t imageCount = caps.minImageCount + 2;
|
||||
if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
|
||||
// Application must settle for fewer images than desired:
|
||||
imageCount = caps.maxImageCount;
|
||||
}
|
||||
|
||||
VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags);
|
||||
if (caps.supportedUsageFlags & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) {
|
||||
usageFlags |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||
}
|
||||
if (caps.supportedUsageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) {
|
||||
usageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
SkASSERT(caps.supportedTransforms & caps.currentTransform);
|
||||
SkASSERT(caps.supportedCompositeAlpha &
|
||||
(VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR));
|
||||
VkCompositeAlphaFlagBitsKHR composite_alpha =
|
||||
(caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
|
||||
? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
|
||||
: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
|
||||
// Pick our surface format.
|
||||
VkFormat surfaceFormat = VK_FORMAT_UNDEFINED;
|
||||
VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
|
||||
for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
|
||||
VkFormat localFormat = surfaceFormats[i].format;
|
||||
skgpu::graphite::TextureFormat format =
|
||||
skgpu::graphite::VkFormatToTextureFormat(localFormat);
|
||||
// Skip unsupported and HW sRGB formats. We can technically render to the sRGB formats
|
||||
// but it requires the SkColorSpace to have a linear gamut. Viewer needs to be able to
|
||||
// set the dst color space for legacy color management and various other modes, so we
|
||||
// skip those formats here for compatibility.
|
||||
if (format != skgpu::graphite::TextureFormat::kUnsupported &&
|
||||
format != skgpu::graphite::TextureFormat::kRGBA8_sRGB &&
|
||||
format != skgpu::graphite::TextureFormat::kBGRA8_sRGB) {
|
||||
surfaceFormat = localFormat;
|
||||
colorSpace = surfaceFormats[i].colorSpace;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fSampleCount = std::max(1, fDisplayParams->msaaSampleCount());
|
||||
fStencilBits = 8;
|
||||
|
||||
if (VK_FORMAT_UNDEFINED == surfaceFormat) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkColorType colorType;
|
||||
switch (surfaceFormat) {
|
||||
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||
colorType = kRGBA_8888_SkColorType;
|
||||
break;
|
||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||
colorType = kBGRA_8888_SkColorType;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// If mailbox mode is available, use it, as it is the lowest-latency non-
|
||||
// tearing mode. If not, fall back to FIFO which is always available.
|
||||
VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
bool hasImmediate = false;
|
||||
for (uint32_t i = 0; i < presentModeCount; ++i) {
|
||||
// use mailbox
|
||||
if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) {
|
||||
mode = VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
}
|
||||
if (VK_PRESENT_MODE_IMMEDIATE_KHR == presentModes[i]) {
|
||||
hasImmediate = true;
|
||||
}
|
||||
}
|
||||
if (fDisplayParams->disableVsync() && hasImmediate) {
|
||||
mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
}
|
||||
|
||||
VkSwapchainCreateInfoKHR swapchainCreateInfo;
|
||||
memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR));
|
||||
swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||
swapchainCreateInfo.flags = fDisplayParams->createProtectedNativeBackend()
|
||||
? VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR
|
||||
: 0;
|
||||
swapchainCreateInfo.surface = fSurface;
|
||||
swapchainCreateInfo.minImageCount = imageCount;
|
||||
swapchainCreateInfo.imageFormat = surfaceFormat;
|
||||
swapchainCreateInfo.imageColorSpace = colorSpace;
|
||||
swapchainCreateInfo.imageExtent = extent;
|
||||
swapchainCreateInfo.imageArrayLayers = 1;
|
||||
swapchainCreateInfo.imageUsage = usageFlags;
|
||||
|
||||
uint32_t queueFamilies[] = {fGraphicsQueueIndex, fPresentQueueIndex};
|
||||
if (fGraphicsQueueIndex != fPresentQueueIndex) {
|
||||
swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||
swapchainCreateInfo.queueFamilyIndexCount = 2;
|
||||
swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
|
||||
} else {
|
||||
swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
swapchainCreateInfo.queueFamilyIndexCount = 0;
|
||||
swapchainCreateInfo.pQueueFamilyIndices = nullptr;
|
||||
}
|
||||
|
||||
swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
swapchainCreateInfo.compositeAlpha = composite_alpha;
|
||||
swapchainCreateInfo.presentMode = mode;
|
||||
swapchainCreateInfo.clipped = true;
|
||||
swapchainCreateInfo.oldSwapchain = fSwapchain;
|
||||
|
||||
res = fCreateSwapchainKHR(fDevice, &swapchainCreateInfo, nullptr, &fSwapchain);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// destroy the old swapchain
|
||||
if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
|
||||
fDeviceWaitIdle(fDevice);
|
||||
|
||||
this->destroyBuffers();
|
||||
|
||||
fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
|
||||
swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (!this->createBuffers(swapchainCreateInfo.imageFormat,
|
||||
usageFlags,
|
||||
colorType,
|
||||
swapchainCreateInfo.imageSharingMode)) {
|
||||
fDeviceWaitIdle(fDevice);
|
||||
|
||||
this->destroyBuffers();
|
||||
|
||||
fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
|
||||
swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GraphiteVulkanWindowContext::createBuffers(VkFormat format,
|
||||
VkImageUsageFlags usageFlags,
|
||||
SkColorType colorType,
|
||||
VkSharingMode sharingMode) {
|
||||
fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, nullptr);
|
||||
SkASSERT(fImageCount);
|
||||
fImages = new VkImage[fImageCount];
|
||||
fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, fImages);
|
||||
|
||||
// set up initial image layouts and create surfaces
|
||||
fImageLayouts = new VkImageLayout[fImageCount];
|
||||
fSurfaces = new sk_sp<SkSurface>[fImageCount];
|
||||
for (uint32_t i = 0; i < fImageCount; ++i) {
|
||||
fImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
skgpu::graphite::VulkanTextureInfo info;
|
||||
info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
info.fFormat = format;
|
||||
info.fImageUsageFlags = usageFlags;
|
||||
info.fSharingMode = sharingMode;
|
||||
info.fFlags =
|
||||
fDisplayParams->createProtectedNativeBackend() ? VK_IMAGE_CREATE_PROTECTED_BIT : 0;
|
||||
|
||||
auto backendTex = skgpu::graphite::BackendTextures::MakeVulkan(this->dimensions(),
|
||||
info,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
fPresentQueueIndex,
|
||||
fImages[i],
|
||||
skgpu::VulkanAlloc());
|
||||
|
||||
fSurfaces[i] = SkSurfaces::WrapBackendTexture(this->graphiteRecorder(),
|
||||
backendTex,
|
||||
colorType,
|
||||
fDisplayParams->colorSpace(),
|
||||
&fDisplayParams->surfaceProps());
|
||||
|
||||
if (!fSurfaces[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// set up the backbuffers
|
||||
VkSemaphoreCreateInfo semaphoreInfo;
|
||||
memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
|
||||
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
semaphoreInfo.pNext = nullptr;
|
||||
semaphoreInfo.flags = 0;
|
||||
|
||||
// we create one additional backbuffer structure here, because we want to
|
||||
// give the command buffers they contain a chance to finish before we cycle back
|
||||
fBackbuffers = new BackbufferInfo[fImageCount + 1];
|
||||
for (uint32_t i = 0; i < fImageCount + 1; ++i) {
|
||||
fBackbuffers[i].fImageIndex = -1;
|
||||
VkResult result;
|
||||
VULKAN_CALL_RESULT_NOCHECK(
|
||||
fInterface,
|
||||
result,
|
||||
CreateSemaphore(
|
||||
fDevice, &semaphoreInfo, nullptr, &fBackbuffers[i].fRenderSemaphore));
|
||||
}
|
||||
fCurrentBackbufferIndex = fImageCount;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphiteVulkanWindowContext::destroyBuffers() {
|
||||
if (fBackbuffers) {
|
||||
for (uint32_t i = 0; i < fImageCount + 1; ++i) {
|
||||
fBackbuffers[i].fImageIndex = -1;
|
||||
VULKAN_CALL(fInterface,
|
||||
DestroySemaphore(fDevice, fBackbuffers[i].fRenderSemaphore, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
delete[] fBackbuffers;
|
||||
fBackbuffers = nullptr;
|
||||
|
||||
// Does this actually free the surfaces?
|
||||
delete[] fSurfaces;
|
||||
fSurfaces = nullptr;
|
||||
delete[] fImageLayouts;
|
||||
fImageLayouts = nullptr;
|
||||
delete[] fImages;
|
||||
fImages = nullptr;
|
||||
}
|
||||
|
||||
GraphiteVulkanWindowContext::~GraphiteVulkanWindowContext() { this->destroyContext(); }
|
||||
|
||||
void GraphiteVulkanWindowContext::destroyContext() {
|
||||
if (this->isValid()) {
|
||||
if (fPresentQueue != VK_NULL_HANDLE) {
|
||||
fQueueWaitIdle(fPresentQueue);
|
||||
}
|
||||
fDeviceWaitIdle(fDevice);
|
||||
|
||||
if (fWaitSemaphore != VK_NULL_HANDLE) {
|
||||
VULKAN_CALL(fInterface, DestroySemaphore(fDevice, fWaitSemaphore, nullptr));
|
||||
fWaitSemaphore = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
this->destroyBuffers();
|
||||
|
||||
if (fSwapchain != VK_NULL_HANDLE) {
|
||||
fDestroySwapchainKHR(fDevice, fSwapchain, nullptr);
|
||||
fSwapchain = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (fSurface != VK_NULL_HANDLE) {
|
||||
fDestroySurfaceKHR(fInstance, fSurface, nullptr);
|
||||
fSurface = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (fGraphiteContext) {
|
||||
fGraphiteRecorder.reset();
|
||||
fGraphiteContext.reset();
|
||||
}
|
||||
fInterface.reset();
|
||||
|
||||
if (fDevice != VK_NULL_HANDLE) {
|
||||
fDestroyDevice(fDevice, nullptr);
|
||||
fDevice = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
#ifdef SK_ENABLE_VK_LAYERS
|
||||
if (fDebugCallback != VK_NULL_HANDLE) {
|
||||
fDestroyDebugReportCallbackEXT(fInstance, fDebugCallback, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
fPhysicalDevice = VK_NULL_HANDLE;
|
||||
|
||||
if (fInstance != VK_NULL_HANDLE) {
|
||||
fDestroyInstance(fInstance, nullptr);
|
||||
fInstance = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
GraphiteVulkanWindowContext::BackbufferInfo* GraphiteVulkanWindowContext::getAvailableBackbuffer() {
|
||||
SkASSERT(fBackbuffers);
|
||||
|
||||
++fCurrentBackbufferIndex;
|
||||
if (fCurrentBackbufferIndex > fImageCount) {
|
||||
fCurrentBackbufferIndex = 0;
|
||||
}
|
||||
|
||||
BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
|
||||
return backbuffer;
|
||||
}
|
||||
|
||||
sk_sp<SkSurface> GraphiteVulkanWindowContext::getBackbufferSurface() {
|
||||
BackbufferInfo* backbuffer = this->getAvailableBackbuffer();
|
||||
SkASSERT(backbuffer);
|
||||
|
||||
// semaphores should be in unsignaled state
|
||||
VkSemaphoreCreateInfo semaphoreInfo;
|
||||
memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
|
||||
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
semaphoreInfo.pNext = nullptr;
|
||||
semaphoreInfo.flags = 0;
|
||||
VkResult result;
|
||||
VULKAN_CALL_RESULT_NOCHECK(
|
||||
fInterface, result, CreateSemaphore(fDevice, &semaphoreInfo, nullptr, &fWaitSemaphore));
|
||||
|
||||
// acquire the image
|
||||
VkResult res = fAcquireNextImageKHR(fDevice,
|
||||
fSwapchain,
|
||||
UINT64_MAX,
|
||||
fWaitSemaphore,
|
||||
VK_NULL_HANDLE,
|
||||
&backbuffer->fImageIndex);
|
||||
if (VK_ERROR_SURFACE_LOST_KHR == res) {
|
||||
// TODO: Recreate fSurface using fCreateVkSurfaceFn, and then rebuild the swapchain
|
||||
VULKAN_CALL(fInterface, DestroySemaphore(fDevice, fWaitSemaphore, nullptr));
|
||||
return nullptr;
|
||||
}
|
||||
if (VK_ERROR_OUT_OF_DATE_KHR == res) {
|
||||
// tear swapchain down and try again
|
||||
if (!this->createSwapchain(-1, -1)) {
|
||||
VULKAN_CALL(fInterface, DestroySemaphore(fDevice, fWaitSemaphore, nullptr));
|
||||
return nullptr;
|
||||
}
|
||||
backbuffer = this->getAvailableBackbuffer();
|
||||
|
||||
// acquire the image
|
||||
res = fAcquireNextImageKHR(fDevice,
|
||||
fSwapchain,
|
||||
UINT64_MAX,
|
||||
fWaitSemaphore,
|
||||
VK_NULL_HANDLE,
|
||||
&backbuffer->fImageIndex);
|
||||
|
||||
if (VK_SUCCESS != res) {
|
||||
VULKAN_CALL(fInterface, DestroySemaphore(fDevice, fWaitSemaphore, nullptr));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SkSurface* surface = fSurfaces[backbuffer->fImageIndex].get();
|
||||
|
||||
return sk_ref_sp(surface);
|
||||
}
|
||||
|
||||
void GraphiteVulkanWindowContext::onSwapBuffers() {
|
||||
if (!fGraphiteContext) {
|
||||
return;
|
||||
}
|
||||
SkASSERT(fGraphiteRecorder);
|
||||
|
||||
BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
|
||||
|
||||
// Rather than using submitToGpu we explicitly do that work here
|
||||
// so we can set up the swapchain semaphores.
|
||||
std::unique_ptr<skgpu::graphite::Recording> recording = fGraphiteRecorder->snap();
|
||||
|
||||
if (recording) {
|
||||
skgpu::graphite::InsertRecordingInfo info;
|
||||
info.fRecording = recording.get();
|
||||
|
||||
// set up surface for layout transition
|
||||
info.fTargetSurface = fSurfaces[backbuffer->fImageIndex].get();
|
||||
skgpu::MutableTextureState presentState = skgpu::MutableTextureStates::MakeVulkan(
|
||||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, fPresentQueueIndex);
|
||||
info.fTargetTextureState = &presentState;
|
||||
|
||||
SkASSERT(fWaitSemaphore != VK_NULL_HANDLE);
|
||||
auto beWaitSemaphore = skgpu::graphite::BackendSemaphores::MakeVulkan(fWaitSemaphore);
|
||||
info.fNumWaitSemaphores = 1;
|
||||
info.fWaitSemaphores = &beWaitSemaphore;
|
||||
auto beSignalSemaphore =
|
||||
skgpu::graphite::BackendSemaphores::MakeVulkan(backbuffer->fRenderSemaphore);
|
||||
info.fNumSignalSemaphores = 1;
|
||||
info.fSignalSemaphores = &beSignalSemaphore;
|
||||
|
||||
// Insert finishedProc to delete waitSemaphore when done
|
||||
struct FinishContext {
|
||||
sk_sp<const skgpu::VulkanInterface> interface;
|
||||
VkDevice device;
|
||||
VkSemaphore waitSemaphore;
|
||||
};
|
||||
auto* finishContext = new FinishContext{fInterface, fDevice, fWaitSemaphore};
|
||||
skgpu::graphite::GpuFinishedProc finishCallback = [](skgpu::graphite::GpuFinishedContext c,
|
||||
skgpu::CallbackResult status) {
|
||||
// regardless of the status we need to destroy the semaphore
|
||||
const auto* context = reinterpret_cast<const FinishContext*>(c);
|
||||
VULKAN_CALL(context->interface,
|
||||
DestroySemaphore(context->device, context->waitSemaphore, nullptr));
|
||||
};
|
||||
info.fFinishedContext = finishContext;
|
||||
info.fFinishedProc = finishCallback;
|
||||
|
||||
fGraphiteContext->insertRecording(info);
|
||||
fGraphiteContext->submit(skgpu::graphite::SyncToCpu::kNo);
|
||||
fWaitSemaphore = VK_NULL_HANDLE; // FinishCallback will destroy this
|
||||
}
|
||||
|
||||
// Submit present operation to present queue
|
||||
const VkPresentInfoKHR presentInfo = {
|
||||
VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, // sType
|
||||
nullptr, // pNext
|
||||
1, // waitSemaphoreCount
|
||||
&backbuffer->fRenderSemaphore, // pWaitSemaphores
|
||||
1, // swapchainCount
|
||||
&fSwapchain, // pSwapchains
|
||||
&backbuffer->fImageIndex, // pImageIndices
|
||||
nullptr // pResults
|
||||
};
|
||||
|
||||
fQueuePresentKHR(fPresentQueue, &presentInfo);
|
||||
}
|
||||
|
||||
} // namespace skwindow::internal
|
||||
119
src/VulkanContext/GraphiteNativeVulkanWindowContext.h
Normal file
119
src/VulkanContext/GraphiteNativeVulkanWindowContext.h
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
// This file has been copied from the Skia library, and may be slightly modified. Please check the NOTICE file for more details
|
||||
|
||||
/*
|
||||
* Copyright 2023 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef GraphiteVulkanWindowContext_DEFINED
|
||||
#define GraphiteVulkanWindowContext_DEFINED
|
||||
|
||||
#include <include/core/SkTypes.h>
|
||||
|
||||
#include "VkTestUtils.h"
|
||||
#include "WindowContext.h"
|
||||
|
||||
class GrRenderTarget;
|
||||
|
||||
namespace skgpu {
|
||||
struct VulkanInterface;
|
||||
}
|
||||
|
||||
namespace skwindow::internal {
|
||||
|
||||
class GraphiteVulkanWindowContext : public WindowContext {
|
||||
public:
|
||||
~GraphiteVulkanWindowContext() override;
|
||||
|
||||
sk_sp<SkSurface> getBackbufferSurface() override;
|
||||
|
||||
bool isValid() override { return fDevice != VK_NULL_HANDLE; }
|
||||
|
||||
void resize(int w, int h) override { this->createSwapchain(w, h); }
|
||||
|
||||
void setDisplayParams(std::unique_ptr<const DisplayParams> params) override {
|
||||
this->destroyContext();
|
||||
fDisplayParams = std::move(params);
|
||||
this->initializeContext();
|
||||
}
|
||||
|
||||
/** Platform specific function that creates a VkSurfaceKHR for a window */
|
||||
using CreateVkSurfaceFn = std::function<VkSurfaceKHR(VkInstance)>;
|
||||
/** Platform specific function that determines whether presentation will succeed. */
|
||||
using CanPresentFn = sk_gpu_test::CanPresentFn;
|
||||
|
||||
GraphiteVulkanWindowContext(std::unique_ptr<const DisplayParams>,
|
||||
CreateVkSurfaceFn,
|
||||
CanPresentFn,
|
||||
PFN_vkGetInstanceProcAddr);
|
||||
|
||||
private:
|
||||
void initializeContext();
|
||||
void destroyContext();
|
||||
|
||||
struct BackbufferInfo {
|
||||
uint32_t fImageIndex; // image this is associated with
|
||||
VkSemaphore fRenderSemaphore; // we wait on this for rendering to be done
|
||||
};
|
||||
|
||||
BackbufferInfo* getAvailableBackbuffer();
|
||||
bool createSwapchain(int width, int height);
|
||||
bool createBuffers(VkFormat format, VkImageUsageFlags, SkColorType colorType, VkSharingMode);
|
||||
void destroyBuffers();
|
||||
void onSwapBuffers() override;
|
||||
|
||||
VkInstance fInstance = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice fPhysicalDevice = VK_NULL_HANDLE;
|
||||
VkDevice fDevice = VK_NULL_HANDLE;
|
||||
VkDebugReportCallbackEXT fDebugCallback = VK_NULL_HANDLE;
|
||||
|
||||
// Create functions
|
||||
CreateVkSurfaceFn fCreateVkSurfaceFn;
|
||||
CanPresentFn fCanPresentFn;
|
||||
|
||||
PFN_vkGetInstanceProcAddr fGetInstanceProcAddr = nullptr;
|
||||
|
||||
// WSI interface functions
|
||||
PFN_vkDestroySurfaceKHR fDestroySurfaceKHR = nullptr;
|
||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR fGetPhysicalDeviceSurfaceSupportKHR = nullptr;
|
||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fGetPhysicalDeviceSurfaceCapabilitiesKHR =
|
||||
nullptr;
|
||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fGetPhysicalDeviceSurfaceFormatsKHR = nullptr;
|
||||
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fGetPhysicalDeviceSurfacePresentModesKHR =
|
||||
nullptr;
|
||||
|
||||
PFN_vkCreateSwapchainKHR fCreateSwapchainKHR = nullptr;
|
||||
PFN_vkDestroySwapchainKHR fDestroySwapchainKHR = nullptr;
|
||||
PFN_vkGetSwapchainImagesKHR fGetSwapchainImagesKHR = nullptr;
|
||||
PFN_vkAcquireNextImageKHR fAcquireNextImageKHR = nullptr;
|
||||
PFN_vkQueuePresentKHR fQueuePresentKHR = nullptr;
|
||||
|
||||
PFN_vkDestroyInstance fDestroyInstance = nullptr;
|
||||
PFN_vkDeviceWaitIdle fDeviceWaitIdle = nullptr;
|
||||
PFN_vkDestroyDebugReportCallbackEXT fDestroyDebugReportCallbackEXT = nullptr;
|
||||
PFN_vkQueueWaitIdle fQueueWaitIdle = nullptr;
|
||||
PFN_vkDestroyDevice fDestroyDevice = nullptr;
|
||||
PFN_vkGetDeviceQueue fGetDeviceQueue = nullptr;
|
||||
|
||||
sk_sp<const skgpu::VulkanInterface> fInterface;
|
||||
|
||||
VkSurfaceKHR fSurface;
|
||||
VkSwapchainKHR fSwapchain;
|
||||
uint32_t fGraphicsQueueIndex;
|
||||
VkQueue fGraphicsQueue;
|
||||
uint32_t fPresentQueueIndex;
|
||||
VkQueue fPresentQueue;
|
||||
|
||||
uint32_t fImageCount;
|
||||
VkImage* fImages; // images in the swapchain
|
||||
VkImageLayout* fImageLayouts; // layouts of these images when not color attachment
|
||||
sk_sp<SkSurface>* fSurfaces; // surfaces client renders to (may not be based on rts)
|
||||
BackbufferInfo* fBackbuffers;
|
||||
uint32_t fCurrentBackbufferIndex;
|
||||
VkSemaphore fWaitSemaphore = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
} // namespace skwindow::internal
|
||||
|
||||
#endif // GraphiteVulkanWindowContext_DEFINED
|
||||
114
src/VulkanContext/GraphiteToolUtils.cpp
Normal file
114
src/VulkanContext/GraphiteToolUtils.cpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// This file has been copied from the Skia library, and may be slightly modified. Please check the NOTICE file for more details
|
||||
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "tools/graphite/GraphiteToolUtils.h"
|
||||
|
||||
#include <include/core/SkCanvas.h>
|
||||
#include <include/core/SkImage.h>
|
||||
#include <include/core/SkTiledImageUtils.h>
|
||||
#include <include/gpu/graphite/Image.h>
|
||||
#include <include/gpu/graphite/ImageProvider.h>
|
||||
#include <include/gpu/graphite/Recorder.h>
|
||||
#include <src/core/SkLRUCache.h>
|
||||
|
||||
namespace {
|
||||
// Currently, we give each new Recorder its own ImageProvider. This means we don't have to deal
|
||||
// w/ any threading issues.
|
||||
// TODO: We should probably have this class generate and report some cache stats
|
||||
// TODO: Hook up to listener system?
|
||||
// TODO: add testing of a single ImageProvider passed to multiple recorders
|
||||
class TestingImageProvider : public skgpu::graphite::ImageProvider {
|
||||
public:
|
||||
TestingImageProvider() : fCache(kDefaultNumCachedImages) {}
|
||||
~TestingImageProvider() override {}
|
||||
|
||||
sk_sp<SkImage> findOrCreate(skgpu::graphite::Recorder* recorder,
|
||||
const SkImage* image,
|
||||
SkImage::RequiredProperties requiredProps) override {
|
||||
if (!requiredProps.fMipmapped) {
|
||||
// If no mipmaps are required, check to see if we have a mipmapped version anyway -
|
||||
// since it can be used in that case.
|
||||
// TODO: we could get fancy and, if ever a mipmapped key eclipsed a non-mipmapped
|
||||
// key, we could remove the hidden non-mipmapped key/image from the cache.
|
||||
ImageKey mipMappedKey(image, /* mipmapped= */ true);
|
||||
auto result = fCache.find(mipMappedKey);
|
||||
if (result) {
|
||||
return *result;
|
||||
}
|
||||
}
|
||||
|
||||
ImageKey key(image, requiredProps.fMipmapped);
|
||||
|
||||
auto result = fCache.find(key);
|
||||
if (result) {
|
||||
return *result;
|
||||
}
|
||||
|
||||
sk_sp<SkImage> newImage = SkImages::TextureFromImage(recorder, image, requiredProps);
|
||||
if (!newImage) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
result = fCache.insert(key, std::move(newImage));
|
||||
SkASSERT(result);
|
||||
|
||||
return *result;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int kDefaultNumCachedImages = 256;
|
||||
|
||||
class ImageKey {
|
||||
public:
|
||||
ImageKey(const SkImage* image, bool mipmapped) {
|
||||
uint32_t flags = mipmapped ? 0x1 : 0x0;
|
||||
SkTiledImageUtils::GetImageKeyValues(image, &fValues[1]);
|
||||
fValues[kNumValues - 1] = flags;
|
||||
fValues[0] = SkChecksum::Hash32(&fValues[1], (kNumValues - 1) * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
uint32_t hash() const { return fValues[0]; }
|
||||
|
||||
bool operator==(const ImageKey& other) const {
|
||||
for (int i = 0; i < kNumValues; ++i) {
|
||||
if (fValues[i] != other.fValues[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool operator!=(const ImageKey& other) const { return !(*this == other); }
|
||||
|
||||
private:
|
||||
static const int kNumValues = SkTiledImageUtils::kNumImageKeyValues + 2;
|
||||
|
||||
uint32_t fValues[kNumValues];
|
||||
};
|
||||
|
||||
struct ImageHash {
|
||||
size_t operator()(const ImageKey& key) const { return key.hash(); }
|
||||
};
|
||||
|
||||
SkLRUCache<ImageKey, sk_sp<SkImage>, ImageHash> fCache;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace ToolUtils {
|
||||
|
||||
skgpu::graphite::RecorderOptions CreateTestingRecorderOptions() {
|
||||
skgpu::graphite::RecorderOptions options;
|
||||
|
||||
options.fImageProvider.reset(new TestingImageProvider);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
} // namespace ToolUtils
|
||||
25
src/VulkanContext/GraphiteToolUtils.h
Normal file
25
src/VulkanContext/GraphiteToolUtils.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// This file has been copied from the Skia library, and may be slightly modified. Please check the NOTICE file for more details
|
||||
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef GraphteToolUtils_DEFINED
|
||||
#define GraphteToolUtils_DEFINED
|
||||
|
||||
#include "include/core/SkTypes.h"
|
||||
|
||||
namespace skgpu::graphite {
|
||||
struct RecorderOptions;
|
||||
}
|
||||
|
||||
namespace ToolUtils {
|
||||
|
||||
skgpu::graphite::RecorderOptions CreateTestingRecorderOptions();
|
||||
|
||||
} // namespace ToolUtils
|
||||
|
||||
#endif
|
||||
267
src/VulkanContext/VkTestMemoryAllocator.cpp
Normal file
267
src/VulkanContext/VkTestMemoryAllocator.cpp
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
// This file has been copied from the Skia library, and may be slightly modified. Please check the NOTICE file for more details
|
||||
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <tools/gpu/vk/VkTestMemoryAllocator.h>
|
||||
#include <include/gpu/vk/VulkanExtensions.h>
|
||||
#include <src/core/SkTraceEvent.h>
|
||||
#include <src/gpu/vk/VulkanInterface.h>
|
||||
|
||||
namespace sk_gpu_test {
|
||||
|
||||
sk_sp<skgpu::VulkanMemoryAllocator> VkTestMemoryAllocator::Make(
|
||||
VkInstance instance,
|
||||
VkPhysicalDevice physicalDevice,
|
||||
VkDevice device,
|
||||
uint32_t physicalDeviceVersion,
|
||||
const skgpu::VulkanExtensions* extensions,
|
||||
const skgpu::VulkanInterface* interface) {
|
||||
#define SKGPU_COPY_FUNCTION(NAME) functions.vk##NAME = interface->fFunctions.f##NAME
|
||||
#define SKGPU_COPY_FUNCTION_KHR(NAME) functions.vk##NAME##KHR = interface->fFunctions.f##NAME
|
||||
|
||||
VmaVulkanFunctions functions;
|
||||
// We should be setting all the required functions (at least through vulkan 1.1), but this is
|
||||
// just extra belt and suspenders to make sure there isn't unitialized values here.
|
||||
memset(&functions, 0, sizeof(VmaVulkanFunctions));
|
||||
|
||||
// We don't use dynamic function getting in the allocator so we set the getProc functions to
|
||||
// null.
|
||||
functions.vkGetInstanceProcAddr = nullptr;
|
||||
functions.vkGetDeviceProcAddr = nullptr;
|
||||
SKGPU_COPY_FUNCTION(GetPhysicalDeviceProperties);
|
||||
SKGPU_COPY_FUNCTION(GetPhysicalDeviceMemoryProperties);
|
||||
SKGPU_COPY_FUNCTION(AllocateMemory);
|
||||
SKGPU_COPY_FUNCTION(FreeMemory);
|
||||
SKGPU_COPY_FUNCTION(MapMemory);
|
||||
SKGPU_COPY_FUNCTION(UnmapMemory);
|
||||
SKGPU_COPY_FUNCTION(FlushMappedMemoryRanges);
|
||||
SKGPU_COPY_FUNCTION(InvalidateMappedMemoryRanges);
|
||||
SKGPU_COPY_FUNCTION(BindBufferMemory);
|
||||
SKGPU_COPY_FUNCTION(BindImageMemory);
|
||||
SKGPU_COPY_FUNCTION(GetBufferMemoryRequirements);
|
||||
SKGPU_COPY_FUNCTION(GetImageMemoryRequirements);
|
||||
SKGPU_COPY_FUNCTION(CreateBuffer);
|
||||
SKGPU_COPY_FUNCTION(DestroyBuffer);
|
||||
SKGPU_COPY_FUNCTION(CreateImage);
|
||||
SKGPU_COPY_FUNCTION(DestroyImage);
|
||||
SKGPU_COPY_FUNCTION(CmdCopyBuffer);
|
||||
SKGPU_COPY_FUNCTION_KHR(GetBufferMemoryRequirements2);
|
||||
SKGPU_COPY_FUNCTION_KHR(GetImageMemoryRequirements2);
|
||||
SKGPU_COPY_FUNCTION_KHR(BindBufferMemory2);
|
||||
SKGPU_COPY_FUNCTION_KHR(BindImageMemory2);
|
||||
SKGPU_COPY_FUNCTION_KHR(GetPhysicalDeviceMemoryProperties2);
|
||||
|
||||
VmaAllocatorCreateInfo info;
|
||||
info.flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
|
||||
if (physicalDeviceVersion >= VK_MAKE_VERSION(1, 1, 0) ||
|
||||
(extensions->hasExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, 1) &&
|
||||
extensions->hasExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, 1))) {
|
||||
info.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
|
||||
}
|
||||
|
||||
info.physicalDevice = physicalDevice;
|
||||
info.device = device;
|
||||
// 4MB was picked for the size here by looking at memory usage of Android apps and runs of DM.
|
||||
// It seems to be a good compromise of not wasting unused allocated space and not making too
|
||||
// many small allocations. The AMD allocator will start making blocks at 1/8 the max size and
|
||||
// builds up block size as needed before capping at the max set here.
|
||||
info.preferredLargeHeapBlockSize = 4 * 1024 * 1024;
|
||||
info.pAllocationCallbacks = nullptr;
|
||||
info.pDeviceMemoryCallbacks = nullptr;
|
||||
info.pHeapSizeLimit = nullptr;
|
||||
info.pVulkanFunctions = &functions;
|
||||
info.instance = instance;
|
||||
// TODO: Update our interface and headers to support vulkan 1.3 and add in the new required
|
||||
// functions for 1.3 that the allocator needs. Until then we just clamp the version to 1.1.
|
||||
info.vulkanApiVersion = std::min(physicalDeviceVersion, VK_MAKE_VERSION(1, 1, 0));
|
||||
info.pTypeExternalMemoryHandleTypes = nullptr;
|
||||
|
||||
VmaAllocator allocator;
|
||||
vmaCreateAllocator(&info, &allocator);
|
||||
|
||||
return sk_sp<VkTestMemoryAllocator>(new VkTestMemoryAllocator(allocator));
|
||||
}
|
||||
|
||||
VkTestMemoryAllocator::VkTestMemoryAllocator(VmaAllocator allocator) : fAllocator(allocator) {}
|
||||
|
||||
VkTestMemoryAllocator::~VkTestMemoryAllocator() {
|
||||
vmaDestroyAllocator(fAllocator);
|
||||
fAllocator = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkResult VkTestMemoryAllocator::allocateImageMemory(VkImage image,
|
||||
uint32_t allocationPropertyFlags,
|
||||
skgpu::VulkanBackendMemory* backendMemory) {
|
||||
TRACE_EVENT0_ALWAYS("skia.gpu", TRACE_FUNC);
|
||||
VmaAllocationCreateInfo info;
|
||||
info.flags = 0;
|
||||
info.usage = VMA_MEMORY_USAGE_UNKNOWN;
|
||||
info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
info.preferredFlags = 0;
|
||||
info.memoryTypeBits = 0;
|
||||
info.pool = VK_NULL_HANDLE;
|
||||
info.pUserData = nullptr;
|
||||
|
||||
if (kDedicatedAllocation_AllocationPropertyFlag & allocationPropertyFlags) {
|
||||
info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
|
||||
}
|
||||
if (kLazyAllocation_AllocationPropertyFlag & allocationPropertyFlags) {
|
||||
info.requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
|
||||
}
|
||||
if (kProtected_AllocationPropertyFlag & allocationPropertyFlags) {
|
||||
info.requiredFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
|
||||
}
|
||||
|
||||
VmaAllocation allocation;
|
||||
VkResult result = vmaAllocateMemoryForImage(fAllocator, image, &info, &allocation, nullptr);
|
||||
if (VK_SUCCESS == result) {
|
||||
*backendMemory = (skgpu::VulkanBackendMemory)allocation;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
VkResult VkTestMemoryAllocator::allocateBufferMemory(VkBuffer buffer,
|
||||
BufferUsage usage,
|
||||
uint32_t allocationPropertyFlags,
|
||||
skgpu::VulkanBackendMemory* backendMemory) {
|
||||
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
|
||||
VmaAllocationCreateInfo info;
|
||||
info.flags = 0;
|
||||
info.usage = VMA_MEMORY_USAGE_UNKNOWN;
|
||||
info.memoryTypeBits = 0;
|
||||
info.pool = VK_NULL_HANDLE;
|
||||
info.pUserData = nullptr;
|
||||
|
||||
switch (usage) {
|
||||
case BufferUsage::kGpuOnly:
|
||||
info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
info.preferredFlags = 0;
|
||||
break;
|
||||
case BufferUsage::kCpuWritesGpuReads:
|
||||
// When doing cpu writes and gpu reads the general rule of thumb is to use coherent
|
||||
// memory. Though this depends on the fact that we are not doing any cpu reads and the
|
||||
// cpu writes are sequential. For sparse writes we'd want cpu cached memory, however we
|
||||
// don't do these types of writes in Skia.
|
||||
//
|
||||
// TODO: In the future there may be times where specific types of memory could benefit
|
||||
// from a coherent and cached memory. Typically these allow for the gpu to read cpu
|
||||
// writes from the cache without needing to flush the writes throughout the cache. The
|
||||
// reverse is not true and GPU writes tend to invalidate the cache regardless. Also
|
||||
// these gpu cache read access are typically lower bandwidth than non-cached memory.
|
||||
// For now Skia doesn't really have a need or want of this type of memory. But if we
|
||||
// ever do we could pass in an AllocationPropertyFlag that requests the cached property.
|
||||
info.requiredFlags =
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
info.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
break;
|
||||
case BufferUsage::kTransfersFromCpuToGpu:
|
||||
info.requiredFlags =
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
info.preferredFlags = 0;
|
||||
break;
|
||||
case BufferUsage::kTransfersFromGpuToCpu:
|
||||
info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
|
||||
info.preferredFlags = VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (kDedicatedAllocation_AllocationPropertyFlag & allocationPropertyFlags) {
|
||||
info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
|
||||
}
|
||||
if ((kLazyAllocation_AllocationPropertyFlag & allocationPropertyFlags) &&
|
||||
BufferUsage::kGpuOnly == usage) {
|
||||
info.preferredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
|
||||
}
|
||||
|
||||
if (kPersistentlyMapped_AllocationPropertyFlag & allocationPropertyFlags) {
|
||||
SkASSERT(BufferUsage::kGpuOnly != usage);
|
||||
info.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
}
|
||||
|
||||
if (kProtected_AllocationPropertyFlag & allocationPropertyFlags) {
|
||||
info.requiredFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
|
||||
}
|
||||
|
||||
VmaAllocation allocation;
|
||||
VkResult result = vmaAllocateMemoryForBuffer(fAllocator, buffer, &info, &allocation, nullptr);
|
||||
if (VK_SUCCESS == result) {
|
||||
*backendMemory = (skgpu::VulkanBackendMemory)allocation;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void VkTestMemoryAllocator::freeMemory(const skgpu::VulkanBackendMemory& memoryHandle) {
|
||||
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
|
||||
const VmaAllocation allocation = (VmaAllocation)memoryHandle;
|
||||
vmaFreeMemory(fAllocator, allocation);
|
||||
}
|
||||
|
||||
void VkTestMemoryAllocator::getAllocInfo(const skgpu::VulkanBackendMemory& memoryHandle,
|
||||
skgpu::VulkanAlloc* alloc) const {
|
||||
const VmaAllocation allocation = (VmaAllocation)memoryHandle;
|
||||
VmaAllocationInfo vmaInfo;
|
||||
vmaGetAllocationInfo(fAllocator, allocation, &vmaInfo);
|
||||
|
||||
VkMemoryPropertyFlags memFlags;
|
||||
vmaGetMemoryTypeProperties(fAllocator, vmaInfo.memoryType, &memFlags);
|
||||
|
||||
uint32_t flags = 0;
|
||||
if (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT & memFlags) {
|
||||
flags |= skgpu::VulkanAlloc::kMappable_Flag;
|
||||
}
|
||||
if (!SkToBool(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT & memFlags)) {
|
||||
flags |= skgpu::VulkanAlloc::kNoncoherent_Flag;
|
||||
}
|
||||
if (VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT & memFlags) {
|
||||
flags |= skgpu::VulkanAlloc::kLazilyAllocated_Flag;
|
||||
}
|
||||
|
||||
alloc->fMemory = vmaInfo.deviceMemory;
|
||||
alloc->fOffset = vmaInfo.offset;
|
||||
alloc->fSize = vmaInfo.size;
|
||||
alloc->fFlags = flags;
|
||||
alloc->fBackendMemory = memoryHandle;
|
||||
}
|
||||
|
||||
VkResult VkTestMemoryAllocator::mapMemory(const skgpu::VulkanBackendMemory& memoryHandle,
|
||||
void** data) {
|
||||
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
|
||||
const VmaAllocation allocation = (VmaAllocation)memoryHandle;
|
||||
return vmaMapMemory(fAllocator, allocation, data);
|
||||
}
|
||||
|
||||
void VkTestMemoryAllocator::unmapMemory(const skgpu::VulkanBackendMemory& memoryHandle) {
|
||||
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
|
||||
const VmaAllocation allocation = (VmaAllocation)memoryHandle;
|
||||
vmaUnmapMemory(fAllocator, allocation);
|
||||
}
|
||||
|
||||
VkResult VkTestMemoryAllocator::flushMemory(const skgpu::VulkanBackendMemory& memoryHandle,
|
||||
VkDeviceSize offset,
|
||||
VkDeviceSize size) {
|
||||
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
|
||||
const VmaAllocation allocation = (VmaAllocation)memoryHandle;
|
||||
return vmaFlushAllocation(fAllocator, allocation, offset, size);
|
||||
}
|
||||
|
||||
VkResult VkTestMemoryAllocator::invalidateMemory(const skgpu::VulkanBackendMemory& memoryHandle,
|
||||
VkDeviceSize offset,
|
||||
VkDeviceSize size) {
|
||||
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
|
||||
const VmaAllocation allocation = (VmaAllocation)memoryHandle;
|
||||
return vmaInvalidateAllocation(fAllocator, allocation, offset, size);
|
||||
}
|
||||
|
||||
std::pair<uint64_t, uint64_t> VkTestMemoryAllocator::totalAllocatedAndUsedMemory() const {
|
||||
VmaTotalStatistics stats;
|
||||
vmaCalculateStatistics(fAllocator, &stats);
|
||||
return {stats.total.statistics.blockBytes, stats.total.statistics.allocationBytes};
|
||||
}
|
||||
|
||||
} // namespace sk_gpu_test
|
||||
69
src/VulkanContext/VkTestMemoryAllocator.h
Normal file
69
src/VulkanContext/VkTestMemoryAllocator.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// This file has been copied from the Skia library, and may be slightly modified. Please check the NOTICE file for more details
|
||||
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef VkTestMemoryAllocator_DEFINED
|
||||
#define VkTestMemoryAllocator_DEFINED
|
||||
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <include/gpu/vk/VulkanMemoryAllocator.h>
|
||||
|
||||
namespace skgpu {
|
||||
class VulkanExtensions;
|
||||
struct VulkanInterface;
|
||||
} // namespace skgpu
|
||||
|
||||
namespace sk_gpu_test {
|
||||
|
||||
// A test-only Vulkan memory allocator. Based on
|
||||
// https://skia.googlesource.com/skia/+/c3fbd20fbc7d3662dd31a0e4139226d2951d1ae4/src/gpu/vk/VulkanAMDMemoryAllocator.h.
|
||||
class VkTestMemoryAllocator : public skgpu::VulkanMemoryAllocator {
|
||||
public:
|
||||
static sk_sp<VulkanMemoryAllocator> Make(VkInstance instance,
|
||||
VkPhysicalDevice physicalDevice,
|
||||
VkDevice device,
|
||||
uint32_t physicalDeviceVersion,
|
||||
const skgpu::VulkanExtensions* extensions,
|
||||
const skgpu::VulkanInterface* interface);
|
||||
|
||||
~VkTestMemoryAllocator() override;
|
||||
|
||||
VkResult allocateImageMemory(VkImage image,
|
||||
uint32_t allocationPropertyFlags,
|
||||
skgpu::VulkanBackendMemory*) override;
|
||||
|
||||
VkResult allocateBufferMemory(VkBuffer buffer,
|
||||
BufferUsage usage,
|
||||
uint32_t allocationPropertyFlags,
|
||||
skgpu::VulkanBackendMemory*) override;
|
||||
|
||||
void freeMemory(const skgpu::VulkanBackendMemory&) override;
|
||||
|
||||
void getAllocInfo(const skgpu::VulkanBackendMemory&, skgpu::VulkanAlloc*) const override;
|
||||
|
||||
VkResult mapMemory(const skgpu::VulkanBackendMemory&, void** data) override;
|
||||
void unmapMemory(const skgpu::VulkanBackendMemory&) override;
|
||||
|
||||
VkResult flushMemory(const skgpu::VulkanBackendMemory&,
|
||||
VkDeviceSize offset,
|
||||
VkDeviceSize size) override;
|
||||
VkResult invalidateMemory(const skgpu::VulkanBackendMemory&,
|
||||
VkDeviceSize offset,
|
||||
VkDeviceSize size) override;
|
||||
|
||||
std::pair<uint64_t, uint64_t> totalAllocatedAndUsedMemory() const override;
|
||||
|
||||
private:
|
||||
VkTestMemoryAllocator(VmaAllocator allocator);
|
||||
|
||||
VmaAllocator fAllocator;
|
||||
};
|
||||
|
||||
} // namespace sk_gpu_test
|
||||
|
||||
#endif // VkTestMemoryAllocator_DEFINED
|
||||
879
src/VulkanContext/VkTestUtils.cpp
Normal file
879
src/VulkanContext/VkTestUtils.cpp
Normal file
|
|
@ -0,0 +1,879 @@
|
|||
// This file has been copied from the Skia library, and may be slightly modified. Please check the NOTICE file for more details
|
||||
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <SDL3/SDL_vulkan.h> // Looking for instance required extensions for SDL
|
||||
|
||||
#include <src/gpu/vk/VulkanInterface.h>
|
||||
#include "VkTestMemoryAllocator.h"
|
||||
#include "VkTestUtils.h"
|
||||
|
||||
#ifndef SK_GPU_TOOLS_VK_LIBRARY_NAME
|
||||
#if defined _WIN32
|
||||
#define SK_GPU_TOOLS_VK_LIBRARY_NAME vulkan-1.dll
|
||||
#elif defined SK_BUILD_FOR_MAC
|
||||
#define SK_GPU_TOOLS_VK_LIBRARY_NAME libvk_swiftshader.dylib
|
||||
#else
|
||||
#define SK_GPU_TOOLS_VK_LIBRARY_NAME libvulkan.so
|
||||
#define SK_GPU_TOOLS_VK_LIBRARY_NAME_BACKUP libvulkan.so.1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define STRINGIFY2(S) #S
|
||||
#define STRINGIFY(S) STRINGIFY2(S)
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(__GLIBC__)
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
#include "include/gpu/vk/VulkanBackendContext.h"
|
||||
#include "include/gpu/vk/VulkanExtensions.h"
|
||||
#include "src/base/SkAutoMalloc.h"
|
||||
|
||||
#if defined(SK_ENABLE_SCOPED_LSAN_SUPPRESSIONS)
|
||||
#include <sanitizer/lsan_interface.h>
|
||||
#endif
|
||||
|
||||
using namespace skia_private;
|
||||
|
||||
namespace sk_gpu_test {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Helper code to set up Vulkan context objects
|
||||
|
||||
#ifdef SK_ENABLE_VK_LAYERS
|
||||
const char* kDebugLayerNames[] = {
|
||||
// single merged layer
|
||||
"VK_LAYER_KHRONOS_validation",
|
||||
// not included in standard_validation
|
||||
//"VK_LAYER_LUNARG_api_dump",
|
||||
//"VK_LAYER_LUNARG_vktrace",
|
||||
//"VK_LAYER_LUNARG_screenshot",
|
||||
};
|
||||
|
||||
static uint32_t remove_patch_version(uint32_t specVersion) {
|
||||
return (specVersion >> 12) << 12;
|
||||
}
|
||||
|
||||
// Returns the index into layers array for the layer we want. Returns -1 if not supported.
|
||||
static int should_include_debug_layer(const char* layerName,
|
||||
uint32_t layerCount, VkLayerProperties* layers,
|
||||
uint32_t version) {
|
||||
for (uint32_t i = 0; i < layerCount; ++i) {
|
||||
if (!strcmp(layerName, layers[i].layerName)) {
|
||||
// Since the layers intercept the vulkan calls and forward them on, we need to make sure
|
||||
// layer was written against a version that isn't older than the version of Vulkan we're
|
||||
// using so that it has all the api entry points.
|
||||
if (version <= remove_patch_version(layers[i].specVersion)) {
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void print_backtrace() {
|
||||
#if defined(__GLIBC__)
|
||||
void* stack[64];
|
||||
int count = backtrace(stack, std::size(stack));
|
||||
backtrace_symbols_fd(stack, count, 2);
|
||||
#else
|
||||
// Please add implementations for other platforms.
|
||||
#endif
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL DebugReportCallback(
|
||||
VkDebugReportFlagsEXT flags,
|
||||
VkDebugReportObjectTypeEXT objectType,
|
||||
uint64_t object,
|
||||
size_t location,
|
||||
int32_t messageCode,
|
||||
const char* pLayerPrefix,
|
||||
const char* pMessage,
|
||||
void* pUserData) {
|
||||
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
|
||||
// See https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/1887
|
||||
if (strstr(pMessage, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-01521") ||
|
||||
strstr(pMessage, "VUID-VkGraphicsPipelineCreateInfo-pDynamicStates-01522")) {
|
||||
return VK_FALSE;
|
||||
}
|
||||
// See https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2171
|
||||
if (strstr(pMessage, "VUID-vkCmdDraw-None-02686") ||
|
||||
strstr(pMessage, "VUID-vkCmdDrawIndexed-None-02686")) {
|
||||
return VK_FALSE;
|
||||
}
|
||||
SkDebugf("Vulkan error [%s]: code: %d: %s\n", pLayerPrefix, messageCode, pMessage);
|
||||
print_backtrace();
|
||||
SkDEBUGFAIL("Vulkan debug layer error");
|
||||
return VK_TRUE; // skip further layers
|
||||
} else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
|
||||
SkDebugf("Vulkan warning [%s]: code: %d: %s\n", pLayerPrefix, messageCode, pMessage);
|
||||
print_backtrace();
|
||||
} else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
|
||||
SkDebugf("Vulkan perf warning [%s]: code: %d: %s\n", pLayerPrefix, messageCode, pMessage);
|
||||
print_backtrace();
|
||||
} else {
|
||||
SkDebugf("Vulkan info/debug [%s]: code: %d: %s\n", pLayerPrefix, messageCode, pMessage);
|
||||
}
|
||||
return VK_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ACQUIRE_VK_INST_PROC_LOCAL(name, instance) \
|
||||
PFN_vk##name grVk##name = \
|
||||
reinterpret_cast<PFN_vk##name>(getInstProc(instance, "vk" #name)); \
|
||||
do { \
|
||||
if (grVk##name == nullptr) { \
|
||||
SkDebugf("Function ptr for vk%s could not be acquired\n", #name); \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// Returns the index into layers array for the layer we want. Returns -1 if not supported.
|
||||
static bool should_include_extension(const char* extensionName) {
|
||||
const char* kValidExtensions[] = {
|
||||
// single merged layer
|
||||
VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME,
|
||||
VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME,
|
||||
VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
|
||||
VK_EXT_DEVICE_FAULT_EXTENSION_NAME,
|
||||
VK_EXT_FRAME_BOUNDARY_EXTENSION_NAME,
|
||||
VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
|
||||
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
|
||||
VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME,
|
||||
VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
|
||||
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
|
||||
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
|
||||
VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
|
||||
VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
|
||||
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
|
||||
VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME,
|
||||
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
|
||||
VK_KHR_MAINTENANCE2_EXTENSION_NAME,
|
||||
VK_KHR_MAINTENANCE3_EXTENSION_NAME,
|
||||
VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
|
||||
VK_KHR_SURFACE_EXTENSION_NAME,
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
// Below are all platform specific extensions. The name macros like we use above are
|
||||
// all defined in platform specific vulkan headers. We currently don't include these
|
||||
// headers as they are a little bit of a pain (e.g. windows headers requires including
|
||||
// <windows.h> which causes all sorts of fun annoyances/problems. So instead we are
|
||||
// just listing the strings these macros are defined to. This really shouldn't cause
|
||||
// any long term issues as the chances of the strings connected to the name macros
|
||||
// changing is next to zero.
|
||||
"VK_KHR_win32_surface", // VK_KHR_WIN32_SURFACE_EXTENSION_NAME
|
||||
"VK_KHR_xcb_surface", // VK_KHR_XCB_SURFACE_EXTENSION_NAME,
|
||||
"VK_ANDROID_external_memory_android_hardware_buffer",
|
||||
// VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
|
||||
"VK_KHR_android_surface", // VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < std::size(kValidExtensions); ++i) {
|
||||
if (!strcmp(extensionName, kValidExtensions[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool init_instance_extensions_and_layers(PFN_vkGetInstanceProcAddr getInstProc,
|
||||
uint32_t specVersion,
|
||||
TArray<VkExtensionProperties>* instanceExtensions,
|
||||
TArray<VkLayerProperties>* instanceLayers) {
|
||||
if (getInstProc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ACQUIRE_VK_INST_PROC_LOCAL(EnumerateInstanceExtensionProperties, VK_NULL_HANDLE);
|
||||
ACQUIRE_VK_INST_PROC_LOCAL(EnumerateInstanceLayerProperties, VK_NULL_HANDLE);
|
||||
|
||||
VkResult res;
|
||||
uint32_t layerCount = 0;
|
||||
#ifdef SK_ENABLE_VK_LAYERS
|
||||
// instance layers
|
||||
res = grVkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
VkLayerProperties* layers = new VkLayerProperties[layerCount];
|
||||
res = grVkEnumerateInstanceLayerProperties(&layerCount, layers);
|
||||
if (VK_SUCCESS != res) {
|
||||
delete[] layers;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t nonPatchVersion = remove_patch_version(specVersion);
|
||||
for (size_t i = 0; i < std::size(kDebugLayerNames); ++i) {
|
||||
int idx = should_include_debug_layer(kDebugLayerNames[i], layerCount, layers,
|
||||
nonPatchVersion);
|
||||
if (idx != -1) {
|
||||
instanceLayers->push_back() = layers[idx];
|
||||
}
|
||||
}
|
||||
delete[] layers;
|
||||
#endif
|
||||
|
||||
// instance extensions
|
||||
// via Vulkan implementation and implicitly enabled layers
|
||||
{
|
||||
uint32_t extensionCount = 0;
|
||||
res = grVkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if SDL extension requirements are met
|
||||
uint32_t sdlRequiredExtensionCount = 0;
|
||||
char const * const * sdlRequiredExtensions = SDL_Vulkan_GetInstanceExtensions(&sdlRequiredExtensionCount);
|
||||
|
||||
VkExtensionProperties* extensions = new VkExtensionProperties[extensionCount];
|
||||
res = grVkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions);
|
||||
if (VK_SUCCESS != res) {
|
||||
delete[] extensions;
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < extensionCount; ++i) {
|
||||
if (should_include_extension(extensions[i].extensionName)) {
|
||||
instanceExtensions->push_back() = extensions[i];
|
||||
}
|
||||
else {
|
||||
for(uint32_t j = 0; j < sdlRequiredExtensionCount; j++) {
|
||||
if(strcmp(extensions[i].extensionName, sdlRequiredExtensions[j]) == 0) {
|
||||
instanceExtensions->push_back() = extensions[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
delete [] extensions;
|
||||
}
|
||||
|
||||
// via explicitly enabled layers
|
||||
layerCount = instanceLayers->size();
|
||||
for (uint32_t layerIndex = 0; layerIndex < layerCount; ++layerIndex) {
|
||||
uint32_t extensionCount = 0;
|
||||
res = grVkEnumerateInstanceExtensionProperties((*instanceLayers)[layerIndex].layerName,
|
||||
&extensionCount, nullptr);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
VkExtensionProperties* extensions = new VkExtensionProperties[extensionCount];
|
||||
res = grVkEnumerateInstanceExtensionProperties((*instanceLayers)[layerIndex].layerName,
|
||||
&extensionCount, extensions);
|
||||
if (VK_SUCCESS != res) {
|
||||
delete[] extensions;
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < extensionCount; ++i) {
|
||||
if (should_include_extension(extensions[i].extensionName)) {
|
||||
instanceExtensions->push_back() = extensions[i];
|
||||
}
|
||||
}
|
||||
delete[] extensions;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define GET_PROC_LOCAL(F, inst, device) PFN_vk ## F F = (PFN_vk ## F) getProc("vk" #F, inst, device)
|
||||
|
||||
static bool init_device_extensions_and_layers(const skgpu::VulkanGetProc& getProc,
|
||||
uint32_t specVersion, VkInstance inst,
|
||||
VkPhysicalDevice physDev,
|
||||
TArray<VkExtensionProperties>* deviceExtensions,
|
||||
TArray<VkLayerProperties>* deviceLayers) {
|
||||
if (getProc == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GET_PROC_LOCAL(EnumerateDeviceExtensionProperties, inst, VK_NULL_HANDLE);
|
||||
GET_PROC_LOCAL(EnumerateDeviceLayerProperties, inst, VK_NULL_HANDLE);
|
||||
|
||||
if (!EnumerateDeviceExtensionProperties ||
|
||||
!EnumerateDeviceLayerProperties) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VkResult res;
|
||||
// device layers
|
||||
uint32_t layerCount = 0;
|
||||
#ifdef SK_ENABLE_VK_LAYERS
|
||||
res = EnumerateDeviceLayerProperties(physDev, &layerCount, nullptr);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
VkLayerProperties* layers = new VkLayerProperties[layerCount];
|
||||
res = EnumerateDeviceLayerProperties(physDev, &layerCount, layers);
|
||||
if (VK_SUCCESS != res) {
|
||||
delete[] layers;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t nonPatchVersion = remove_patch_version(specVersion);
|
||||
for (size_t i = 0; i < std::size(kDebugLayerNames); ++i) {
|
||||
int idx = should_include_debug_layer(kDebugLayerNames[i], layerCount, layers,
|
||||
nonPatchVersion);
|
||||
if (idx != -1) {
|
||||
deviceLayers->push_back() = layers[idx];
|
||||
}
|
||||
}
|
||||
delete[] layers;
|
||||
#endif
|
||||
|
||||
// device extensions
|
||||
// via Vulkan implementation and implicitly enabled layers
|
||||
{
|
||||
uint32_t extensionCount = 0;
|
||||
res = EnumerateDeviceExtensionProperties(physDev, nullptr, &extensionCount, nullptr);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
VkExtensionProperties* extensions = new VkExtensionProperties[extensionCount];
|
||||
res = EnumerateDeviceExtensionProperties(physDev, nullptr, &extensionCount, extensions);
|
||||
if (VK_SUCCESS != res) {
|
||||
delete[] extensions;
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < extensionCount; ++i) {
|
||||
if (should_include_extension(extensions[i].extensionName)) {
|
||||
deviceExtensions->push_back() = extensions[i];
|
||||
}
|
||||
}
|
||||
delete[] extensions;
|
||||
}
|
||||
|
||||
// via explicitly enabled layers
|
||||
layerCount = deviceLayers->size();
|
||||
for (uint32_t layerIndex = 0; layerIndex < layerCount; ++layerIndex) {
|
||||
uint32_t extensionCount = 0;
|
||||
res = EnumerateDeviceExtensionProperties(physDev,
|
||||
(*deviceLayers)[layerIndex].layerName,
|
||||
&extensionCount, nullptr);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
VkExtensionProperties* extensions = new VkExtensionProperties[extensionCount];
|
||||
res = EnumerateDeviceExtensionProperties(physDev,
|
||||
(*deviceLayers)[layerIndex].layerName,
|
||||
&extensionCount, extensions);
|
||||
if (VK_SUCCESS != res) {
|
||||
delete[] extensions;
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < extensionCount; ++i) {
|
||||
if (should_include_extension(extensions[i].extensionName)) {
|
||||
deviceExtensions->push_back() = extensions[i];
|
||||
}
|
||||
}
|
||||
delete[] extensions;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define ACQUIRE_VK_INST_PROC_NOCHECK(name, instance) \
|
||||
PFN_vk##name grVk##name = reinterpret_cast<PFN_vk##name>(getInstProc(instance, "vk" #name))
|
||||
|
||||
#define ACQUIRE_VK_INST_PROC(name, instance) \
|
||||
PFN_vk##name grVk##name = \
|
||||
reinterpret_cast<PFN_vk##name>(getInstProc(instance, "vk" #name)); \
|
||||
do { \
|
||||
if (grVk##name == nullptr) { \
|
||||
SkDebugf("Function ptr for vk%s could not be acquired\n", #name); \
|
||||
if (inst != VK_NULL_HANDLE) { \
|
||||
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension); \
|
||||
} \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ACQUIRE_VK_PROC_NOCHECK(name, instance, device) \
|
||||
PFN_vk##name grVk##name = reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, device))
|
||||
|
||||
#define ACQUIRE_VK_PROC(name, instance, device) \
|
||||
PFN_vk##name grVk##name = \
|
||||
reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, device)); \
|
||||
do { \
|
||||
if (grVk##name == nullptr) { \
|
||||
SkDebugf("Function ptr for vk%s could not be acquired\n", #name); \
|
||||
if (inst != VK_NULL_HANDLE) { \
|
||||
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension); \
|
||||
} \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ACQUIRE_VK_PROC_LOCAL(name, instance, device) \
|
||||
PFN_vk##name grVk##name = \
|
||||
reinterpret_cast<PFN_vk##name>(getProc("vk" #name, instance, device)); \
|
||||
do { \
|
||||
if (grVk##name == nullptr) { \
|
||||
SkDebugf("Function ptr for vk%s could not be acquired\n", #name); \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static bool destroy_instance(PFN_vkGetInstanceProcAddr getInstProc, VkInstance inst,
|
||||
VkDebugReportCallbackEXT* debugCallback,
|
||||
bool hasDebugExtension) {
|
||||
if (hasDebugExtension && *debugCallback != VK_NULL_HANDLE) {
|
||||
ACQUIRE_VK_INST_PROC_LOCAL(DestroyDebugReportCallbackEXT, inst);
|
||||
grVkDestroyDebugReportCallbackEXT(inst, *debugCallback, nullptr);
|
||||
*debugCallback = VK_NULL_HANDLE;
|
||||
}
|
||||
ACQUIRE_VK_INST_PROC_LOCAL(DestroyInstance, inst);
|
||||
grVkDestroyInstance(inst, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setup_features(const skgpu::VulkanGetProc& getProc, VkInstance inst,
|
||||
VkPhysicalDevice physDev, uint32_t physDeviceVersion,
|
||||
skgpu::VulkanExtensions* extensions, VkPhysicalDeviceFeatures2* features,
|
||||
bool isProtected) {
|
||||
SkASSERT(physDeviceVersion >= VK_MAKE_VERSION(1, 1, 0) ||
|
||||
extensions->hasExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, 1));
|
||||
|
||||
// Setup all extension feature structs we may want to use.
|
||||
void** tailPNext = &features->pNext;
|
||||
|
||||
// If |isProtected| is given, attach that first
|
||||
VkPhysicalDeviceProtectedMemoryFeatures* protectedMemoryFeatures = nullptr;
|
||||
if (isProtected) {
|
||||
SkASSERT(physDeviceVersion >= VK_MAKE_VERSION(1, 1, 0));
|
||||
protectedMemoryFeatures =
|
||||
(VkPhysicalDeviceProtectedMemoryFeatures*)sk_malloc_throw(
|
||||
sizeof(VkPhysicalDeviceProtectedMemoryFeatures));
|
||||
protectedMemoryFeatures->sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
|
||||
protectedMemoryFeatures->pNext = nullptr;
|
||||
*tailPNext = protectedMemoryFeatures;
|
||||
tailPNext = &protectedMemoryFeatures->pNext;
|
||||
}
|
||||
|
||||
VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT* blend = nullptr;
|
||||
if (extensions->hasExtension(VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME, 2)) {
|
||||
blend = (VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT*) sk_malloc_throw(
|
||||
sizeof(VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT));
|
||||
blend->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT;
|
||||
blend->pNext = nullptr;
|
||||
*tailPNext = blend;
|
||||
tailPNext = &blend->pNext;
|
||||
}
|
||||
|
||||
VkPhysicalDeviceSamplerYcbcrConversionFeatures* ycbcrFeature = nullptr;
|
||||
if (physDeviceVersion >= VK_MAKE_VERSION(1, 1, 0) ||
|
||||
extensions->hasExtension(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, 1)) {
|
||||
ycbcrFeature = (VkPhysicalDeviceSamplerYcbcrConversionFeatures*) sk_malloc_throw(
|
||||
sizeof(VkPhysicalDeviceSamplerYcbcrConversionFeatures));
|
||||
ycbcrFeature->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
|
||||
ycbcrFeature->pNext = nullptr;
|
||||
ycbcrFeature->samplerYcbcrConversion = VK_TRUE;
|
||||
*tailPNext = ycbcrFeature;
|
||||
tailPNext = &ycbcrFeature->pNext;
|
||||
}
|
||||
|
||||
if (physDeviceVersion >= VK_MAKE_VERSION(1, 1, 0)) {
|
||||
ACQUIRE_VK_PROC_LOCAL(GetPhysicalDeviceFeatures2, inst, VK_NULL_HANDLE);
|
||||
grVkGetPhysicalDeviceFeatures2(physDev, features);
|
||||
} else {
|
||||
SkASSERT(extensions->hasExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
|
||||
1));
|
||||
ACQUIRE_VK_PROC_LOCAL(GetPhysicalDeviceFeatures2KHR, inst, VK_NULL_HANDLE);
|
||||
grVkGetPhysicalDeviceFeatures2KHR(physDev, features);
|
||||
}
|
||||
|
||||
if (isProtected) {
|
||||
if (!protectedMemoryFeatures->protectedMemory) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
// If we want to disable any extension features do so here.
|
||||
}
|
||||
|
||||
bool CreateVkBackendContext(PFN_vkGetInstanceProcAddr getInstProc,
|
||||
skgpu::VulkanBackendContext* ctx,
|
||||
skgpu::VulkanExtensions* extensions,
|
||||
VkPhysicalDeviceFeatures2* features,
|
||||
VkDebugReportCallbackEXT* debugCallback,
|
||||
uint32_t* presentQueueIndexPtr,
|
||||
const CanPresentFn& canPresent,
|
||||
bool isProtected) {
|
||||
VkResult err;
|
||||
|
||||
ACQUIRE_VK_INST_PROC_NOCHECK(EnumerateInstanceVersion, VK_NULL_HANDLE);
|
||||
uint32_t instanceVersion = 0;
|
||||
if (!grVkEnumerateInstanceVersion) {
|
||||
instanceVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||
} else {
|
||||
err = grVkEnumerateInstanceVersion(&instanceVersion);
|
||||
if (err) {
|
||||
SkDebugf("failed to enumerate instance version. Err: %d\n", err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
SkASSERT(instanceVersion >= VK_MAKE_VERSION(1, 0, 0));
|
||||
if (isProtected && instanceVersion < VK_MAKE_VERSION(1, 1, 0)) {
|
||||
SkDebugf("protected requires vk instance version 1.1\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t apiVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||
if (instanceVersion >= VK_MAKE_VERSION(1, 1, 0)) {
|
||||
// If the instance version is 1.0 we must have the apiVersion also be 1.0. However, if the
|
||||
// instance version is 1.1 or higher, we can set the apiVersion to be whatever the highest
|
||||
// api we may use in skia (technically it can be arbitrary). So for now we set it to 1.1
|
||||
// since that is the highest vulkan version.
|
||||
apiVersion = VK_MAKE_VERSION(1, 1, 0);
|
||||
}
|
||||
|
||||
instanceVersion = std::min(instanceVersion, apiVersion);
|
||||
|
||||
STArray<2, VkPhysicalDevice> physDevs;
|
||||
VkDevice device;
|
||||
VkInstance inst = VK_NULL_HANDLE;
|
||||
|
||||
const VkApplicationInfo app_info = {
|
||||
VK_STRUCTURE_TYPE_APPLICATION_INFO, // sType
|
||||
nullptr, // pNext
|
||||
"vktest", // pApplicationName
|
||||
0, // applicationVersion
|
||||
"vktest", // pEngineName
|
||||
0, // engineVerison
|
||||
apiVersion, // apiVersion
|
||||
};
|
||||
|
||||
TArray<VkLayerProperties> instanceLayers;
|
||||
TArray<VkExtensionProperties> instanceExtensions;
|
||||
|
||||
if (!init_instance_extensions_and_layers(getInstProc, instanceVersion,
|
||||
&instanceExtensions,
|
||||
&instanceLayers)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TArray<const char*> instanceLayerNames;
|
||||
TArray<const char*> instanceExtensionNames;
|
||||
for (int i = 0; i < instanceLayers.size(); ++i) {
|
||||
instanceLayerNames.push_back(instanceLayers[i].layerName);
|
||||
}
|
||||
for (int i = 0; i < instanceExtensions.size(); ++i) {
|
||||
instanceExtensionNames.push_back(instanceExtensions[i].extensionName);
|
||||
}
|
||||
|
||||
const VkInstanceCreateInfo instance_create = {
|
||||
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // sType
|
||||
nullptr, // pNext
|
||||
0, // flags
|
||||
&app_info, // pApplicationInfo
|
||||
(uint32_t) instanceLayerNames.size(), // enabledLayerNameCount
|
||||
instanceLayerNames.begin(), // ppEnabledLayerNames
|
||||
(uint32_t) instanceExtensionNames.size(), // enabledExtensionNameCount
|
||||
instanceExtensionNames.begin(), // ppEnabledExtensionNames
|
||||
};
|
||||
|
||||
bool hasDebugExtension = false;
|
||||
|
||||
ACQUIRE_VK_INST_PROC(CreateInstance, VK_NULL_HANDLE);
|
||||
err = grVkCreateInstance(&instance_create, nullptr, &inst);
|
||||
if (err < 0) {
|
||||
SkDebugf("vkCreateInstance failed: %d\n", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
ACQUIRE_VK_INST_PROC(GetDeviceProcAddr, inst);
|
||||
auto getProc = [getInstProc, grVkGetDeviceProcAddr](const char* proc_name,
|
||||
VkInstance instance, VkDevice device) {
|
||||
if (device != VK_NULL_HANDLE) {
|
||||
return grVkGetDeviceProcAddr(device, proc_name);
|
||||
}
|
||||
return getInstProc(instance, proc_name);
|
||||
};
|
||||
|
||||
#ifdef SK_ENABLE_VK_LAYERS
|
||||
*debugCallback = VK_NULL_HANDLE;
|
||||
for (int i = 0; i < instanceExtensionNames.size() && !hasDebugExtension; ++i) {
|
||||
if (!strcmp(instanceExtensionNames[i], VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) {
|
||||
hasDebugExtension = true;
|
||||
}
|
||||
}
|
||||
if (hasDebugExtension) {
|
||||
// Setup callback creation information
|
||||
VkDebugReportCallbackCreateInfoEXT callbackCreateInfo;
|
||||
callbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
|
||||
callbackCreateInfo.pNext = nullptr;
|
||||
callbackCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT |
|
||||
VK_DEBUG_REPORT_WARNING_BIT_EXT |
|
||||
// VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
|
||||
// VK_DEBUG_REPORT_DEBUG_BIT_EXT |
|
||||
VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
|
||||
callbackCreateInfo.pfnCallback = &DebugReportCallback;
|
||||
callbackCreateInfo.pUserData = nullptr;
|
||||
|
||||
ACQUIRE_VK_PROC(CreateDebugReportCallbackEXT, inst, VK_NULL_HANDLE);
|
||||
// Register the callback
|
||||
grVkCreateDebugReportCallbackEXT(inst, &callbackCreateInfo, nullptr, debugCallback);
|
||||
}
|
||||
#endif
|
||||
|
||||
ACQUIRE_VK_PROC(EnumeratePhysicalDevices, inst, VK_NULL_HANDLE);
|
||||
ACQUIRE_VK_PROC(GetPhysicalDeviceProperties, inst, VK_NULL_HANDLE);
|
||||
ACQUIRE_VK_PROC(GetPhysicalDeviceQueueFamilyProperties, inst, VK_NULL_HANDLE);
|
||||
ACQUIRE_VK_PROC(GetPhysicalDeviceFeatures, inst, VK_NULL_HANDLE);
|
||||
ACQUIRE_VK_PROC(CreateDevice, inst, VK_NULL_HANDLE);
|
||||
ACQUIRE_VK_PROC(GetDeviceQueue, inst, VK_NULL_HANDLE);
|
||||
ACQUIRE_VK_PROC(DeviceWaitIdle, inst, VK_NULL_HANDLE);
|
||||
ACQUIRE_VK_PROC(DestroyDevice, inst, VK_NULL_HANDLE);
|
||||
|
||||
uint32_t gpuCount;
|
||||
err = grVkEnumeratePhysicalDevices(inst, &gpuCount, nullptr);
|
||||
if (err) {
|
||||
SkDebugf("vkEnumeratePhysicalDevices failed: %d\n", err);
|
||||
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
|
||||
return false;
|
||||
}
|
||||
if (!gpuCount) {
|
||||
SkDebugf("vkEnumeratePhysicalDevices returned no supported devices.\n");
|
||||
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
|
||||
return false;
|
||||
}
|
||||
// Allocate enough storage for all available physical devices. We should be able to just ask for
|
||||
// the first one, but a bug in RenderDoc (https://github.com/baldurk/renderdoc/issues/2766)
|
||||
// will smash the stack if we do that.
|
||||
physDevs.resize(gpuCount);
|
||||
err = grVkEnumeratePhysicalDevices(inst, &gpuCount, physDevs.data());
|
||||
if (err) {
|
||||
SkDebugf("vkEnumeratePhysicalDevices failed: %d\n", err);
|
||||
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
|
||||
return false;
|
||||
}
|
||||
// We just use the first physical device.
|
||||
// TODO: find best match for our needs
|
||||
VkPhysicalDevice physDev = physDevs.front();
|
||||
|
||||
VkPhysicalDeviceProperties physDeviceProperties;
|
||||
grVkGetPhysicalDeviceProperties(physDev, &physDeviceProperties);
|
||||
uint32_t physDeviceVersion = std::min(physDeviceProperties.apiVersion, apiVersion);
|
||||
|
||||
if (isProtected && physDeviceVersion < VK_MAKE_VERSION(1, 1, 0)) {
|
||||
SkDebugf("protected requires vk physical device version 1.1\n");
|
||||
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
|
||||
return false;
|
||||
}
|
||||
|
||||
// query to get the initial queue props size
|
||||
uint32_t queueCount;
|
||||
grVkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr);
|
||||
if (!queueCount) {
|
||||
SkDebugf("vkGetPhysicalDeviceQueueFamilyProperties returned no queues.\n");
|
||||
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
|
||||
return false;
|
||||
}
|
||||
|
||||
SkAutoMalloc queuePropsAlloc(queueCount * sizeof(VkQueueFamilyProperties));
|
||||
// now get the actual queue props
|
||||
VkQueueFamilyProperties* queueProps = (VkQueueFamilyProperties*)queuePropsAlloc.get();
|
||||
|
||||
grVkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueProps);
|
||||
|
||||
// iterate to find the graphics queue
|
||||
uint32_t graphicsQueueIndex = queueCount;
|
||||
for (uint32_t i = 0; i < queueCount; i++) {
|
||||
if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||
graphicsQueueIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (graphicsQueueIndex == queueCount) {
|
||||
SkDebugf("Could not find any supported graphics queues.\n");
|
||||
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
|
||||
return false;
|
||||
}
|
||||
|
||||
// iterate to find the present queue, if needed
|
||||
uint32_t presentQueueIndex = queueCount;
|
||||
if (presentQueueIndexPtr && canPresent) {
|
||||
for (uint32_t i = 0; i < queueCount; i++) {
|
||||
if (canPresent(inst, physDev, i)) {
|
||||
presentQueueIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (presentQueueIndex == queueCount) {
|
||||
SkDebugf("Could not find any supported present queues.\n");
|
||||
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
|
||||
return false;
|
||||
}
|
||||
*presentQueueIndexPtr = presentQueueIndex;
|
||||
} else {
|
||||
// Just setting this so we end up make a single queue for graphics since there was no
|
||||
// request for a present queue.
|
||||
presentQueueIndex = graphicsQueueIndex;
|
||||
}
|
||||
|
||||
TArray<VkLayerProperties> deviceLayers;
|
||||
TArray<VkExtensionProperties> deviceExtensions;
|
||||
if (!init_device_extensions_and_layers(getProc, physDeviceVersion,
|
||||
inst, physDev,
|
||||
&deviceExtensions,
|
||||
&deviceLayers)) {
|
||||
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
|
||||
return false;
|
||||
}
|
||||
|
||||
TArray<const char*> deviceLayerNames;
|
||||
TArray<const char*> deviceExtensionNames;
|
||||
for (int i = 0; i < deviceLayers.size(); ++i) {
|
||||
deviceLayerNames.push_back(deviceLayers[i].layerName);
|
||||
}
|
||||
|
||||
for (int i = 0; i < deviceExtensions.size(); ++i) {
|
||||
deviceExtensionNames.push_back(deviceExtensions[i].extensionName);
|
||||
}
|
||||
|
||||
extensions->init(getProc, inst, physDev,
|
||||
(uint32_t) instanceExtensionNames.size(),
|
||||
instanceExtensionNames.begin(),
|
||||
(uint32_t) deviceExtensionNames.size(),
|
||||
deviceExtensionNames.begin());
|
||||
|
||||
memset(features, 0, sizeof(VkPhysicalDeviceFeatures2));
|
||||
features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
||||
features->pNext = nullptr;
|
||||
|
||||
VkPhysicalDeviceFeatures* deviceFeatures = &features->features;
|
||||
void* pointerToFeatures = nullptr;
|
||||
if (physDeviceVersion >= VK_MAKE_VERSION(1, 1, 0) ||
|
||||
extensions->hasExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, 1)) {
|
||||
if (!setup_features(getProc, inst, physDev, physDeviceVersion, extensions, features,
|
||||
isProtected)) {
|
||||
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we set the pNext of the VkDeviceCreateInfo to our VkPhysicalDeviceFeatures2 struct,
|
||||
// the device creation will use that instead of the ppEnabledFeatures.
|
||||
pointerToFeatures = features;
|
||||
} else {
|
||||
grVkGetPhysicalDeviceFeatures(physDev, deviceFeatures);
|
||||
}
|
||||
|
||||
// this looks like it would slow things down,
|
||||
// and we can't depend on it on all platforms
|
||||
deviceFeatures->robustBufferAccess = VK_FALSE;
|
||||
|
||||
VkDeviceQueueCreateFlags flags = isProtected ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0;
|
||||
float queuePriorities[1] = { 0.0 };
|
||||
// Here we assume no need for swapchain queue
|
||||
// If one is needed, the client will need its own setup code
|
||||
const VkDeviceQueueCreateInfo queueInfo[2] = {
|
||||
{
|
||||
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
|
||||
nullptr, // pNext
|
||||
flags, // VkDeviceQueueCreateFlags
|
||||
graphicsQueueIndex, // queueFamilyIndex
|
||||
1, // queueCount
|
||||
queuePriorities, // pQueuePriorities
|
||||
|
||||
},
|
||||
{
|
||||
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType
|
||||
nullptr, // pNext
|
||||
0, // VkDeviceQueueCreateFlags
|
||||
presentQueueIndex, // queueFamilyIndex
|
||||
1, // queueCount
|
||||
queuePriorities, // pQueuePriorities
|
||||
}
|
||||
};
|
||||
uint32_t queueInfoCount = (presentQueueIndex != graphicsQueueIndex) ? 2 : 1;
|
||||
|
||||
const VkDeviceCreateInfo deviceInfo = {
|
||||
VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // sType
|
||||
pointerToFeatures, // pNext
|
||||
0, // VkDeviceCreateFlags
|
||||
queueInfoCount, // queueCreateInfoCount
|
||||
queueInfo, // pQueueCreateInfos
|
||||
(uint32_t) deviceLayerNames.size(), // layerCount
|
||||
deviceLayerNames.begin(), // ppEnabledLayerNames
|
||||
(uint32_t) deviceExtensionNames.size(), // extensionCount
|
||||
deviceExtensionNames.begin(), // ppEnabledExtensionNames
|
||||
pointerToFeatures ? nullptr : deviceFeatures // ppEnabledFeatures
|
||||
};
|
||||
|
||||
{
|
||||
#if defined(SK_ENABLE_SCOPED_LSAN_SUPPRESSIONS)
|
||||
// skia:8712
|
||||
__lsan::ScopedDisabler lsanDisabler;
|
||||
#endif
|
||||
err = grVkCreateDevice(physDev, &deviceInfo, nullptr, &device);
|
||||
}
|
||||
if (err) {
|
||||
SkDebugf("CreateDevice failed: %d\n", err);
|
||||
destroy_instance(getInstProc, inst, debugCallback, hasDebugExtension);
|
||||
return false;
|
||||
}
|
||||
|
||||
VkQueue queue;
|
||||
if (isProtected) {
|
||||
ACQUIRE_VK_PROC(GetDeviceQueue2, inst, device);
|
||||
SkASSERT(grVkGetDeviceQueue2 != nullptr);
|
||||
VkDeviceQueueInfo2 queue_info2 = {
|
||||
VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, // sType
|
||||
nullptr, // pNext
|
||||
VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT, // flags
|
||||
graphicsQueueIndex, // queueFamilyIndex
|
||||
0 // queueIndex
|
||||
};
|
||||
grVkGetDeviceQueue2(device, &queue_info2, &queue);
|
||||
} else {
|
||||
grVkGetDeviceQueue(device, graphicsQueueIndex, 0, &queue);
|
||||
}
|
||||
|
||||
skgpu::VulkanInterface interface = skgpu::VulkanInterface(
|
||||
getProc, inst, device, instanceVersion, physDeviceVersion, extensions);
|
||||
SkASSERT(interface.validate(instanceVersion, physDeviceVersion, extensions));
|
||||
|
||||
sk_sp<skgpu::VulkanMemoryAllocator> memoryAllocator = VkTestMemoryAllocator::Make(
|
||||
inst, physDev, device, physDeviceVersion, extensions, &interface);
|
||||
|
||||
ctx->fInstance = inst;
|
||||
ctx->fPhysicalDevice = physDev;
|
||||
ctx->fDevice = device;
|
||||
ctx->fQueue = queue;
|
||||
ctx->fGraphicsQueueIndex = graphicsQueueIndex;
|
||||
ctx->fMaxAPIVersion = apiVersion;
|
||||
ctx->fVkExtensions = extensions;
|
||||
ctx->fDeviceFeatures2 = features;
|
||||
ctx->fGetProc = getProc;
|
||||
ctx->fProtectedContext = skgpu::Protected(isProtected);
|
||||
ctx->fMemoryAllocator = memoryAllocator;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FreeVulkanFeaturesStructs(const VkPhysicalDeviceFeatures2* features) {
|
||||
// All Vulkan structs that could be part of the features chain will start with the
|
||||
// structure type followed by the pNext pointer. We cast to the CommonVulkanHeader
|
||||
// so we can get access to the pNext for the next struct.
|
||||
struct CommonVulkanHeader {
|
||||
VkStructureType sType;
|
||||
void* pNext;
|
||||
};
|
||||
|
||||
void* pNext = features->pNext;
|
||||
while (pNext) {
|
||||
void* current = pNext;
|
||||
pNext = static_cast<CommonVulkanHeader*>(current)->pNext;
|
||||
sk_free(current);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sk_gpu_test
|
||||
40
src/VulkanContext/VkTestUtils.h
Normal file
40
src/VulkanContext/VkTestUtils.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// This file has been copied from the Skia library, and may be slightly modified. Please check the NOTICE file for more details
|
||||
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef VkTestUtils_DEFINED
|
||||
#define VkTestUtils_DEFINED
|
||||
|
||||
#include <include/core/SkTypes.h>
|
||||
#include <include/gpu/vk/VulkanBackendContext.h>
|
||||
#include <tools/gpu/vk/GrVulkanDefines.h>
|
||||
#include <functional>
|
||||
|
||||
namespace skgpu {
|
||||
struct VulkanBackendContext;
|
||||
class VulkanExtensions;
|
||||
}
|
||||
|
||||
namespace sk_gpu_test {
|
||||
using CanPresentFn = std::function<bool(VkInstance, VkPhysicalDevice,
|
||||
uint32_t queueFamilyIndex)>;
|
||||
|
||||
bool CreateVkBackendContext(PFN_vkGetInstanceProcAddr getInstProc,
|
||||
skgpu::VulkanBackendContext* ctx,
|
||||
skgpu::VulkanExtensions*,
|
||||
VkPhysicalDeviceFeatures2*,
|
||||
VkDebugReportCallbackEXT* debugCallback,
|
||||
uint32_t* presentQueueIndexPtr = nullptr,
|
||||
const CanPresentFn& canPresent = CanPresentFn(),
|
||||
bool isProtected = false);
|
||||
|
||||
void FreeVulkanFeaturesStructs(const VkPhysicalDeviceFeatures2*);
|
||||
|
||||
} // namespace sk_gpu_test
|
||||
|
||||
#endif
|
||||
600
src/VulkanContext/VulkanWindowContext.cpp
Normal file
600
src/VulkanContext/VulkanWindowContext.cpp
Normal file
|
|
@ -0,0 +1,600 @@
|
|||
// This file has been copied from the Skia library, and may be slightly modified. Please check the NOTICE file for more details
|
||||
|
||||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "VulkanWindowContext.h"
|
||||
|
||||
#include <include/core/SkSurface.h>
|
||||
#include <include/gpu/ganesh/GrBackendSemaphore.h>
|
||||
#include <include/gpu/ganesh/GrBackendSurface.h>
|
||||
#include <include/gpu/ganesh/GrDirectContext.h>
|
||||
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
|
||||
#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
|
||||
#include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
|
||||
#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
|
||||
#include <include/gpu/ganesh/vk/GrVkTypes.h>
|
||||
#include <include/gpu/vk/VulkanExtensions.h>
|
||||
#include <src/gpu/GpuTypesPriv.h>
|
||||
#include <include/gpu/vk/VulkanMutableTextureState.h>
|
||||
#include <src/base/SkAutoMalloc.h>
|
||||
#include <src/gpu/ganesh/vk/GrVkImage.h>
|
||||
#include <src/gpu/ganesh/vk/GrVkUtil.h>
|
||||
#include <src/gpu/vk/VulkanInterface.h>
|
||||
#include <src/gpu/vk/vulkanmemoryallocator/VulkanAMDMemoryAllocator.h>
|
||||
|
||||
#ifdef VK_USE_PLATFORM_WIN32_KHR
|
||||
// windows wants to define this as CreateSemaphoreA or CreateSemaphoreW
|
||||
#undef CreateSemaphore
|
||||
#endif
|
||||
|
||||
#define GET_PROC(F) f ## F = \
|
||||
(PFN_vk ## F) backendContext.fGetProc("vk" #F, fInstance, VK_NULL_HANDLE)
|
||||
#define GET_DEV_PROC(F) f ## F = \
|
||||
(PFN_vk ## F) backendContext.fGetProc("vk" #F, VK_NULL_HANDLE, fDevice)
|
||||
|
||||
namespace skwindow::internal {
|
||||
|
||||
VulkanWindowContext::VulkanWindowContext(std::unique_ptr<const DisplayParams> params,
|
||||
CreateVkSurfaceFn createVkSurface,
|
||||
CanPresentFn canPresent,
|
||||
PFN_vkGetInstanceProcAddr instProc)
|
||||
: WindowContext(std::move(params))
|
||||
, fCreateVkSurfaceFn(std::move(createVkSurface))
|
||||
, fCanPresentFn(std::move(canPresent))
|
||||
, fSurface(VK_NULL_HANDLE)
|
||||
, fSwapchain(VK_NULL_HANDLE)
|
||||
, fImages(nullptr)
|
||||
, fImageLayouts(nullptr)
|
||||
, fSurfaces(nullptr)
|
||||
, fBackbuffers(nullptr) {
|
||||
fGetInstanceProcAddr = instProc;
|
||||
this->initializeContext();
|
||||
}
|
||||
|
||||
void VulkanWindowContext::initializeContext() {
|
||||
SkASSERT(!fContext);
|
||||
// any config code here (particularly for msaa)?
|
||||
|
||||
PFN_vkGetInstanceProcAddr getInstanceProc = fGetInstanceProcAddr;
|
||||
skgpu::VulkanBackendContext backendContext;
|
||||
skgpu::VulkanExtensions extensions;
|
||||
VkPhysicalDeviceFeatures2 features;
|
||||
if (!sk_gpu_test::CreateVkBackendContext(getInstanceProc,
|
||||
&backendContext,
|
||||
&extensions,
|
||||
&features,
|
||||
&fDebugCallback,
|
||||
&fPresentQueueIndex,
|
||||
fCanPresentFn,
|
||||
fDisplayParams->createProtectedNativeBackend())) {
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!extensions.hasExtension(VK_KHR_SURFACE_EXTENSION_NAME, 25) ||
|
||||
!extensions.hasExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, 68)) {
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
return;
|
||||
}
|
||||
|
||||
fInstance = backendContext.fInstance;
|
||||
fPhysicalDevice = backendContext.fPhysicalDevice;
|
||||
fDevice = backendContext.fDevice;
|
||||
fGraphicsQueueIndex = backendContext.fGraphicsQueueIndex;
|
||||
fGraphicsQueue = backendContext.fQueue;
|
||||
|
||||
PFN_vkGetPhysicalDeviceProperties localGetPhysicalDeviceProperties =
|
||||
reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(
|
||||
backendContext.fGetProc("vkGetPhysicalDeviceProperties",
|
||||
backendContext.fInstance,
|
||||
VK_NULL_HANDLE));
|
||||
if (!localGetPhysicalDeviceProperties) {
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
return;
|
||||
}
|
||||
VkPhysicalDeviceProperties physDeviceProperties;
|
||||
localGetPhysicalDeviceProperties(backendContext.fPhysicalDevice, &physDeviceProperties);
|
||||
uint32_t physDevVersion = physDeviceProperties.apiVersion;
|
||||
|
||||
fInterface.reset(new skgpu::VulkanInterface(backendContext.fGetProc,
|
||||
fInstance,
|
||||
fDevice,
|
||||
backendContext.fMaxAPIVersion,
|
||||
physDevVersion,
|
||||
&extensions));
|
||||
|
||||
GET_PROC(DestroyInstance);
|
||||
if (fDebugCallback != VK_NULL_HANDLE) {
|
||||
GET_PROC(DestroyDebugReportCallbackEXT);
|
||||
}
|
||||
GET_PROC(DestroySurfaceKHR);
|
||||
GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
|
||||
GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
|
||||
GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
|
||||
GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
|
||||
GET_DEV_PROC(DeviceWaitIdle);
|
||||
GET_DEV_PROC(QueueWaitIdle);
|
||||
GET_DEV_PROC(DestroyDevice);
|
||||
GET_DEV_PROC(CreateSwapchainKHR);
|
||||
GET_DEV_PROC(DestroySwapchainKHR);
|
||||
GET_DEV_PROC(GetSwapchainImagesKHR);
|
||||
GET_DEV_PROC(AcquireNextImageKHR);
|
||||
GET_DEV_PROC(QueuePresentKHR);
|
||||
GET_DEV_PROC(GetDeviceQueue);
|
||||
|
||||
backendContext.fMemoryAllocator =
|
||||
skgpu::VulkanAMDMemoryAllocator::Make(fInstance,
|
||||
backendContext.fPhysicalDevice,
|
||||
backendContext.fDevice,
|
||||
physDevVersion,
|
||||
&extensions,
|
||||
fInterface.get(),
|
||||
skgpu::ThreadSafe::kNo,
|
||||
/*blockSize=*/std::nullopt);
|
||||
|
||||
fContext = GrDirectContexts::MakeVulkan(backendContext, fDisplayParams->grContextOptions());
|
||||
|
||||
fSurface = fCreateVkSurfaceFn(fInstance);
|
||||
if (VK_NULL_HANDLE == fSurface) {
|
||||
this->destroyContext();
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
return;
|
||||
}
|
||||
|
||||
VkBool32 supported;
|
||||
VkResult res = fGetPhysicalDeviceSurfaceSupportKHR(fPhysicalDevice, fPresentQueueIndex,
|
||||
fSurface, &supported);
|
||||
if (VK_SUCCESS != res) {
|
||||
this->destroyContext();
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->createSwapchain(-1, -1)) {
|
||||
this->destroyContext();
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
return;
|
||||
}
|
||||
|
||||
// create presentQueue
|
||||
fGetDeviceQueue(fDevice, fPresentQueueIndex, 0, &fPresentQueue);
|
||||
sk_gpu_test::FreeVulkanFeaturesStructs(&features);
|
||||
}
|
||||
|
||||
bool VulkanWindowContext::createSwapchain(int width, int height) {
|
||||
// check for capabilities
|
||||
VkSurfaceCapabilitiesKHR caps;
|
||||
VkResult res = fGetPhysicalDeviceSurfaceCapabilitiesKHR(fPhysicalDevice, fSurface, &caps);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t surfaceFormatCount;
|
||||
res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
|
||||
nullptr);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR));
|
||||
VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get();
|
||||
res = fGetPhysicalDeviceSurfaceFormatsKHR(fPhysicalDevice, fSurface, &surfaceFormatCount,
|
||||
surfaceFormats);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t presentModeCount;
|
||||
res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
|
||||
nullptr);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR));
|
||||
VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get();
|
||||
res = fGetPhysicalDeviceSurfacePresentModesKHR(fPhysicalDevice, fSurface, &presentModeCount,
|
||||
presentModes);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VkExtent2D extent = caps.currentExtent;
|
||||
// use the hints
|
||||
if (extent.width == (uint32_t)-1) {
|
||||
extent.width = width;
|
||||
extent.height = height;
|
||||
}
|
||||
|
||||
// clamp width; to protect us from broken hints
|
||||
if (extent.width < caps.minImageExtent.width) {
|
||||
extent.width = caps.minImageExtent.width;
|
||||
} else if (extent.width > caps.maxImageExtent.width) {
|
||||
extent.width = caps.maxImageExtent.width;
|
||||
}
|
||||
// clamp height
|
||||
if (extent.height < caps.minImageExtent.height) {
|
||||
extent.height = caps.minImageExtent.height;
|
||||
} else if (extent.height > caps.maxImageExtent.height) {
|
||||
extent.height = caps.maxImageExtent.height;
|
||||
}
|
||||
|
||||
fWidth = (int)extent.width;
|
||||
fHeight = (int)extent.height;
|
||||
|
||||
uint32_t imageCount = caps.minImageCount + 2;
|
||||
if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
|
||||
// Application must settle for fewer images than desired:
|
||||
imageCount = caps.maxImageCount;
|
||||
}
|
||||
|
||||
VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags);
|
||||
if (caps.supportedUsageFlags & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) {
|
||||
usageFlags |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||
}
|
||||
if (caps.supportedUsageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) {
|
||||
usageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
SkASSERT(caps.supportedTransforms & caps.currentTransform);
|
||||
SkASSERT(caps.supportedCompositeAlpha & (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
|
||||
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR));
|
||||
VkCompositeAlphaFlagBitsKHR composite_alpha =
|
||||
(caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ?
|
||||
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR :
|
||||
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
|
||||
// Pick our surface format.
|
||||
VkFormat surfaceFormat = VK_FORMAT_UNDEFINED;
|
||||
VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
|
||||
for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
|
||||
VkFormat localFormat = surfaceFormats[i].format;
|
||||
if (GrVkFormatIsSupported(localFormat)) {
|
||||
surfaceFormat = localFormat;
|
||||
colorSpace = surfaceFormats[i].colorSpace;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fSampleCount = std::max(1, fDisplayParams->msaaSampleCount());
|
||||
fStencilBits = 8;
|
||||
|
||||
if (VK_FORMAT_UNDEFINED == surfaceFormat) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkColorType colorType;
|
||||
switch (surfaceFormat) {
|
||||
case VK_FORMAT_R8G8B8A8_UNORM: // fall through
|
||||
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||
colorType = kRGBA_8888_SkColorType;
|
||||
break;
|
||||
case VK_FORMAT_B8G8R8A8_UNORM: // fall through
|
||||
colorType = kBGRA_8888_SkColorType;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// If mailbox mode is available, use it, as it is the lowest-latency non-
|
||||
// tearing mode. If not, fall back to FIFO which is always available.
|
||||
VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
bool hasImmediate = false;
|
||||
for (uint32_t i = 0; i < presentModeCount; ++i) {
|
||||
// use mailbox
|
||||
if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) {
|
||||
mode = VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
}
|
||||
if (VK_PRESENT_MODE_IMMEDIATE_KHR == presentModes[i]) {
|
||||
hasImmediate = true;
|
||||
}
|
||||
}
|
||||
if (fDisplayParams->disableVsync() && hasImmediate) {
|
||||
mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
}
|
||||
|
||||
VkSwapchainCreateInfoKHR swapchainCreateInfo;
|
||||
memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR));
|
||||
swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||
swapchainCreateInfo.flags = fDisplayParams->createProtectedNativeBackend()
|
||||
? VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR
|
||||
: 0;
|
||||
swapchainCreateInfo.surface = fSurface;
|
||||
swapchainCreateInfo.minImageCount = imageCount;
|
||||
swapchainCreateInfo.imageFormat = surfaceFormat;
|
||||
swapchainCreateInfo.imageColorSpace = colorSpace;
|
||||
swapchainCreateInfo.imageExtent = extent;
|
||||
swapchainCreateInfo.imageArrayLayers = 1;
|
||||
swapchainCreateInfo.imageUsage = usageFlags;
|
||||
|
||||
uint32_t queueFamilies[] = { fGraphicsQueueIndex, fPresentQueueIndex };
|
||||
if (fGraphicsQueueIndex != fPresentQueueIndex) {
|
||||
swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||
swapchainCreateInfo.queueFamilyIndexCount = 2;
|
||||
swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
|
||||
} else {
|
||||
swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
swapchainCreateInfo.queueFamilyIndexCount = 0;
|
||||
swapchainCreateInfo.pQueueFamilyIndices = nullptr;
|
||||
}
|
||||
|
||||
swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
swapchainCreateInfo.compositeAlpha = composite_alpha;
|
||||
swapchainCreateInfo.presentMode = mode;
|
||||
swapchainCreateInfo.clipped = true;
|
||||
swapchainCreateInfo.oldSwapchain = fSwapchain;
|
||||
|
||||
res = fCreateSwapchainKHR(fDevice, &swapchainCreateInfo, nullptr, &fSwapchain);
|
||||
if (VK_SUCCESS != res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// destroy the old swapchain
|
||||
if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
|
||||
fDeviceWaitIdle(fDevice);
|
||||
|
||||
this->destroyBuffers();
|
||||
|
||||
fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
|
||||
}
|
||||
|
||||
if (!this->createBuffers(swapchainCreateInfo.imageFormat, usageFlags, colorType,
|
||||
swapchainCreateInfo.imageSharingMode)) {
|
||||
fDeviceWaitIdle(fDevice);
|
||||
|
||||
this->destroyBuffers();
|
||||
|
||||
fDestroySwapchainKHR(fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanWindowContext::createBuffers(VkFormat format,
|
||||
VkImageUsageFlags usageFlags,
|
||||
SkColorType colorType,
|
||||
VkSharingMode sharingMode) {
|
||||
fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, nullptr);
|
||||
SkASSERT(fImageCount);
|
||||
fImages = new VkImage[fImageCount];
|
||||
fGetSwapchainImagesKHR(fDevice, fSwapchain, &fImageCount, fImages);
|
||||
|
||||
// set up initial image layouts and create surfaces
|
||||
fImageLayouts = new VkImageLayout[fImageCount];
|
||||
fSurfaces = new sk_sp<SkSurface>[fImageCount];
|
||||
for (uint32_t i = 0; i < fImageCount; ++i) {
|
||||
fImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
GrVkImageInfo info;
|
||||
info.fImage = fImages[i];
|
||||
info.fAlloc = skgpu::VulkanAlloc();
|
||||
info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
info.fFormat = format;
|
||||
info.fImageUsageFlags = usageFlags;
|
||||
info.fLevelCount = 1;
|
||||
info.fCurrentQueueFamily = fPresentQueueIndex;
|
||||
info.fProtected = skgpu::Protected(fDisplayParams->createProtectedNativeBackend());
|
||||
info.fSharingMode = sharingMode;
|
||||
|
||||
if (usageFlags & VK_IMAGE_USAGE_SAMPLED_BIT) {
|
||||
GrBackendTexture backendTexture = GrBackendTextures::MakeVk(fWidth, fHeight, info);
|
||||
fSurfaces[i] = SkSurfaces::WrapBackendTexture(fContext.get(),
|
||||
backendTexture,
|
||||
kTopLeft_GrSurfaceOrigin,
|
||||
fDisplayParams->msaaSampleCount(),
|
||||
colorType,
|
||||
fDisplayParams->colorSpace(),
|
||||
&fDisplayParams->surfaceProps());
|
||||
} else {
|
||||
if (fDisplayParams->msaaSampleCount() > 1) {
|
||||
return false;
|
||||
}
|
||||
info.fSampleCount = fSampleCount;
|
||||
GrBackendRenderTarget backendRT = GrBackendRenderTargets::MakeVk(fWidth, fHeight, info);
|
||||
fSurfaces[i] = SkSurfaces::WrapBackendRenderTarget(fContext.get(),
|
||||
backendRT,
|
||||
kTopLeft_GrSurfaceOrigin,
|
||||
colorType,
|
||||
fDisplayParams->colorSpace(),
|
||||
&fDisplayParams->surfaceProps());
|
||||
}
|
||||
if (!fSurfaces[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// set up the backbuffers
|
||||
VkSemaphoreCreateInfo semaphoreInfo;
|
||||
memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
|
||||
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
semaphoreInfo.pNext = nullptr;
|
||||
semaphoreInfo.flags = 0;
|
||||
|
||||
// we create one additional backbuffer structure here, because we want to
|
||||
// give the command buffers they contain a chance to finish before we cycle back
|
||||
fBackbuffers = new BackbufferInfo[fImageCount + 1];
|
||||
for (uint32_t i = 0; i < fImageCount + 1; ++i) {
|
||||
fBackbuffers[i].fImageIndex = -1;
|
||||
SkDEBUGCODE(VkResult result = )GR_VK_CALL(fInterface,
|
||||
CreateSemaphore(fDevice, &semaphoreInfo, nullptr,
|
||||
&fBackbuffers[i].fRenderSemaphore));
|
||||
SkASSERT(result == VK_SUCCESS);
|
||||
}
|
||||
fCurrentBackbufferIndex = fImageCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VulkanWindowContext::destroyBuffers() {
|
||||
|
||||
if (fBackbuffers) {
|
||||
for (uint32_t i = 0; i < fImageCount + 1; ++i) {
|
||||
fBackbuffers[i].fImageIndex = -1;
|
||||
GR_VK_CALL(fInterface,
|
||||
DestroySemaphore(fDevice,
|
||||
fBackbuffers[i].fRenderSemaphore,
|
||||
nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
delete[] fBackbuffers;
|
||||
fBackbuffers = nullptr;
|
||||
|
||||
// Does this actually free the surfaces?
|
||||
delete[] fSurfaces;
|
||||
fSurfaces = nullptr;
|
||||
delete[] fImageLayouts;
|
||||
fImageLayouts = nullptr;
|
||||
delete[] fImages;
|
||||
fImages = nullptr;
|
||||
}
|
||||
|
||||
VulkanWindowContext::~VulkanWindowContext() {
|
||||
this->destroyContext();
|
||||
}
|
||||
|
||||
void VulkanWindowContext::destroyContext() {
|
||||
if (this->isValid()) {
|
||||
fQueueWaitIdle(fPresentQueue);
|
||||
fDeviceWaitIdle(fDevice);
|
||||
|
||||
this->destroyBuffers();
|
||||
|
||||
if (VK_NULL_HANDLE != fSwapchain) {
|
||||
fDestroySwapchainKHR(fDevice, fSwapchain, nullptr);
|
||||
fSwapchain = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (VK_NULL_HANDLE != fSurface) {
|
||||
fDestroySurfaceKHR(fInstance, fSurface, nullptr);
|
||||
fSurface = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
SkASSERT(fContext->unique());
|
||||
fContext.reset();
|
||||
fInterface.reset();
|
||||
|
||||
if (VK_NULL_HANDLE != fDevice) {
|
||||
fDestroyDevice(fDevice, nullptr);
|
||||
fDevice = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
#ifdef SK_ENABLE_VK_LAYERS
|
||||
if (fDebugCallback != VK_NULL_HANDLE) {
|
||||
fDestroyDebugReportCallbackEXT(fInstance, fDebugCallback, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
fPhysicalDevice = VK_NULL_HANDLE;
|
||||
|
||||
if (VK_NULL_HANDLE != fInstance) {
|
||||
fDestroyInstance(fInstance, nullptr);
|
||||
fInstance = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
VulkanWindowContext::BackbufferInfo* VulkanWindowContext::getAvailableBackbuffer() {
|
||||
SkASSERT(fBackbuffers);
|
||||
|
||||
++fCurrentBackbufferIndex;
|
||||
if (fCurrentBackbufferIndex > fImageCount) {
|
||||
fCurrentBackbufferIndex = 0;
|
||||
}
|
||||
|
||||
BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
|
||||
return backbuffer;
|
||||
}
|
||||
|
||||
sk_sp<SkSurface> VulkanWindowContext::getBackbufferSurface() {
|
||||
BackbufferInfo* backbuffer = this->getAvailableBackbuffer();
|
||||
SkASSERT(backbuffer);
|
||||
|
||||
// semaphores should be in unsignaled state
|
||||
VkSemaphoreCreateInfo semaphoreInfo;
|
||||
memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
|
||||
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
semaphoreInfo.pNext = nullptr;
|
||||
semaphoreInfo.flags = 0;
|
||||
VkSemaphore semaphore;
|
||||
SkDEBUGCODE(VkResult result = )GR_VK_CALL(fInterface, CreateSemaphore(fDevice, &semaphoreInfo,
|
||||
nullptr, &semaphore));
|
||||
SkASSERT(result == VK_SUCCESS);
|
||||
|
||||
// acquire the image
|
||||
VkResult res = fAcquireNextImageKHR(fDevice, fSwapchain, UINT64_MAX,
|
||||
semaphore, VK_NULL_HANDLE,
|
||||
&backbuffer->fImageIndex);
|
||||
if (VK_ERROR_SURFACE_LOST_KHR == res) {
|
||||
// need to figure out how to create a new vkSurface without the platformData*
|
||||
// maybe use attach somehow? but need a Window
|
||||
GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
|
||||
return nullptr;
|
||||
}
|
||||
if (VK_ERROR_OUT_OF_DATE_KHR == res) {
|
||||
// tear swapchain down and try again
|
||||
if (!this->createSwapchain(-1, -1)) {
|
||||
GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
|
||||
return nullptr;
|
||||
}
|
||||
backbuffer = this->getAvailableBackbuffer();
|
||||
|
||||
// acquire the image
|
||||
res = fAcquireNextImageKHR(fDevice, fSwapchain, UINT64_MAX,
|
||||
semaphore, VK_NULL_HANDLE,
|
||||
&backbuffer->fImageIndex);
|
||||
|
||||
if (VK_SUCCESS != res) {
|
||||
GR_VK_CALL(fInterface, DestroySemaphore(fDevice, semaphore, nullptr));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SkSurface* surface = fSurfaces[backbuffer->fImageIndex].get();
|
||||
|
||||
GrBackendSemaphore beSemaphore = GrBackendSemaphores::MakeVk(semaphore);
|
||||
|
||||
surface->wait(1, &beSemaphore);
|
||||
|
||||
return sk_ref_sp(surface);
|
||||
}
|
||||
|
||||
void VulkanWindowContext::onSwapBuffers() {
|
||||
|
||||
BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
|
||||
SkSurface* surface = fSurfaces[backbuffer->fImageIndex].get();
|
||||
|
||||
GrBackendSemaphore beSemaphore = GrBackendSemaphores::MakeVk(backbuffer->fRenderSemaphore);
|
||||
|
||||
GrFlushInfo info;
|
||||
info.fNumSemaphores = 1;
|
||||
info.fSignalSemaphores = &beSemaphore;
|
||||
skgpu::MutableTextureState presentState = skgpu::MutableTextureStates::MakeVulkan(
|
||||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, fPresentQueueIndex);
|
||||
auto dContext = surface->recordingContext()->asDirectContext();
|
||||
dContext->flush(surface, info, &presentState);
|
||||
dContext->submit();
|
||||
|
||||
// Submit present operation to present queue
|
||||
const VkPresentInfoKHR presentInfo =
|
||||
{
|
||||
VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, // sType
|
||||
nullptr, // pNext
|
||||
1, // waitSemaphoreCount
|
||||
&backbuffer->fRenderSemaphore, // pWaitSemaphores
|
||||
1, // swapchainCount
|
||||
&fSwapchain, // pSwapchains
|
||||
&backbuffer->fImageIndex, // pImageIndices
|
||||
nullptr // pResults
|
||||
};
|
||||
|
||||
fQueuePresentKHR(fPresentQueue, &presentInfo);
|
||||
}
|
||||
|
||||
} // namespace skwindow::internal
|
||||
114
src/VulkanContext/VulkanWindowContext.h
Normal file
114
src/VulkanContext/VulkanWindowContext.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// This file has been copied from the Skia library, and may be slightly modified. Please check the NOTICE file for more details
|
||||
|
||||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef VulkanWindowContext_DEFINED
|
||||
#define VulkanWindowContext_DEFINED
|
||||
|
||||
#include <include/core/SkTypes.h>
|
||||
#include <include/gpu/vk/VulkanBackendContext.h>
|
||||
#include "VkTestUtils.h"
|
||||
#include "WindowContext.h"
|
||||
|
||||
class GrRenderTarget;
|
||||
|
||||
namespace skgpu { struct VulkanInterface; }
|
||||
|
||||
namespace skwindow::internal {
|
||||
|
||||
class VulkanWindowContext : public WindowContext {
|
||||
public:
|
||||
~VulkanWindowContext() override;
|
||||
|
||||
sk_sp<SkSurface> getBackbufferSurface() override;
|
||||
|
||||
bool isValid() override { return fDevice != VK_NULL_HANDLE; }
|
||||
|
||||
void resize(int w, int h) override { this->createSwapchain(w, h); }
|
||||
|
||||
void setDisplayParams(std::unique_ptr<const DisplayParams> params) override {
|
||||
this->destroyContext();
|
||||
fDisplayParams = std::move(params);
|
||||
this->initializeContext();
|
||||
}
|
||||
|
||||
/** Platform specific function that creates a VkSurfaceKHR for a window */
|
||||
using CreateVkSurfaceFn = std::function<VkSurfaceKHR(VkInstance)>;
|
||||
/** Platform specific function that determines whether presentation will succeed. */
|
||||
using CanPresentFn = sk_gpu_test::CanPresentFn;
|
||||
|
||||
VulkanWindowContext(std::unique_ptr<const DisplayParams>,
|
||||
CreateVkSurfaceFn,
|
||||
CanPresentFn,
|
||||
PFN_vkGetInstanceProcAddr);
|
||||
|
||||
private:
|
||||
void initializeContext();
|
||||
void destroyContext();
|
||||
|
||||
struct BackbufferInfo {
|
||||
uint32_t fImageIndex; // image this is associated with
|
||||
VkSemaphore fRenderSemaphore; // we wait on this for rendering to be done
|
||||
};
|
||||
|
||||
BackbufferInfo* getAvailableBackbuffer();
|
||||
bool createSwapchain(int width, int height);
|
||||
bool createBuffers(VkFormat format, VkImageUsageFlags, SkColorType colorType, VkSharingMode);
|
||||
void destroyBuffers();
|
||||
void onSwapBuffers() override;
|
||||
|
||||
VkInstance fInstance = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice fPhysicalDevice = VK_NULL_HANDLE;
|
||||
VkDevice fDevice = VK_NULL_HANDLE;
|
||||
VkDebugReportCallbackEXT fDebugCallback = VK_NULL_HANDLE;
|
||||
|
||||
// Create functions
|
||||
CreateVkSurfaceFn fCreateVkSurfaceFn;
|
||||
CanPresentFn fCanPresentFn;
|
||||
|
||||
PFN_vkGetInstanceProcAddr fGetInstanceProcAddr = nullptr;
|
||||
|
||||
// WSI interface functions
|
||||
PFN_vkDestroySurfaceKHR fDestroySurfaceKHR = nullptr;
|
||||
PFN_vkGetPhysicalDeviceSurfaceSupportKHR fGetPhysicalDeviceSurfaceSupportKHR = nullptr;
|
||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fGetPhysicalDeviceSurfaceCapabilitiesKHR =nullptr;
|
||||
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fGetPhysicalDeviceSurfaceFormatsKHR = nullptr;
|
||||
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fGetPhysicalDeviceSurfacePresentModesKHR =nullptr;
|
||||
|
||||
PFN_vkCreateSwapchainKHR fCreateSwapchainKHR = nullptr;
|
||||
PFN_vkDestroySwapchainKHR fDestroySwapchainKHR = nullptr;
|
||||
PFN_vkGetSwapchainImagesKHR fGetSwapchainImagesKHR = nullptr;
|
||||
PFN_vkAcquireNextImageKHR fAcquireNextImageKHR = nullptr;
|
||||
PFN_vkQueuePresentKHR fQueuePresentKHR = nullptr;
|
||||
|
||||
PFN_vkDestroyInstance fDestroyInstance = nullptr;
|
||||
PFN_vkDeviceWaitIdle fDeviceWaitIdle = nullptr;
|
||||
PFN_vkDestroyDebugReportCallbackEXT fDestroyDebugReportCallbackEXT = nullptr;
|
||||
PFN_vkQueueWaitIdle fQueueWaitIdle = nullptr;
|
||||
PFN_vkDestroyDevice fDestroyDevice = nullptr;
|
||||
PFN_vkGetDeviceQueue fGetDeviceQueue = nullptr;
|
||||
|
||||
sk_sp<const skgpu::VulkanInterface> fInterface;
|
||||
|
||||
VkSurfaceKHR fSurface;
|
||||
VkSwapchainKHR fSwapchain;
|
||||
uint32_t fGraphicsQueueIndex;
|
||||
VkQueue fGraphicsQueue;
|
||||
uint32_t fPresentQueueIndex;
|
||||
VkQueue fPresentQueue;
|
||||
|
||||
uint32_t fImageCount;
|
||||
VkImage* fImages; // images in the swapchain
|
||||
VkImageLayout* fImageLayouts; // layouts of these images when not color attachment
|
||||
sk_sp<SkSurface>* fSurfaces; // surfaces client renders to (may not be based on rts)
|
||||
BackbufferInfo* fBackbuffers;
|
||||
uint32_t fCurrentBackbufferIndex;
|
||||
};
|
||||
|
||||
} // namespace skwindow::internal
|
||||
|
||||
#endif
|
||||
89
src/VulkanContext/WindowContext.cpp
Normal file
89
src/VulkanContext/WindowContext.cpp
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// This file has been copied from the Skia library, and may be slightly modified. Please check the NOTICE file for more details
|
||||
|
||||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <tools/window/WindowContext.h>
|
||||
#include <include/gpu/ganesh/GrDirectContext.h>
|
||||
#if defined(SK_GRAPHITE)
|
||||
#include <include/gpu/graphite/Context.h>
|
||||
#include <include/gpu/graphite/Recorder.h>
|
||||
#endif
|
||||
|
||||
namespace skwindow {
|
||||
|
||||
WindowContext::WindowContext(std::unique_ptr<const DisplayParams> params)
|
||||
: fDisplayParams(std::move(params)) {}
|
||||
|
||||
WindowContext::~WindowContext() {}
|
||||
|
||||
void WindowContext::swapBuffers() {
|
||||
this->onSwapBuffers();
|
||||
}
|
||||
|
||||
bool WindowContext::supportsGpuTimer() const {
|
||||
auto flags = skgpu::GpuStatsFlags::kNone;
|
||||
#if defined(SK_GRAPHITE)
|
||||
if (fGraphiteContext) {
|
||||
flags = fGraphiteContext->supportedGpuStats();
|
||||
} else
|
||||
#endif
|
||||
if (fContext) {
|
||||
flags = fContext->supportedGpuStats();
|
||||
}
|
||||
using T = std::underlying_type_t<skgpu::GpuStatsFlags>;
|
||||
return static_cast<T>(flags) & static_cast<T>(skgpu::GpuStatsFlags::kElapsedTime);
|
||||
}
|
||||
|
||||
void WindowContext::submitToGpu(GpuTimerCallback statsCallback) {
|
||||
#if defined(SK_GRAPHITE)
|
||||
if (fGraphiteContext) {
|
||||
SkASSERT(fGraphiteRecorder);
|
||||
std::unique_ptr<skgpu::graphite::Recording> recording = fGraphiteRecorder->snap();
|
||||
if (recording) {
|
||||
skgpu::graphite::InsertRecordingInfo info;
|
||||
if (statsCallback) {
|
||||
auto callback = std::make_unique<GpuTimerCallback>(std::move(statsCallback));
|
||||
info.fFinishedContext = callback.release();
|
||||
info.fFinishedWithStatsProc = [](skgpu::graphite::GpuFinishedContext context,
|
||||
skgpu::CallbackResult,
|
||||
const skgpu::GpuStats& stats) {
|
||||
std::unique_ptr<GpuTimerCallback> callback{
|
||||
static_cast<GpuTimerCallback*>(context)};
|
||||
(*callback)(stats.elapsedTime);
|
||||
};
|
||||
info.fGpuStatsFlags = skgpu::GpuStatsFlags::kElapsedTime;
|
||||
}
|
||||
info.fRecording = recording.get();
|
||||
fGraphiteContext->insertRecording(info);
|
||||
fGraphiteContext->submit(skgpu::graphite::SyncToCpu::kNo);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (auto dc = this->directContext()) {
|
||||
GrFlushInfo info;
|
||||
if (statsCallback) {
|
||||
auto callback = std::make_unique<GpuTimerCallback>(std::move(statsCallback));
|
||||
info.fFinishedContext = callback.release();
|
||||
info.fFinishedWithStatsProc = [](GrGpuFinishedContext context,
|
||||
const skgpu::GpuStats& stats) {
|
||||
std::unique_ptr<GpuTimerCallback> callback{static_cast<GpuTimerCallback*>(context)};
|
||||
(*callback)(stats.elapsedTime);
|
||||
};
|
||||
info.fGpuStatsFlags = skgpu::GpuStatsFlags::kElapsedTime;
|
||||
}
|
||||
dc->flush(info);
|
||||
dc->submit();
|
||||
return;
|
||||
}
|
||||
if (statsCallback) {
|
||||
statsCallback(0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace skwindow
|
||||
87
src/VulkanContext/WindowContext.h
Normal file
87
src/VulkanContext/WindowContext.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
// This file has been copied from the Skia library, and may be slightly modified. Please check the NOTICE file for more details
|
||||
|
||||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#ifndef WindowContext_DEFINED
|
||||
#define WindowContext_DEFINED
|
||||
|
||||
#include <include/core/SkRefCnt.h>
|
||||
#include <include/core/SkSurfaceProps.h>
|
||||
#include <include/gpu/ganesh/GrTypes.h>
|
||||
#include <tools/window/DisplayParams.h>
|
||||
|
||||
class GrDirectContext;
|
||||
class SkSurface;
|
||||
#if defined(SK_GRAPHITE)
|
||||
namespace skgpu::graphite {
|
||||
class Context;
|
||||
class Recorder;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace skwindow {
|
||||
|
||||
class WindowContext {
|
||||
public:
|
||||
WindowContext(std::unique_ptr<const DisplayParams>);
|
||||
|
||||
virtual ~WindowContext();
|
||||
|
||||
virtual sk_sp<SkSurface> getBackbufferSurface() = 0;
|
||||
|
||||
void swapBuffers();
|
||||
|
||||
virtual bool isValid() = 0;
|
||||
|
||||
virtual void resize(int w, int h) = 0;
|
||||
|
||||
virtual void activate(bool isActive) {}
|
||||
|
||||
const DisplayParams* getDisplayParams() { return fDisplayParams.get(); }
|
||||
virtual void setDisplayParams(std::unique_ptr<const DisplayParams>) = 0;
|
||||
|
||||
GrDirectContext* directContext() const { return fContext.get(); }
|
||||
#if defined(SK_GRAPHITE)
|
||||
skgpu::graphite::Context* graphiteContext() const { return fGraphiteContext.get(); }
|
||||
skgpu::graphite::Recorder* graphiteRecorder() const { return fGraphiteRecorder.get(); }
|
||||
#endif
|
||||
|
||||
using GpuTimerCallback = std::function<void(uint64_t ns)>;
|
||||
void submitToGpu(GpuTimerCallback = {});
|
||||
bool supportsGpuTimer() const;
|
||||
|
||||
int width() const { return fWidth; }
|
||||
int height() const { return fHeight; }
|
||||
SkISize dimensions() const { return {fWidth, fHeight}; }
|
||||
int sampleCount() const { return fSampleCount; }
|
||||
int stencilBits() const { return fStencilBits; }
|
||||
|
||||
sk_sp<GrDirectContext> fContext;
|
||||
protected:
|
||||
virtual bool isGpuContext() { return true; }
|
||||
|
||||
virtual void onSwapBuffers() = 0;
|
||||
|
||||
#if defined(SK_GRAPHITE)
|
||||
std::unique_ptr<skgpu::graphite::Context> fGraphiteContext;
|
||||
std::unique_ptr<skgpu::graphite::Recorder> fGraphiteRecorder;
|
||||
#endif
|
||||
|
||||
int fWidth;
|
||||
int fHeight;
|
||||
std::unique_ptr<const DisplayParams> fDisplayParams;
|
||||
|
||||
// parameters obtained from the native window
|
||||
// Note that the platform .cpp file is responsible for
|
||||
// initializing fSampleCount and fStencilBits!
|
||||
int fSampleCount = 1;
|
||||
int fStencilBits = 0;
|
||||
};
|
||||
|
||||
} // namespace skwindow
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue