008b9d80ab
Adds a bitfield to GrContextOptions that masks out path renderers. Adds commandline flags support to set this bitfield in tools apps. Removes GrGLInterfaceRemoveNVPR since we can now accomplish the same thing in the context options. BUG=skia: Change-Id: Icf2a4df36374b3ba2f69ebf0db56e8aedd6cf65f Reviewed-on: https://skia-review.googlesource.com/8786 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
616 lines
26 KiB
C++
616 lines
26 KiB
C++
|
|
/*
|
|
* Copyright 2015 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "GrContext.h"
|
|
#include "GrRenderTarget.h"
|
|
#include "SkCommonFlagsPathRenderer.h"
|
|
#include "SkAutoMalloc.h"
|
|
#include "SkSurface.h"
|
|
#include "VulkanWindowContext.h"
|
|
|
|
#include "vk/GrVkInterface.h"
|
|
#include "vk/GrVkMemory.h"
|
|
#include "vk/GrVkUtil.h"
|
|
#include "vk/GrVkTypes.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) vkGetInstanceProcAddr(instance, "vk" #F)
|
|
#define GET_DEV_PROC(F) f ## F = (PFN_vk ## F) vkGetDeviceProcAddr(device, "vk" #F)
|
|
|
|
namespace sk_app {
|
|
|
|
VulkanWindowContext::VulkanWindowContext(const DisplayParams& params,
|
|
CreateVkSurfaceFn createVkSurface,
|
|
CanPresentFn canPresent)
|
|
: WindowContext()
|
|
, fSurface(VK_NULL_HANDLE)
|
|
, fSwapchain(VK_NULL_HANDLE)
|
|
, fImages(nullptr)
|
|
, fImageLayouts(nullptr)
|
|
, fSurfaces(nullptr)
|
|
, fCommandPool(VK_NULL_HANDLE)
|
|
, fBackbuffers(nullptr) {
|
|
|
|
// any config code here (particularly for msaa)?
|
|
fBackendContext.reset(GrVkBackendContext::Create(&fPresentQueueIndex, canPresent));
|
|
|
|
if (!(fBackendContext->fExtensions & kKHR_surface_GrVkExtensionFlag) ||
|
|
!(fBackendContext->fExtensions & kKHR_swapchain_GrVkExtensionFlag)) {
|
|
fBackendContext.reset(nullptr);
|
|
return;
|
|
}
|
|
|
|
VkInstance instance = fBackendContext->fInstance;
|
|
VkDevice device = fBackendContext->fDevice;
|
|
GET_PROC(DestroySurfaceKHR);
|
|
GET_PROC(GetPhysicalDeviceSurfaceSupportKHR);
|
|
GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR);
|
|
GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR);
|
|
GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR);
|
|
GET_DEV_PROC(CreateSwapchainKHR);
|
|
GET_DEV_PROC(DestroySwapchainKHR);
|
|
GET_DEV_PROC(GetSwapchainImagesKHR);
|
|
GET_DEV_PROC(AcquireNextImageKHR);
|
|
GET_DEV_PROC(QueuePresentKHR);
|
|
|
|
GrContextOptions ctxOptions;
|
|
ctxOptions.fGpuPathRenderers = CollectGpuPathRenderersFromFlags();
|
|
fContext = GrContext::Create(kVulkan_GrBackend, (GrBackendContext) fBackendContext.get(),
|
|
ctxOptions);
|
|
|
|
fSurface = createVkSurface(instance);
|
|
if (VK_NULL_HANDLE == fSurface) {
|
|
fBackendContext.reset(nullptr);
|
|
return;
|
|
}
|
|
|
|
VkBool32 supported;
|
|
VkResult res = fGetPhysicalDeviceSurfaceSupportKHR(fBackendContext->fPhysicalDevice,
|
|
fPresentQueueIndex, fSurface,
|
|
&supported);
|
|
if (VK_SUCCESS != res) {
|
|
this->destroyContext();
|
|
return;
|
|
}
|
|
|
|
if (!this->createSwapchain(-1, -1, params)) {
|
|
this->destroyContext();
|
|
return;
|
|
}
|
|
|
|
// create presentQueue
|
|
vkGetDeviceQueue(fBackendContext->fDevice, fPresentQueueIndex, 0, &fPresentQueue);
|
|
}
|
|
|
|
bool VulkanWindowContext::createSwapchain(int width, int height,
|
|
const DisplayParams& params) {
|
|
// check for capabilities
|
|
VkSurfaceCapabilitiesKHR caps;
|
|
VkResult res = fGetPhysicalDeviceSurfaceCapabilitiesKHR(fBackendContext->fPhysicalDevice,
|
|
fSurface, &caps);
|
|
if (VK_SUCCESS != res) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t surfaceFormatCount;
|
|
res = fGetPhysicalDeviceSurfaceFormatsKHR(fBackendContext->fPhysicalDevice, fSurface,
|
|
&surfaceFormatCount, nullptr);
|
|
if (VK_SUCCESS != res) {
|
|
return false;
|
|
}
|
|
|
|
SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR));
|
|
VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get();
|
|
res = fGetPhysicalDeviceSurfaceFormatsKHR(fBackendContext->fPhysicalDevice, fSurface,
|
|
&surfaceFormatCount, surfaceFormats);
|
|
if (VK_SUCCESS != res) {
|
|
return false;
|
|
}
|
|
|
|
uint32_t presentModeCount;
|
|
res = fGetPhysicalDeviceSurfacePresentModesKHR(fBackendContext->fPhysicalDevice, fSurface,
|
|
&presentModeCount, nullptr);
|
|
if (VK_SUCCESS != res) {
|
|
return false;
|
|
}
|
|
|
|
SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR));
|
|
VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get();
|
|
res = fGetPhysicalDeviceSurfacePresentModesKHR(fBackendContext->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);
|
|
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. For now, just make sure it matches our sRGB request:
|
|
VkFormat surfaceFormat = VK_FORMAT_UNDEFINED;
|
|
VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
|
|
auto srgbColorSpace = SkColorSpace::MakeSRGB();
|
|
bool wantSRGB = srgbColorSpace == params.fColorSpace;
|
|
for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
|
|
GrPixelConfig config;
|
|
if (GrVkFormatToPixelConfig(surfaceFormats[i].format, &config) &&
|
|
GrPixelConfigIsSRGB(config) == wantSRGB) {
|
|
surfaceFormat = surfaceFormats[i].format;
|
|
colorSpace = surfaceFormats[i].colorSpace;
|
|
break;
|
|
}
|
|
}
|
|
fDisplayParams = params;
|
|
|
|
if (VK_FORMAT_UNDEFINED == surfaceFormat) {
|
|
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;
|
|
for (uint32_t i = 0; i < presentModeCount; ++i) {
|
|
// use mailbox
|
|
if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) {
|
|
mode = presentModes[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
VkSwapchainCreateInfoKHR swapchainCreateInfo;
|
|
memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR));
|
|
swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
|
swapchainCreateInfo.surface = fSurface;
|
|
swapchainCreateInfo.minImageCount = imageCount;
|
|
swapchainCreateInfo.imageFormat = surfaceFormat;
|
|
swapchainCreateInfo.imageColorSpace = colorSpace;
|
|
swapchainCreateInfo.imageExtent = extent;
|
|
swapchainCreateInfo.imageArrayLayers = 1;
|
|
swapchainCreateInfo.imageUsage = usageFlags;
|
|
|
|
uint32_t queueFamilies[] = { fBackendContext->fGraphicsQueueIndex, fPresentQueueIndex };
|
|
if (fBackendContext->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(fBackendContext->fDevice, &swapchainCreateInfo, nullptr, &fSwapchain);
|
|
if (VK_SUCCESS != res) {
|
|
return false;
|
|
}
|
|
|
|
// destroy the old swapchain
|
|
if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
|
|
GR_VK_CALL(fBackendContext->fInterface, DeviceWaitIdle(fBackendContext->fDevice));
|
|
|
|
this->destroyBuffers();
|
|
|
|
fDestroySwapchainKHR(fBackendContext->fDevice, swapchainCreateInfo.oldSwapchain, nullptr);
|
|
}
|
|
|
|
this->createBuffers(swapchainCreateInfo.imageFormat);
|
|
|
|
return true;
|
|
}
|
|
|
|
void VulkanWindowContext::createBuffers(VkFormat format) {
|
|
GrVkFormatToPixelConfig(format, &fPixelConfig);
|
|
|
|
fGetSwapchainImagesKHR(fBackendContext->fDevice, fSwapchain, &fImageCount, nullptr);
|
|
SkASSERT(fImageCount);
|
|
fImages = new VkImage[fImageCount];
|
|
fGetSwapchainImagesKHR(fBackendContext->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;
|
|
|
|
GrBackendRenderTargetDesc desc;
|
|
GrVkImageInfo info;
|
|
info.fImage = fImages[i];
|
|
info.fAlloc = { VK_NULL_HANDLE, 0, 0, 0 };
|
|
info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
|
|
info.fFormat = format;
|
|
info.fLevelCount = 1;
|
|
desc.fWidth = fWidth;
|
|
desc.fHeight = fHeight;
|
|
desc.fConfig = fPixelConfig;
|
|
desc.fOrigin = kTopLeft_GrSurfaceOrigin;
|
|
desc.fSampleCnt = 0;
|
|
desc.fStencilBits = 0;
|
|
desc.fRenderTargetHandle = (GrBackendObject) &info;
|
|
|
|
fSurfaces[i] = SkSurface::MakeFromBackendRenderTarget(fContext, desc,
|
|
fDisplayParams.fColorSpace,
|
|
&fSurfaceProps);
|
|
}
|
|
|
|
// create the command pool for the command buffers
|
|
if (VK_NULL_HANDLE == fCommandPool) {
|
|
VkCommandPoolCreateInfo commandPoolInfo;
|
|
memset(&commandPoolInfo, 0, sizeof(VkCommandPoolCreateInfo));
|
|
commandPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
// this needs to be on the render queue
|
|
commandPoolInfo.queueFamilyIndex = fBackendContext->fGraphicsQueueIndex;
|
|
commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
CreateCommandPool(fBackendContext->fDevice, &commandPoolInfo,
|
|
nullptr, &fCommandPool));
|
|
}
|
|
|
|
// 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;
|
|
VkCommandBufferAllocateInfo commandBuffersInfo;
|
|
memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo));
|
|
commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
commandBuffersInfo.pNext = nullptr;
|
|
commandBuffersInfo.commandPool = fCommandPool;
|
|
commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
commandBuffersInfo.commandBufferCount = 2;
|
|
VkFenceCreateInfo fenceInfo;
|
|
memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo));
|
|
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
fenceInfo.pNext = nullptr;
|
|
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
|
|
|
// 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;
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
CreateSemaphore(fBackendContext->fDevice, &semaphoreInfo,
|
|
nullptr, &fBackbuffers[i].fAcquireSemaphore));
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
CreateSemaphore(fBackendContext->fDevice, &semaphoreInfo,
|
|
nullptr, &fBackbuffers[i].fRenderSemaphore));
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
AllocateCommandBuffers(fBackendContext->fDevice, &commandBuffersInfo,
|
|
fBackbuffers[i].fTransitionCmdBuffers));
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
CreateFence(fBackendContext->fDevice, &fenceInfo, nullptr,
|
|
&fBackbuffers[i].fUsageFences[0]));
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
CreateFence(fBackendContext->fDevice, &fenceInfo, nullptr,
|
|
&fBackbuffers[i].fUsageFences[1]));
|
|
}
|
|
fCurrentBackbufferIndex = fImageCount;
|
|
}
|
|
|
|
void VulkanWindowContext::destroyBuffers() {
|
|
|
|
if (fBackbuffers) {
|
|
for (uint32_t i = 0; i < fImageCount + 1; ++i) {
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
WaitForFences(fBackendContext->fDevice, 2,
|
|
fBackbuffers[i].fUsageFences,
|
|
true, UINT64_MAX));
|
|
fBackbuffers[i].fImageIndex = -1;
|
|
GR_VK_CALL(fBackendContext->fInterface,
|
|
DestroySemaphore(fBackendContext->fDevice,
|
|
fBackbuffers[i].fAcquireSemaphore,
|
|
nullptr));
|
|
GR_VK_CALL(fBackendContext->fInterface,
|
|
DestroySemaphore(fBackendContext->fDevice,
|
|
fBackbuffers[i].fRenderSemaphore,
|
|
nullptr));
|
|
GR_VK_CALL(fBackendContext->fInterface,
|
|
FreeCommandBuffers(fBackendContext->fDevice, fCommandPool, 2,
|
|
fBackbuffers[i].fTransitionCmdBuffers));
|
|
GR_VK_CALL(fBackendContext->fInterface,
|
|
DestroyFence(fBackendContext->fDevice, fBackbuffers[i].fUsageFences[0], 0));
|
|
GR_VK_CALL(fBackendContext->fInterface,
|
|
DestroyFence(fBackendContext->fDevice, fBackbuffers[i].fUsageFences[1], 0));
|
|
}
|
|
}
|
|
|
|
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 (!fBackendContext.get()) {
|
|
return;
|
|
}
|
|
|
|
GR_VK_CALL(fBackendContext->fInterface, QueueWaitIdle(fPresentQueue));
|
|
GR_VK_CALL(fBackendContext->fInterface, DeviceWaitIdle(fBackendContext->fDevice));
|
|
|
|
this->destroyBuffers();
|
|
|
|
if (VK_NULL_HANDLE != fCommandPool) {
|
|
GR_VK_CALL(fBackendContext->fInterface, DestroyCommandPool(fBackendContext->fDevice,
|
|
fCommandPool, nullptr));
|
|
fCommandPool = VK_NULL_HANDLE;
|
|
}
|
|
|
|
if (VK_NULL_HANDLE != fSwapchain) {
|
|
fDestroySwapchainKHR(fBackendContext->fDevice, fSwapchain, nullptr);
|
|
fSwapchain = VK_NULL_HANDLE;
|
|
}
|
|
|
|
if (VK_NULL_HANDLE != fSurface) {
|
|
fDestroySurfaceKHR(fBackendContext->fInstance, fSurface, nullptr);
|
|
fSurface = VK_NULL_HANDLE;
|
|
}
|
|
|
|
fContext->unref();
|
|
|
|
fBackendContext.reset(nullptr);
|
|
}
|
|
|
|
VulkanWindowContext::BackbufferInfo* VulkanWindowContext::getAvailableBackbuffer() {
|
|
SkASSERT(fBackbuffers);
|
|
|
|
++fCurrentBackbufferIndex;
|
|
if (fCurrentBackbufferIndex > fImageCount) {
|
|
fCurrentBackbufferIndex = 0;
|
|
}
|
|
|
|
BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
WaitForFences(fBackendContext->fDevice, 2, backbuffer->fUsageFences,
|
|
true, UINT64_MAX));
|
|
return backbuffer;
|
|
}
|
|
|
|
sk_sp<SkSurface> VulkanWindowContext::getBackbufferSurface() {
|
|
BackbufferInfo* backbuffer = this->getAvailableBackbuffer();
|
|
SkASSERT(backbuffer);
|
|
|
|
// reset the fence
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
ResetFences(fBackendContext->fDevice, 2, backbuffer->fUsageFences));
|
|
// semaphores should be in unsignaled state
|
|
|
|
// acquire the image
|
|
VkResult res = fAcquireNextImageKHR(fBackendContext->fDevice, fSwapchain, UINT64_MAX,
|
|
backbuffer->fAcquireSemaphore, 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
|
|
return nullptr;
|
|
}
|
|
if (VK_ERROR_OUT_OF_DATE_KHR == res) {
|
|
// tear swapchain down and try again
|
|
if (!this->createSwapchain(-1, -1, fDisplayParams)) {
|
|
return nullptr;
|
|
}
|
|
backbuffer = this->getAvailableBackbuffer();
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
ResetFences(fBackendContext->fDevice, 2, backbuffer->fUsageFences));
|
|
|
|
// acquire the image
|
|
res = fAcquireNextImageKHR(fBackendContext->fDevice, fSwapchain, UINT64_MAX,
|
|
backbuffer->fAcquireSemaphore, VK_NULL_HANDLE,
|
|
&backbuffer->fImageIndex);
|
|
|
|
if (VK_SUCCESS != res) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// set up layout transfer from initial to color attachment
|
|
VkImageLayout layout = fImageLayouts[backbuffer->fImageIndex];
|
|
SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout || VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout);
|
|
VkPipelineStageFlags srcStageMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ?
|
|
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT :
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
VkAccessFlags srcAccessMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ?
|
|
0 : VK_ACCESS_MEMORY_READ_BIT;
|
|
VkAccessFlags dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
|
|
VkImageMemoryBarrier imageMemoryBarrier = {
|
|
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
|
|
NULL, // pNext
|
|
srcAccessMask, // outputMask
|
|
dstAccessMask, // inputMask
|
|
layout, // oldLayout
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout
|
|
fPresentQueueIndex, // srcQueueFamilyIndex
|
|
fBackendContext->fGraphicsQueueIndex, // dstQueueFamilyIndex
|
|
fImages[backbuffer->fImageIndex], // image
|
|
{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } // subresourceRange
|
|
};
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
ResetCommandBuffer(backbuffer->fTransitionCmdBuffers[0], 0));
|
|
VkCommandBufferBeginInfo info;
|
|
memset(&info, 0, sizeof(VkCommandBufferBeginInfo));
|
|
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
info.flags = 0;
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
BeginCommandBuffer(backbuffer->fTransitionCmdBuffers[0], &info));
|
|
|
|
GR_VK_CALL(fBackendContext->fInterface,
|
|
CmdPipelineBarrier(backbuffer->fTransitionCmdBuffers[0],
|
|
srcStageMask, dstStageMask, 0,
|
|
0, nullptr,
|
|
0, nullptr,
|
|
1, &imageMemoryBarrier));
|
|
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
EndCommandBuffer(backbuffer->fTransitionCmdBuffers[0]));
|
|
|
|
VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
// insert the layout transfer into the queue and wait on the acquire
|
|
VkSubmitInfo submitInfo;
|
|
memset(&submitInfo, 0, sizeof(VkSubmitInfo));
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submitInfo.waitSemaphoreCount = 1;
|
|
submitInfo.pWaitSemaphores = &backbuffer->fAcquireSemaphore;
|
|
submitInfo.pWaitDstStageMask = &waitDstStageFlags;
|
|
submitInfo.commandBufferCount = 1;
|
|
submitInfo.pCommandBuffers = &backbuffer->fTransitionCmdBuffers[0];
|
|
submitInfo.signalSemaphoreCount = 0;
|
|
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
QueueSubmit(fBackendContext->fQueue, 1, &submitInfo,
|
|
backbuffer->fUsageFences[0]));
|
|
|
|
GrVkImageInfo* imageInfo;
|
|
SkSurface* surface = fSurfaces[backbuffer->fImageIndex].get();
|
|
surface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
|
|
SkSurface::kFlushRead_BackendHandleAccess);
|
|
imageInfo->updateImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
|
|
|
return sk_ref_sp(surface);
|
|
}
|
|
|
|
void VulkanWindowContext::swapBuffers() {
|
|
|
|
BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
|
|
GrVkImageInfo* imageInfo;
|
|
SkSurface* surface = fSurfaces[backbuffer->fImageIndex].get();
|
|
surface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
|
|
SkSurface::kFlushRead_BackendHandleAccess);
|
|
// Check to make sure we never change the actually wrapped image
|
|
SkASSERT(imageInfo->fImage == fImages[backbuffer->fImageIndex]);
|
|
|
|
VkImageLayout layout = imageInfo->fImageLayout;
|
|
VkPipelineStageFlags srcStageMask = GrVkMemory::LayoutToPipelineStageFlags(layout);
|
|
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
|
VkAccessFlags srcAccessMask = GrVkMemory::LayoutToSrcAccessMask(layout);
|
|
VkAccessFlags dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
|
|
|
VkImageMemoryBarrier imageMemoryBarrier = {
|
|
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
|
|
NULL, // pNext
|
|
srcAccessMask, // outputMask
|
|
dstAccessMask, // inputMask
|
|
layout, // oldLayout
|
|
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // newLayout
|
|
fBackendContext->fGraphicsQueueIndex, // srcQueueFamilyIndex
|
|
fPresentQueueIndex, // dstQueueFamilyIndex
|
|
fImages[backbuffer->fImageIndex], // image
|
|
{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } // subresourceRange
|
|
};
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
ResetCommandBuffer(backbuffer->fTransitionCmdBuffers[1], 0));
|
|
VkCommandBufferBeginInfo info;
|
|
memset(&info, 0, sizeof(VkCommandBufferBeginInfo));
|
|
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
info.flags = 0;
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
BeginCommandBuffer(backbuffer->fTransitionCmdBuffers[1], &info));
|
|
GR_VK_CALL(fBackendContext->fInterface,
|
|
CmdPipelineBarrier(backbuffer->fTransitionCmdBuffers[1],
|
|
srcStageMask, dstStageMask, 0,
|
|
0, nullptr,
|
|
0, nullptr,
|
|
1, &imageMemoryBarrier));
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
EndCommandBuffer(backbuffer->fTransitionCmdBuffers[1]));
|
|
|
|
fImageLayouts[backbuffer->fImageIndex] = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
|
|
// insert the layout transfer into the queue and wait on the acquire
|
|
VkSubmitInfo submitInfo;
|
|
memset(&submitInfo, 0, sizeof(VkSubmitInfo));
|
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submitInfo.waitSemaphoreCount = 0;
|
|
submitInfo.pWaitDstStageMask = 0;
|
|
submitInfo.commandBufferCount = 1;
|
|
submitInfo.pCommandBuffers = &backbuffer->fTransitionCmdBuffers[1];
|
|
submitInfo.signalSemaphoreCount = 1;
|
|
submitInfo.pSignalSemaphores = &backbuffer->fRenderSemaphore;
|
|
|
|
GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
|
|
QueueSubmit(fBackendContext->fQueue, 1, &submitInfo,
|
|
backbuffer->fUsageFences[1]));
|
|
|
|
// Submit present operation to present queue
|
|
const VkPresentInfoKHR presentInfo =
|
|
{
|
|
VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, // sType
|
|
NULL, // pNext
|
|
1, // waitSemaphoreCount
|
|
&backbuffer->fRenderSemaphore, // pWaitSemaphores
|
|
1, // swapchainCount
|
|
&fSwapchain, // pSwapchains
|
|
&backbuffer->fImageIndex, // pImageIndices
|
|
NULL // pResults
|
|
};
|
|
|
|
fQueuePresentKHR(fPresentQueue, &presentInfo);
|
|
}
|
|
|
|
} //namespace sk_app
|