mesa/src/vulkan/wsi/wsi_common_win32.cpp
Boris Brezillon 2f462105fa vulkan/wsi: Hook-up DXGI swapchains and DComp
The CPU copy is horribly slow, so let's hook-up DXGI swapchains. Note
that we're still limited in term of features. For instance, we can't
support more than 2 images per swapchain because of the DXGI present
ordering constraint. We also have to do an extra copy, because DXGI
only allows rendering to a resource on the queue that the swapchain
was created against, but swapchains in Vulkan don't have a queue.

The swapchain is bound to the window using DirectComposition aka
DComp. The DComp infrastructure is set up in the surface, and is
transitioned from one swapchain to the next when the new swapchain
begins presenting.

Unlike Wayland and X, there's no requirement that the compositor has
to release a surface before you can start rendering against it. However,
since we're now supporting the non-sw path, we do need to prevent apps
from rendering to a resource *while* the blit is occurring. We do this
by blocking for a fence while acquiring an image.

Co-authored-by: Jesse Natalie <jenatali@microsoft.com>
Acked-by: Daniel Stone <daniels@collabora.com>
Acked-by: Jason Ekstrand <jason.ekstrand@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/16200>
2023-01-11 09:31:02 +00:00

936 lines
29 KiB
C++

/*
* Copyright © 2015 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "vk_format.h"
#include "vk_instance.h"
#include "vk_physical_device.h"
#include "vk_util.h"
#include "wsi_common_entrypoints.h"
#include "wsi_common_private.h"
#define D3D12_IGNORE_SDK_LAYERS
#include <dxgi1_4.h>
#include <directx/d3d12.h>
#include <dxguids/dxguids.h>
#include <dcomp.h>
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
#endif
struct wsi_win32;
struct wsi_win32 {
struct wsi_interface base;
struct wsi_device *wsi;
const VkAllocationCallbacks *alloc;
VkPhysicalDevice physical_device;
struct {
IDXGIFactory4 *factory;
IDCompositionDevice *dcomp;
} dxgi;
};
enum wsi_win32_image_state {
WSI_IMAGE_IDLE,
WSI_IMAGE_DRAWING,
WSI_IMAGE_QUEUED,
};
struct wsi_win32_image {
struct wsi_image base;
enum wsi_win32_image_state state;
struct wsi_win32_swapchain *chain;
struct {
ID3D12Resource *swapchain_res;
} dxgi;
struct {
HDC dc;
HBITMAP bmp;
int bmp_row_pitch;
void *ppvBits;
} sw;
};
struct wsi_win32_surface {
VkIcdSurfaceWin32 base;
/* The first time a swapchain is created against this surface, a DComp
* target/visual will be created for it and that swapchain will be bound.
* When a new swapchain is created, we delay changing the visual's content
* until that swapchain has completed its first present once, otherwise the
* window will flash white. When the currently-bound swapchain is destroyed,
* the visual's content is unset.
*/
IDCompositionTarget *target;
IDCompositionVisual *visual;
struct wsi_win32_swapchain *current_swapchain;
};
struct wsi_win32_swapchain {
struct wsi_swapchain base;
IDXGISwapChain3 *dxgi;
struct wsi_win32 *wsi;
wsi_win32_surface *surface;
uint64_t flip_sequence;
VkResult status;
VkExtent2D extent;
HWND wnd;
HDC chain_dc;
struct wsi_win32_image images[0];
};
VKAPI_ATTR VkBool32 VKAPI_CALL
wsi_GetPhysicalDeviceWin32PresentationSupportKHR(VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex)
{
return TRUE;
}
VKAPI_ATTR VkResult VKAPI_CALL
wsi_CreateWin32SurfaceKHR(VkInstance _instance,
const VkWin32SurfaceCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks *pAllocator,
VkSurfaceKHR *pSurface)
{
VK_FROM_HANDLE(vk_instance, instance, _instance);
wsi_win32_surface *surface;
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR);
surface = (wsi_win32_surface *)vk_zalloc2(&instance->alloc, pAllocator, sizeof(*surface), 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (surface == NULL)
return VK_ERROR_OUT_OF_HOST_MEMORY;
surface->base.base.platform = VK_ICD_WSI_PLATFORM_WIN32;
surface->base.hinstance = pCreateInfo->hinstance;
surface->base.hwnd = pCreateInfo->hwnd;
*pSurface = VkIcdSurfaceBase_to_handle(&surface->base.base);
return VK_SUCCESS;
}
void
wsi_win32_surface_destroy(VkIcdSurfaceBase *icd_surface, VkInstance _instance,
const VkAllocationCallbacks *pAllocator)
{
VK_FROM_HANDLE(vk_instance, instance, _instance);
wsi_win32_surface *surface = (wsi_win32_surface *)icd_surface;
if (surface->visual)
surface->visual->Release();
if (surface->target)
surface->target->Release();
vk_free2(&instance->alloc, pAllocator, icd_surface);
}
static VkResult
wsi_win32_surface_get_support(VkIcdSurfaceBase *surface,
struct wsi_device *wsi_device,
uint32_t queueFamilyIndex,
VkBool32* pSupported)
{
*pSupported = true;
return VK_SUCCESS;
}
static VkResult
wsi_win32_surface_get_capabilities(VkIcdSurfaceBase *surf,
struct wsi_device *wsi_device,
VkSurfaceCapabilitiesKHR* caps)
{
VkIcdSurfaceWin32 *surface = (VkIcdSurfaceWin32 *)surf;
RECT win_rect;
if (!GetClientRect(surface->hwnd, &win_rect))
return VK_ERROR_SURFACE_LOST_KHR;
caps->minImageCount = 1;
if (!wsi_device->sw && wsi_device->win32.get_d3d12_command_queue) {
/* DXGI doesn't support random presenting order (images need to
* be presented in the order they were acquired), so we can't
* expose more than two image per swapchain.
*/
caps->minImageCount = caps->maxImageCount = 2;
} else {
caps->minImageCount = 1;
/* Software callbacke, there is no real maximum */
caps->maxImageCount = 0;
}
caps->currentExtent = {
(uint32_t)win_rect.right - (uint32_t)win_rect.left,
(uint32_t)win_rect.bottom - (uint32_t)win_rect.top
};
caps->minImageExtent = { 1u, 1u };
caps->maxImageExtent = {
wsi_device->maxImageDimension2D,
wsi_device->maxImageDimension2D,
};
caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
caps->maxImageArrayLayers = 1;
caps->supportedCompositeAlpha =
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
caps->supportedUsageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_STORAGE_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
return VK_SUCCESS;
}
static VkResult
wsi_win32_surface_get_capabilities2(VkIcdSurfaceBase *surface,
struct wsi_device *wsi_device,
const void *info_next,
VkSurfaceCapabilities2KHR* caps)
{
assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR);
VkResult result =
wsi_win32_surface_get_capabilities(surface, wsi_device,
&caps->surfaceCapabilities);
vk_foreach_struct(ext, caps->pNext) {
switch (ext->sType) {
case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: {
VkSurfaceProtectedCapabilitiesKHR *protected_cap = (VkSurfaceProtectedCapabilitiesKHR *)ext;
protected_cap->supportsProtected = VK_FALSE;
break;
}
default:
/* Ignored */
break;
}
}
return result;
}
static const struct {
VkFormat format;
} available_surface_formats[] = {
{ VK_FORMAT_B8G8R8A8_SRGB },
{ VK_FORMAT_B8G8R8A8_UNORM },
};
static void
get_sorted_vk_formats(struct wsi_device *wsi_device, VkFormat *sorted_formats)
{
for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++)
sorted_formats[i] = available_surface_formats[i].format;
if (wsi_device->force_bgra8_unorm_first) {
for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
if (sorted_formats[i] == VK_FORMAT_B8G8R8A8_UNORM) {
sorted_formats[i] = sorted_formats[0];
sorted_formats[0] = VK_FORMAT_B8G8R8A8_UNORM;
break;
}
}
}
}
static VkResult
wsi_win32_surface_get_formats(VkIcdSurfaceBase *icd_surface,
struct wsi_device *wsi_device,
uint32_t* pSurfaceFormatCount,
VkSurfaceFormatKHR* pSurfaceFormats)
{
VK_OUTARRAY_MAKE_TYPED(VkSurfaceFormatKHR, out, pSurfaceFormats, pSurfaceFormatCount);
VkFormat sorted_formats[ARRAY_SIZE(available_surface_formats)];
get_sorted_vk_formats(wsi_device, sorted_formats);
for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) {
vk_outarray_append_typed(VkSurfaceFormatKHR, &out, f) {
f->format = sorted_formats[i];
f->colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
}
}
return vk_outarray_status(&out);
}
static VkResult
wsi_win32_surface_get_formats2(VkIcdSurfaceBase *icd_surface,
struct wsi_device *wsi_device,
const void *info_next,
uint32_t* pSurfaceFormatCount,
VkSurfaceFormat2KHR* pSurfaceFormats)
{
VK_OUTARRAY_MAKE_TYPED(VkSurfaceFormat2KHR, out, pSurfaceFormats, pSurfaceFormatCount);
VkFormat sorted_formats[ARRAY_SIZE(available_surface_formats)];
get_sorted_vk_formats(wsi_device, sorted_formats);
for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) {
vk_outarray_append_typed(VkSurfaceFormat2KHR, &out, f) {
assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR);
f->surfaceFormat.format = sorted_formats[i];
f->surfaceFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
}
}
return vk_outarray_status(&out);
}
static const VkPresentModeKHR present_modes[] = {
//VK_PRESENT_MODE_MAILBOX_KHR,
VK_PRESENT_MODE_FIFO_KHR,
};
static VkResult
wsi_win32_surface_get_present_modes(VkIcdSurfaceBase *surface,
uint32_t* pPresentModeCount,
VkPresentModeKHR* pPresentModes)
{
if (pPresentModes == NULL) {
*pPresentModeCount = ARRAY_SIZE(present_modes);
return VK_SUCCESS;
}
*pPresentModeCount = MIN2(*pPresentModeCount, ARRAY_SIZE(present_modes));
typed_memcpy(pPresentModes, present_modes, *pPresentModeCount);
if (*pPresentModeCount < ARRAY_SIZE(present_modes))
return VK_INCOMPLETE;
else
return VK_SUCCESS;
}
static VkResult
wsi_win32_surface_get_present_rectangles(VkIcdSurfaceBase *surface,
struct wsi_device *wsi_device,
uint32_t* pRectCount,
VkRect2D* pRects)
{
VK_OUTARRAY_MAKE_TYPED(VkRect2D, out, pRects, pRectCount);
vk_outarray_append_typed(VkRect2D, &out, rect) {
/* We don't know a size so just return the usual "I don't know." */
*rect = {
{ 0, 0 },
{ UINT32_MAX, UINT32_MAX },
};
}
return vk_outarray_status(&out);
}
static VkResult
wsi_create_dxgi_image_mem(const struct wsi_swapchain *drv_chain,
const struct wsi_image_info *info,
struct wsi_image *image)
{
struct wsi_win32_swapchain *chain = (struct wsi_win32_swapchain *)drv_chain;
const struct wsi_device *wsi = chain->base.wsi;
assert(chain->base.blit.type != WSI_SWAPCHAIN_BUFFER_BLIT);
struct wsi_win32_image *win32_image =
container_of(image, struct wsi_win32_image, base);
uint32_t image_idx =
((uintptr_t)win32_image - (uintptr_t)chain->images) /
sizeof(*win32_image);
if (FAILED(chain->dxgi->GetBuffer(image_idx,
IID_PPV_ARGS(&win32_image->dxgi.swapchain_res))))
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
VkResult result =
wsi->win32.create_image_memory(chain->base.device,
win32_image->dxgi.swapchain_res,
&chain->base.alloc,
chain->base.blit.type == WSI_SWAPCHAIN_NO_BLIT ?
&image->memory : &image->blit.memory);
if (result != VK_SUCCESS)
return result;
if (chain->base.blit.type == WSI_SWAPCHAIN_NO_BLIT)
return VK_SUCCESS;
VkImageCreateInfo create = info->create;
create.usage &= ~VK_IMAGE_USAGE_STORAGE_BIT;
create.initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
result = wsi->CreateImage(chain->base.device, &create,
&chain->base.alloc, &image->blit.image);
if (result != VK_SUCCESS)
return result;
result = wsi->BindImageMemory(chain->base.device, image->blit.image,
image->blit.memory, 0);
if (result != VK_SUCCESS)
return result;
VkMemoryRequirements reqs;
wsi->GetImageMemoryRequirements(chain->base.device, image->image, &reqs);
const VkMemoryDedicatedAllocateInfo memory_dedicated_info = {
VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
nullptr,
image->blit.image,
VK_NULL_HANDLE,
};
const VkMemoryAllocateInfo memory_info = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
&memory_dedicated_info,
reqs.size,
info->select_image_memory_type(wsi, reqs.memoryTypeBits),
};
return wsi->AllocateMemory(chain->base.device, &memory_info,
&chain->base.alloc, &image->memory);
}
enum wsi_swapchain_blit_type
wsi_dxgi_image_needs_blit(const struct wsi_device *wsi,
const struct wsi_dxgi_image_params *params,
VkDevice device)
{
if (wsi->win32.requires_blits && wsi->win32.requires_blits(device))
return WSI_SWAPCHAIN_IMAGE_BLIT;
else if (params->storage_image)
return WSI_SWAPCHAIN_IMAGE_BLIT;
return WSI_SWAPCHAIN_NO_BLIT;
}
VkResult
wsi_dxgi_configure_image(const struct wsi_swapchain *chain,
const VkSwapchainCreateInfoKHR *pCreateInfo,
const struct wsi_dxgi_image_params *params,
struct wsi_image_info *info)
{
VkResult result =
wsi_configure_image(chain, pCreateInfo, 0, info);
if (result != VK_SUCCESS)
return result;
info->create_mem = wsi_create_dxgi_image_mem;
if (chain->blit.type != WSI_SWAPCHAIN_NO_BLIT) {
wsi_configure_image_blit_image(chain, info);
info->select_image_memory_type = wsi_select_device_memory_type;
info->select_blit_dst_memory_type = wsi_select_device_memory_type;
}
return VK_SUCCESS;
}
static VkResult
wsi_win32_image_init(VkDevice device_h,
struct wsi_win32_swapchain *chain,
const VkSwapchainCreateInfoKHR *create_info,
const VkAllocationCallbacks *allocator,
struct wsi_win32_image *image)
{
VkResult result = wsi_create_image(&chain->base, &chain->base.image_info,
&image->base);
if (result != VK_SUCCESS)
return result;
VkIcdSurfaceWin32 *win32_surface = (VkIcdSurfaceWin32 *)create_info->surface;
chain->wnd = win32_surface->hwnd;
chain->chain_dc = GetDC(chain->wnd);
image->chain = chain;
if (chain->base.blit.type != WSI_SWAPCHAIN_BUFFER_BLIT)
return VK_SUCCESS;
image->sw.dc = CreateCompatibleDC(chain->chain_dc);
HBITMAP bmp = NULL;
BITMAPINFO info = { 0 };
info.bmiHeader.biSize = sizeof(BITMAPINFO);
info.bmiHeader.biWidth = create_info->imageExtent.width;
info.bmiHeader.biHeight = -create_info->imageExtent.height;
info.bmiHeader.biPlanes = 1;
info.bmiHeader.biBitCount = 32;
info.bmiHeader.biCompression = BI_RGB;
bmp = CreateDIBSection(image->sw.dc, &info, DIB_RGB_COLORS, &image->sw.ppvBits, NULL, 0);
assert(bmp && image->sw.ppvBits);
SelectObject(image->sw.dc, bmp);
BITMAP header;
int status = GetObject(bmp, sizeof(BITMAP), &header);
(void)status;
image->sw.bmp_row_pitch = header.bmWidthBytes;
image->sw.bmp = bmp;
return VK_SUCCESS;
}
static void
wsi_win32_image_finish(struct wsi_win32_swapchain *chain,
const VkAllocationCallbacks *allocator,
struct wsi_win32_image *image)
{
if (image->dxgi.swapchain_res)
image->dxgi.swapchain_res->Release();
if (image->sw.dc)
DeleteDC(image->sw.dc);
if(image->sw.bmp)
DeleteObject(image->sw.bmp);
wsi_destroy_image(&chain->base, &image->base);
}
static VkResult
wsi_win32_swapchain_destroy(struct wsi_swapchain *drv_chain,
const VkAllocationCallbacks *allocator)
{
struct wsi_win32_swapchain *chain =
(struct wsi_win32_swapchain *) drv_chain;
for (uint32_t i = 0; i < chain->base.image_count; i++)
wsi_win32_image_finish(chain, allocator, &chain->images[i]);
DeleteDC(chain->chain_dc);
if (chain->surface->current_swapchain == chain)
chain->surface->current_swapchain = NULL;
if (chain->dxgi)
chain->dxgi->Release();
wsi_swapchain_finish(&chain->base);
vk_free(allocator, chain);
return VK_SUCCESS;
}
static struct wsi_image *
wsi_win32_get_wsi_image(struct wsi_swapchain *drv_chain,
uint32_t image_index)
{
struct wsi_win32_swapchain *chain =
(struct wsi_win32_swapchain *) drv_chain;
return &chain->images[image_index].base;
}
static VkResult
wsi_win32_acquire_next_image(struct wsi_swapchain *drv_chain,
const VkAcquireNextImageInfoKHR *info,
uint32_t *image_index)
{
struct wsi_win32_swapchain *chain =
(struct wsi_win32_swapchain *)drv_chain;
/* Bail early if the swapchain is broken */
if (chain->status != VK_SUCCESS)
return chain->status;
for (uint32_t i = 0; i < chain->base.image_count; i++) {
if (chain->images[i].state == WSI_IMAGE_IDLE) {
*image_index = i;
chain->images[i].state = WSI_IMAGE_DRAWING;
return VK_SUCCESS;
}
}
assert(chain->dxgi);
uint32_t index = chain->dxgi->GetCurrentBackBufferIndex();
if (chain->wsi->wsi->WaitForFences(chain->base.device, 1,
&chain->base.fences[index],
false, 2000) != VK_SUCCESS)
return VK_TIMEOUT;
*image_index = index;
chain->images[index].state = WSI_IMAGE_DRAWING;
return VK_SUCCESS;
}
static VkResult
wsi_win32_queue_present_dxgi(struct wsi_win32_swapchain *chain,
struct wsi_win32_image *image,
const VkPresentRegionKHR *damage)
{
uint32_t rect_count = damage ? damage->rectangleCount : 0;
STACK_ARRAY(RECT, rects, rect_count);
for (uint32_t r = 0; r < rect_count; r++) {
rects[r].left = damage->pRectangles[r].offset.x;
rects[r].top = damage->pRectangles[r].offset.y;
rects[r].right = damage->pRectangles[r].offset.x + damage->pRectangles[r].extent.width;
rects[r].bottom = damage->pRectangles[r].offset.y + damage->pRectangles[r].extent.height;
}
DXGI_PRESENT_PARAMETERS params = {
rect_count,
rects,
};
image->state = WSI_IMAGE_QUEUED;
HRESULT hres = chain->dxgi->Present1(0, 0, &params);
switch (hres) {
case DXGI_ERROR_DEVICE_REMOVED: return VK_ERROR_DEVICE_LOST;
case E_OUTOFMEMORY: return VK_ERROR_OUT_OF_DEVICE_MEMORY;
default:
if (FAILED(hres))
return VK_ERROR_OUT_OF_HOST_MEMORY;
break;
}
if (chain->surface->current_swapchain != chain) {
chain->surface->visual->SetContent(chain->dxgi);
chain->wsi->dxgi.dcomp->Commit();
chain->surface->current_swapchain = chain;
}
/* Mark the other image idle */
chain->status = VK_SUCCESS;
return VK_SUCCESS;
}
static VkResult
wsi_win32_queue_present(struct wsi_swapchain *drv_chain,
uint32_t image_index,
uint64_t present_id,
const VkPresentRegionKHR *damage)
{
struct wsi_win32_swapchain *chain = (struct wsi_win32_swapchain *) drv_chain;
assert(image_index < chain->base.image_count);
struct wsi_win32_image *image = &chain->images[image_index];
assert(image->state == WSI_IMAGE_DRAWING);
if (chain->dxgi)
return wsi_win32_queue_present_dxgi(chain, image, damage);
assert(chain->base.blit.type == WSI_SWAPCHAIN_BUFFER_BLIT);
char *ptr = (char *)image->base.cpu_map;
char *dptr = (char *)image->sw.ppvBits;
for (unsigned h = 0; h < chain->extent.height; h++) {
memcpy(dptr, ptr, chain->extent.width * 4);
dptr += image->sw.bmp_row_pitch;
ptr += image->base.row_pitches[0];
}
if (!StretchBlt(chain->chain_dc, 0, 0, chain->extent.width, chain->extent.height, image->sw.dc, 0, 0, chain->extent.width, chain->extent.height, SRCCOPY))
chain->status = VK_ERROR_MEMORY_MAP_FAILED;
image->state = WSI_IMAGE_IDLE;
return chain->status;
}
static VkResult
wsi_win32_surface_create_swapchain_dxgi(
wsi_win32_surface *surface,
VkDevice device,
struct wsi_win32 *wsi,
const VkSwapchainCreateInfoKHR *create_info,
struct wsi_win32_swapchain *chain)
{
IDXGIFactory4 *factory = wsi->dxgi.factory;
ID3D12CommandQueue *queue =
(ID3D12CommandQueue *)wsi->wsi->win32.get_d3d12_command_queue(device);
DXGI_SWAP_CHAIN_DESC1 desc = {
create_info->imageExtent.width,
create_info->imageExtent.height,
create_info->imageFormat == VK_FORMAT_B8G8R8A8_SRGB ?
DXGI_FORMAT_B8G8R8A8_UNORM_SRGB : DXGI_FORMAT_B8G8R8A8_UNORM,
create_info->imageArrayLayers > 1, // Stereo
{ 1 }, // SampleDesc
0, // Usage (filled in below)
create_info->minImageCount,
DXGI_SCALING_STRETCH,
DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
DXGI_ALPHA_MODE_UNSPECIFIED,
0, // Flags
};
if (create_info->imageUsage &
(VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))
desc.BufferUsage |= DXGI_USAGE_SHADER_INPUT;
if (create_info->imageUsage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
desc.BufferUsage |= DXGI_USAGE_RENDER_TARGET_OUTPUT;
IDXGISwapChain1 *swapchain1;
if (FAILED(factory->CreateSwapChainForComposition(queue, &desc, NULL, &swapchain1)) ||
FAILED(swapchain1->QueryInterface(&chain->dxgi)))
return VK_ERROR_INITIALIZATION_FAILED;
swapchain1->Release();
if (!surface->target &&
FAILED(wsi->dxgi.dcomp->CreateTargetForHwnd(surface->base.hwnd, false, &surface->target)))
return VK_ERROR_INITIALIZATION_FAILED;
if (!surface->visual) {
if (FAILED(wsi->dxgi.dcomp->CreateVisual(&surface->visual)) ||
FAILED(surface->target->SetRoot(surface->visual)) ||
FAILED(surface->visual->SetContent(chain->dxgi)) ||
FAILED(wsi->dxgi.dcomp->Commit()))
return VK_ERROR_INITIALIZATION_FAILED;
surface->current_swapchain = chain;
}
return VK_SUCCESS;
}
static VkResult
wsi_win32_surface_create_swapchain(
VkIcdSurfaceBase *icd_surface,
VkDevice device,
struct wsi_device *wsi_device,
const VkSwapchainCreateInfoKHR *create_info,
const VkAllocationCallbacks *allocator,
struct wsi_swapchain **swapchain_out)
{
wsi_win32_surface *surface = (wsi_win32_surface *)icd_surface;
struct wsi_win32 *wsi =
(struct wsi_win32 *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_WIN32];
assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
const unsigned num_images = create_info->minImageCount;
struct wsi_win32_swapchain *chain;
size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]);
chain = (wsi_win32_swapchain *)vk_zalloc(allocator, size,
8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (chain == NULL)
return VK_ERROR_OUT_OF_HOST_MEMORY;
struct wsi_dxgi_image_params dxgi_image_params = {
{ WSI_IMAGE_TYPE_DXGI },
};
dxgi_image_params.storage_image = (create_info->imageUsage & VK_IMAGE_USAGE_STORAGE_BIT) != 0;
struct wsi_cpu_image_params cpu_image_params = {
{ WSI_IMAGE_TYPE_CPU },
};
bool supports_dxgi = wsi->dxgi.factory &&
wsi->dxgi.dcomp &&
wsi->wsi->win32.get_d3d12_command_queue;
struct wsi_base_image_params *image_params = supports_dxgi ?
&dxgi_image_params.base : &cpu_image_params.base;
VkResult result = wsi_swapchain_init(wsi_device, &chain->base, device,
create_info, image_params,
allocator);
if (result != VK_SUCCESS) {
vk_free(allocator, chain);
return result;
}
chain->base.destroy = wsi_win32_swapchain_destroy;
chain->base.get_wsi_image = wsi_win32_get_wsi_image;
chain->base.acquire_next_image = wsi_win32_acquire_next_image;
chain->base.queue_present = wsi_win32_queue_present;
chain->base.present_mode = wsi_swapchain_get_present_mode(wsi_device, create_info);
chain->extent = create_info->imageExtent;
chain->wsi = wsi;
chain->status = VK_SUCCESS;
chain->surface = surface;
if (image_params->image_type == WSI_IMAGE_TYPE_DXGI) {
result = wsi_win32_surface_create_swapchain_dxgi(surface, device, wsi, create_info, chain);
if (result != VK_SUCCESS)
goto fail;
}
for (uint32_t image = 0; image < num_images; image++) {
result = wsi_win32_image_init(device, chain,
create_info, allocator,
&chain->images[image]);
if (result != VK_SUCCESS)
goto fail;
chain->base.image_count++;
}
*swapchain_out = &chain->base;
return VK_SUCCESS;
fail:
if (surface->visual) {
surface->visual->SetContent(NULL);
surface->current_swapchain = NULL;
wsi->dxgi.dcomp->Commit();
}
wsi_win32_swapchain_destroy(&chain->base, allocator);
return result;
}
static IDXGIFactory4 *
dxgi_get_factory(bool debug)
{
HMODULE dxgi_mod = LoadLibraryA("DXGI.DLL");
if (!dxgi_mod) {
return NULL;
}
typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY2)(UINT flags, REFIID riid, void **ppFactory);
PFN_CREATE_DXGI_FACTORY2 CreateDXGIFactory2;
CreateDXGIFactory2 = (PFN_CREATE_DXGI_FACTORY2)GetProcAddress(dxgi_mod, "CreateDXGIFactory2");
if (!CreateDXGIFactory2) {
return NULL;
}
UINT flags = 0;
if (debug)
flags |= DXGI_CREATE_FACTORY_DEBUG;
IDXGIFactory4 *factory;
HRESULT hr = CreateDXGIFactory2(flags, IID_PPV_ARGS(&factory));
if (FAILED(hr)) {
return NULL;
}
return factory;
}
static IDCompositionDevice *
dcomp_get_device()
{
HMODULE dcomp_mod = LoadLibraryA("DComp.DLL");
if (!dcomp_mod) {
return NULL;
}
typedef HRESULT (STDAPICALLTYPE *PFN_DCOMP_CREATE_DEVICE)(IDXGIDevice *, REFIID, void **);
PFN_DCOMP_CREATE_DEVICE DCompositionCreateDevice;
DCompositionCreateDevice = (PFN_DCOMP_CREATE_DEVICE)GetProcAddress(dcomp_mod, "DCompositionCreateDevice");
if (!DCompositionCreateDevice) {
return NULL;
}
IDCompositionDevice *device;
HRESULT hr = DCompositionCreateDevice(NULL, IID_PPV_ARGS(&device));
if (FAILED(hr)) {
return NULL;
}
return device;
}
VkResult
wsi_win32_init_wsi(struct wsi_device *wsi_device,
const VkAllocationCallbacks *alloc,
VkPhysicalDevice physical_device)
{
struct wsi_win32 *wsi;
VkResult result;
wsi = (wsi_win32 *)vk_zalloc(alloc, sizeof(*wsi), 8,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
if (!wsi) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail;
}
wsi->physical_device = physical_device;
wsi->alloc = alloc;
wsi->wsi = wsi_device;
if (!wsi_device->sw) {
wsi->dxgi.factory = dxgi_get_factory(WSI_DEBUG & WSI_DEBUG_DXGI);
if (!wsi->dxgi.factory) {
vk_free(alloc, wsi);
result = VK_ERROR_INITIALIZATION_FAILED;
goto fail;
}
wsi->dxgi.dcomp = dcomp_get_device();
if (!wsi->dxgi.dcomp) {
wsi->dxgi.factory->Release();
vk_free(alloc, wsi);
result = VK_ERROR_INITIALIZATION_FAILED;
goto fail;
}
}
wsi->base.get_support = wsi_win32_surface_get_support;
wsi->base.get_capabilities2 = wsi_win32_surface_get_capabilities2;
wsi->base.get_formats = wsi_win32_surface_get_formats;
wsi->base.get_formats2 = wsi_win32_surface_get_formats2;
wsi->base.get_present_modes = wsi_win32_surface_get_present_modes;
wsi->base.get_present_rectangles = wsi_win32_surface_get_present_rectangles;
wsi->base.create_swapchain = wsi_win32_surface_create_swapchain;
wsi_device->wsi[VK_ICD_WSI_PLATFORM_WIN32] = &wsi->base;
return VK_SUCCESS;
fail:
wsi_device->wsi[VK_ICD_WSI_PLATFORM_WIN32] = NULL;
return result;
}
void
wsi_win32_finish_wsi(struct wsi_device *wsi_device,
const VkAllocationCallbacks *alloc)
{
struct wsi_win32 *wsi =
(struct wsi_win32 *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_WIN32];
if (!wsi)
return;
if (wsi->dxgi.factory)
wsi->dxgi.factory->Release();
if (wsi->dxgi.dcomp)
wsi->dxgi.dcomp->Release();
vk_free(alloc, wsi);
}