1 /*
2 * Copyright © 2015 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include <assert.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28
29 #include "vk_format.h"
30 #include "vk_instance.h"
31 #include "vk_physical_device.h"
32 #include "vk_util.h"
33 #include "wsi_common_entrypoints.h"
34 #include "wsi_common_private.h"
35
36 #define D3D12_IGNORE_SDK_LAYERS
37 #include <dxgi1_4.h>
38 #include <directx/d3d12.h>
39 #include <dxguids/dxguids.h>
40
41 #include <dcomp.h>
42
43 #if defined(__GNUC__)
44 #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size
45 #endif
46
47 struct wsi_win32;
48
49 struct wsi_win32 {
50 struct wsi_interface base;
51
52 struct wsi_device *wsi;
53
54 const VkAllocationCallbacks *alloc;
55 VkPhysicalDevice physical_device;
56 struct {
57 IDXGIFactory4 *factory;
58 IDCompositionDevice *dcomp;
59 } dxgi;
60 };
61
62 enum wsi_win32_image_state {
63 WSI_IMAGE_IDLE,
64 WSI_IMAGE_DRAWING,
65 WSI_IMAGE_QUEUED,
66 };
67
68 struct wsi_win32_image {
69 struct wsi_image base;
70 enum wsi_win32_image_state state;
71 struct wsi_win32_swapchain *chain;
72 struct {
73 ID3D12Resource *swapchain_res;
74 } dxgi;
75 struct {
76 HDC dc;
77 HBITMAP bmp;
78 int bmp_row_pitch;
79 void *ppvBits;
80 } sw;
81 };
82
83 struct wsi_win32_surface {
84 VkIcdSurfaceWin32 base;
85
86 /* The first time a swapchain is created against this surface, a DComp
87 * target/visual will be created for it and that swapchain will be bound.
88 * When a new swapchain is created, we delay changing the visual's content
89 * until that swapchain has completed its first present once, otherwise the
90 * window will flash white. When the currently-bound swapchain is destroyed,
91 * the visual's content is unset.
92 */
93 IDCompositionTarget *target;
94 IDCompositionVisual *visual;
95 struct wsi_win32_swapchain *current_swapchain;
96 };
97
98 struct wsi_win32_swapchain {
99 struct wsi_swapchain base;
100 IDXGISwapChain3 *dxgi;
101 struct wsi_win32 *wsi;
102 wsi_win32_surface *surface;
103 uint64_t flip_sequence;
104 VkResult status;
105 VkExtent2D extent;
106 HWND wnd;
107 HDC chain_dc;
108 struct wsi_win32_image images[0];
109 };
110
111 VKAPI_ATTR VkBool32 VKAPI_CALL
wsi_GetPhysicalDeviceWin32PresentationSupportKHR(VkPhysicalDevice physicalDevice,uint32_t queueFamilyIndex)112 wsi_GetPhysicalDeviceWin32PresentationSupportKHR(VkPhysicalDevice physicalDevice,
113 uint32_t queueFamilyIndex)
114 {
115 VK_FROM_HANDLE(vk_physical_device, pdevice, physicalDevice);
116 struct wsi_device *wsi_device = pdevice->wsi_device;
117 return (wsi_device->queue_supports_blit & BITFIELD64_BIT(queueFamilyIndex)) != 0;
118 }
119
120 VKAPI_ATTR VkResult VKAPI_CALL
wsi_CreateWin32SurfaceKHR(VkInstance _instance,const VkWin32SurfaceCreateInfoKHR * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkSurfaceKHR * pSurface)121 wsi_CreateWin32SurfaceKHR(VkInstance _instance,
122 const VkWin32SurfaceCreateInfoKHR *pCreateInfo,
123 const VkAllocationCallbacks *pAllocator,
124 VkSurfaceKHR *pSurface)
125 {
126 VK_FROM_HANDLE(vk_instance, instance, _instance);
127 wsi_win32_surface *surface;
128
129 assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR);
130
131 surface = (wsi_win32_surface *)vk_zalloc2(&instance->alloc, pAllocator, sizeof(*surface), 8,
132 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
133
134 if (surface == NULL)
135 return VK_ERROR_OUT_OF_HOST_MEMORY;
136
137 surface->base.base.platform = VK_ICD_WSI_PLATFORM_WIN32;
138
139 surface->base.hinstance = pCreateInfo->hinstance;
140 surface->base.hwnd = pCreateInfo->hwnd;
141
142 *pSurface = VkIcdSurfaceBase_to_handle(&surface->base.base);
143
144 return VK_SUCCESS;
145 }
146
147 void
wsi_win32_surface_destroy(VkIcdSurfaceBase * icd_surface,VkInstance _instance,const VkAllocationCallbacks * pAllocator)148 wsi_win32_surface_destroy(VkIcdSurfaceBase *icd_surface, VkInstance _instance,
149 const VkAllocationCallbacks *pAllocator)
150 {
151 VK_FROM_HANDLE(vk_instance, instance, _instance);
152 wsi_win32_surface *surface = (wsi_win32_surface *)icd_surface;
153 if (surface->visual)
154 surface->visual->Release();
155 if (surface->target)
156 surface->target->Release();
157 vk_free2(&instance->alloc, pAllocator, icd_surface);
158 }
159
160 static VkResult
wsi_win32_surface_get_support(VkIcdSurfaceBase * surface,struct wsi_device * wsi_device,uint32_t queueFamilyIndex,VkBool32 * pSupported)161 wsi_win32_surface_get_support(VkIcdSurfaceBase *surface,
162 struct wsi_device *wsi_device,
163 uint32_t queueFamilyIndex,
164 VkBool32* pSupported)
165 {
166 *pSupported = true;
167
168 return VK_SUCCESS;
169 }
170
171 static VkResult
wsi_win32_surface_get_capabilities(VkIcdSurfaceBase * surf,struct wsi_device * wsi_device,VkSurfaceCapabilitiesKHR * caps)172 wsi_win32_surface_get_capabilities(VkIcdSurfaceBase *surf,
173 struct wsi_device *wsi_device,
174 VkSurfaceCapabilitiesKHR* caps)
175 {
176 VkIcdSurfaceWin32 *surface = (VkIcdSurfaceWin32 *)surf;
177
178 RECT win_rect;
179 if (!GetClientRect(surface->hwnd, &win_rect))
180 return VK_ERROR_SURFACE_LOST_KHR;
181
182 caps->minImageCount = 1;
183
184 if (!wsi_device->sw && wsi_device->win32.get_d3d12_command_queue) {
185 /* DXGI doesn't support random presenting order (images need to
186 * be presented in the order they were acquired), so we can't
187 * expose more than two image per swapchain.
188 */
189 caps->minImageCount = caps->maxImageCount = 2;
190 } else {
191 caps->minImageCount = 1;
192 /* Software callbacke, there is no real maximum */
193 caps->maxImageCount = 0;
194 }
195
196 caps->currentExtent = {
197 (uint32_t)win_rect.right - (uint32_t)win_rect.left,
198 (uint32_t)win_rect.bottom - (uint32_t)win_rect.top
199 };
200 caps->minImageExtent = { 1u, 1u };
201 caps->maxImageExtent = {
202 wsi_device->maxImageDimension2D,
203 wsi_device->maxImageDimension2D,
204 };
205
206 caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
207 caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
208 caps->maxImageArrayLayers = 1;
209
210 caps->supportedCompositeAlpha =
211 VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
212 VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
213
214 caps->supportedUsageFlags = wsi_caps_get_image_usage();
215
216 VK_FROM_HANDLE(vk_physical_device, pdevice, wsi_device->pdevice);
217 if (pdevice->supported_extensions.EXT_attachment_feedback_loop_layout)
218 caps->supportedUsageFlags |= VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT;
219
220 return VK_SUCCESS;
221 }
222
223 static VkResult
wsi_win32_surface_get_capabilities2(VkIcdSurfaceBase * surface,struct wsi_device * wsi_device,const void * info_next,VkSurfaceCapabilities2KHR * caps)224 wsi_win32_surface_get_capabilities2(VkIcdSurfaceBase *surface,
225 struct wsi_device *wsi_device,
226 const void *info_next,
227 VkSurfaceCapabilities2KHR* caps)
228 {
229 assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR);
230
231 const VkSurfacePresentModeEXT *present_mode =
232 (const VkSurfacePresentModeEXT *)vk_find_struct_const(info_next, SURFACE_PRESENT_MODE_EXT);
233
234 VkResult result =
235 wsi_win32_surface_get_capabilities(surface, wsi_device,
236 &caps->surfaceCapabilities);
237
238 vk_foreach_struct(ext, caps->pNext) {
239 switch (ext->sType) {
240 case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: {
241 VkSurfaceProtectedCapabilitiesKHR *protected_cap = (VkSurfaceProtectedCapabilitiesKHR *)ext;
242 protected_cap->supportsProtected = VK_FALSE;
243 break;
244 }
245
246 case VK_STRUCTURE_TYPE_SURFACE_PRESENT_SCALING_CAPABILITIES_EXT: {
247 /* Unsupported. */
248 VkSurfacePresentScalingCapabilitiesEXT *scaling =
249 (VkSurfacePresentScalingCapabilitiesEXT *)ext;
250 scaling->supportedPresentScaling = 0;
251 scaling->supportedPresentGravityX = 0;
252 scaling->supportedPresentGravityY = 0;
253 scaling->minScaledImageExtent = caps->surfaceCapabilities.minImageExtent;
254 scaling->maxScaledImageExtent = caps->surfaceCapabilities.maxImageExtent;
255 break;
256 }
257
258 case VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_COMPATIBILITY_EXT: {
259 /* Unsupported, just report the input present mode. */
260 VkSurfacePresentModeCompatibilityEXT *compat =
261 (VkSurfacePresentModeCompatibilityEXT *)ext;
262 if (compat->pPresentModes) {
263 if (compat->presentModeCount) {
264 assert(present_mode);
265 compat->pPresentModes[0] = present_mode->presentMode;
266 compat->presentModeCount = 1;
267 }
268 } else {
269 if (!present_mode)
270 wsi_common_vk_warn_once("Use of VkSurfacePresentModeCompatibilityEXT "
271 "without a VkSurfacePresentModeEXT set. This is an "
272 "application bug.\n");
273 compat->presentModeCount = 1;
274 }
275 break;
276 }
277
278 default:
279 /* Ignored */
280 break;
281 }
282 }
283
284 return result;
285 }
286
287
288 static const struct {
289 VkFormat format;
290 } available_surface_formats[] = {
291 { VK_FORMAT_B8G8R8A8_SRGB },
292 { VK_FORMAT_B8G8R8A8_UNORM },
293 };
294
295
296 static void
get_sorted_vk_formats(struct wsi_device * wsi_device,VkFormat * sorted_formats)297 get_sorted_vk_formats(struct wsi_device *wsi_device, VkFormat *sorted_formats)
298 {
299 for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++)
300 sorted_formats[i] = available_surface_formats[i].format;
301
302 if (wsi_device->force_bgra8_unorm_first) {
303 for (unsigned i = 0; i < ARRAY_SIZE(available_surface_formats); i++) {
304 if (sorted_formats[i] == VK_FORMAT_B8G8R8A8_UNORM) {
305 sorted_formats[i] = sorted_formats[0];
306 sorted_formats[0] = VK_FORMAT_B8G8R8A8_UNORM;
307 break;
308 }
309 }
310 }
311 }
312
313 static VkResult
wsi_win32_surface_get_formats(VkIcdSurfaceBase * icd_surface,struct wsi_device * wsi_device,uint32_t * pSurfaceFormatCount,VkSurfaceFormatKHR * pSurfaceFormats)314 wsi_win32_surface_get_formats(VkIcdSurfaceBase *icd_surface,
315 struct wsi_device *wsi_device,
316 uint32_t* pSurfaceFormatCount,
317 VkSurfaceFormatKHR* pSurfaceFormats)
318 {
319 VK_OUTARRAY_MAKE_TYPED(VkSurfaceFormatKHR, out, pSurfaceFormats, pSurfaceFormatCount);
320
321 VkFormat sorted_formats[ARRAY_SIZE(available_surface_formats)];
322 get_sorted_vk_formats(wsi_device, sorted_formats);
323
324 for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) {
325 vk_outarray_append_typed(VkSurfaceFormatKHR, &out, f) {
326 f->format = sorted_formats[i];
327 f->colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
328 }
329 }
330
331 return vk_outarray_status(&out);
332 }
333
334 static VkResult
wsi_win32_surface_get_formats2(VkIcdSurfaceBase * icd_surface,struct wsi_device * wsi_device,const void * info_next,uint32_t * pSurfaceFormatCount,VkSurfaceFormat2KHR * pSurfaceFormats)335 wsi_win32_surface_get_formats2(VkIcdSurfaceBase *icd_surface,
336 struct wsi_device *wsi_device,
337 const void *info_next,
338 uint32_t* pSurfaceFormatCount,
339 VkSurfaceFormat2KHR* pSurfaceFormats)
340 {
341 VK_OUTARRAY_MAKE_TYPED(VkSurfaceFormat2KHR, out, pSurfaceFormats, pSurfaceFormatCount);
342
343 VkFormat sorted_formats[ARRAY_SIZE(available_surface_formats)];
344 get_sorted_vk_formats(wsi_device, sorted_formats);
345
346 for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) {
347 vk_outarray_append_typed(VkSurfaceFormat2KHR, &out, f) {
348 assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR);
349 f->surfaceFormat.format = sorted_formats[i];
350 f->surfaceFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
351 }
352 }
353
354 return vk_outarray_status(&out);
355 }
356
357 static const VkPresentModeKHR present_modes_gdi[] = {
358 VK_PRESENT_MODE_FIFO_KHR,
359 };
360 static const VkPresentModeKHR present_modes_dxgi[] = {
361 VK_PRESENT_MODE_IMMEDIATE_KHR,
362 VK_PRESENT_MODE_MAILBOX_KHR,
363 VK_PRESENT_MODE_FIFO_KHR,
364 };
365
366 static VkResult
wsi_win32_surface_get_present_modes(VkIcdSurfaceBase * surface,struct wsi_device * wsi_device,uint32_t * pPresentModeCount,VkPresentModeKHR * pPresentModes)367 wsi_win32_surface_get_present_modes(VkIcdSurfaceBase *surface,
368 struct wsi_device *wsi_device,
369 uint32_t* pPresentModeCount,
370 VkPresentModeKHR* pPresentModes)
371 {
372 const VkPresentModeKHR *array;
373 size_t array_size;
374 if (wsi_device->sw || !wsi_device->win32.get_d3d12_command_queue) {
375 array = present_modes_gdi;
376 array_size = ARRAY_SIZE(present_modes_gdi);
377 } else {
378 array = present_modes_dxgi;
379 array_size = ARRAY_SIZE(present_modes_dxgi);
380 }
381
382 if (pPresentModes == NULL) {
383 *pPresentModeCount = array_size;
384 return VK_SUCCESS;
385 }
386
387 *pPresentModeCount = MIN2(*pPresentModeCount, array_size);
388 typed_memcpy(pPresentModes, array, *pPresentModeCount);
389
390 if (*pPresentModeCount < array_size)
391 return VK_INCOMPLETE;
392 else
393 return VK_SUCCESS;
394 }
395
396 static VkResult
wsi_win32_surface_get_present_rectangles(VkIcdSurfaceBase * surface,struct wsi_device * wsi_device,uint32_t * pRectCount,VkRect2D * pRects)397 wsi_win32_surface_get_present_rectangles(VkIcdSurfaceBase *surface,
398 struct wsi_device *wsi_device,
399 uint32_t* pRectCount,
400 VkRect2D* pRects)
401 {
402 VK_OUTARRAY_MAKE_TYPED(VkRect2D, out, pRects, pRectCount);
403
404 vk_outarray_append_typed(VkRect2D, &out, rect) {
405 /* We don't know a size so just return the usual "I don't know." */
406 *rect = {
407 { 0, 0 },
408 { UINT32_MAX, UINT32_MAX },
409 };
410 }
411
412 return vk_outarray_status(&out);
413 }
414
415 static VkResult
wsi_create_dxgi_image_mem(const struct wsi_swapchain * drv_chain,const struct wsi_image_info * info,struct wsi_image * image)416 wsi_create_dxgi_image_mem(const struct wsi_swapchain *drv_chain,
417 const struct wsi_image_info *info,
418 struct wsi_image *image)
419 {
420 struct wsi_win32_swapchain *chain = (struct wsi_win32_swapchain *)drv_chain;
421 const struct wsi_device *wsi = chain->base.wsi;
422
423 assert(chain->base.blit.type != WSI_SWAPCHAIN_BUFFER_BLIT);
424
425 struct wsi_win32_image *win32_image =
426 container_of(image, struct wsi_win32_image, base);
427 uint32_t image_idx =
428 ((uintptr_t)win32_image - (uintptr_t)chain->images) /
429 sizeof(*win32_image);
430 if (FAILED(chain->dxgi->GetBuffer(image_idx,
431 IID_PPV_ARGS(&win32_image->dxgi.swapchain_res))))
432 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
433
434 VkResult result =
435 wsi->win32.create_image_memory(chain->base.device,
436 win32_image->dxgi.swapchain_res,
437 &chain->base.alloc,
438 chain->base.blit.type == WSI_SWAPCHAIN_NO_BLIT ?
439 &image->memory : &image->blit.memory);
440 if (result != VK_SUCCESS)
441 return result;
442
443 if (chain->base.blit.type == WSI_SWAPCHAIN_NO_BLIT)
444 return VK_SUCCESS;
445
446 VkImageCreateInfo create = info->create;
447
448 create.usage &= ~VK_IMAGE_USAGE_STORAGE_BIT;
449 create.initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
450
451 result = wsi->CreateImage(chain->base.device, &create,
452 &chain->base.alloc, &image->blit.image);
453 if (result != VK_SUCCESS)
454 return result;
455
456 result = wsi->BindImageMemory(chain->base.device, image->blit.image,
457 image->blit.memory, 0);
458 if (result != VK_SUCCESS)
459 return result;
460
461 VkMemoryRequirements reqs;
462 wsi->GetImageMemoryRequirements(chain->base.device, image->image, &reqs);
463
464 const VkMemoryDedicatedAllocateInfo memory_dedicated_info = {
465 VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO,
466 nullptr,
467 image->blit.image,
468 VK_NULL_HANDLE,
469 };
470 const VkMemoryAllocateInfo memory_info = {
471 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
472 &memory_dedicated_info,
473 reqs.size,
474 info->select_image_memory_type(wsi, reqs.memoryTypeBits),
475 };
476
477 return wsi->AllocateMemory(chain->base.device, &memory_info,
478 &chain->base.alloc, &image->memory);
479 }
480
481 enum wsi_swapchain_blit_type
wsi_dxgi_image_needs_blit(const struct wsi_device * wsi,const struct wsi_dxgi_image_params * params,VkDevice device)482 wsi_dxgi_image_needs_blit(const struct wsi_device *wsi,
483 const struct wsi_dxgi_image_params *params,
484 VkDevice device)
485 {
486 if (wsi->win32.requires_blits && wsi->win32.requires_blits(device))
487 return WSI_SWAPCHAIN_IMAGE_BLIT;
488 else if (params->storage_image)
489 return WSI_SWAPCHAIN_IMAGE_BLIT;
490 return WSI_SWAPCHAIN_NO_BLIT;
491 }
492
493 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)494 wsi_dxgi_configure_image(const struct wsi_swapchain *chain,
495 const VkSwapchainCreateInfoKHR *pCreateInfo,
496 const struct wsi_dxgi_image_params *params,
497 struct wsi_image_info *info)
498 {
499 VkResult result =
500 wsi_configure_image(chain, pCreateInfo, 0, info);
501 if (result != VK_SUCCESS)
502 return result;
503
504 info->create_mem = wsi_create_dxgi_image_mem;
505
506 if (chain->blit.type != WSI_SWAPCHAIN_NO_BLIT) {
507 wsi_configure_image_blit_image(chain, info);
508 info->select_image_memory_type = wsi_select_device_memory_type;
509 info->select_blit_dst_memory_type = wsi_select_device_memory_type;
510 }
511
512 return VK_SUCCESS;
513 }
514
515 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)516 wsi_win32_image_init(VkDevice device_h,
517 struct wsi_win32_swapchain *chain,
518 const VkSwapchainCreateInfoKHR *create_info,
519 const VkAllocationCallbacks *allocator,
520 struct wsi_win32_image *image)
521 {
522 VkResult result = wsi_create_image(&chain->base, &chain->base.image_info,
523 &image->base);
524 if (result != VK_SUCCESS)
525 return result;
526
527 VkIcdSurfaceWin32 *win32_surface = (VkIcdSurfaceWin32 *)create_info->surface;
528 chain->wnd = win32_surface->hwnd;
529 image->chain = chain;
530
531 if (chain->dxgi)
532 return VK_SUCCESS;
533
534 chain->chain_dc = GetDC(chain->wnd);
535 image->sw.dc = CreateCompatibleDC(chain->chain_dc);
536 HBITMAP bmp = NULL;
537
538 BITMAPINFO info = { 0 };
539 info.bmiHeader.biSize = sizeof(BITMAPINFO);
540 info.bmiHeader.biWidth = create_info->imageExtent.width;
541 info.bmiHeader.biHeight = -create_info->imageExtent.height;
542 info.bmiHeader.biPlanes = 1;
543 info.bmiHeader.biBitCount = 32;
544 info.bmiHeader.biCompression = BI_RGB;
545
546 bmp = CreateDIBSection(image->sw.dc, &info, DIB_RGB_COLORS, &image->sw.ppvBits, NULL, 0);
547 assert(bmp && image->sw.ppvBits);
548
549 SelectObject(image->sw.dc, bmp);
550
551 BITMAP header;
552 int status = GetObject(bmp, sizeof(BITMAP), &header);
553 (void)status;
554 image->sw.bmp_row_pitch = header.bmWidthBytes;
555 image->sw.bmp = bmp;
556
557 return VK_SUCCESS;
558 }
559
560 static void
wsi_win32_image_finish(struct wsi_win32_swapchain * chain,const VkAllocationCallbacks * allocator,struct wsi_win32_image * image)561 wsi_win32_image_finish(struct wsi_win32_swapchain *chain,
562 const VkAllocationCallbacks *allocator,
563 struct wsi_win32_image *image)
564 {
565 if (image->dxgi.swapchain_res)
566 image->dxgi.swapchain_res->Release();
567
568 if (image->sw.dc)
569 DeleteDC(image->sw.dc);
570 if(image->sw.bmp)
571 DeleteObject(image->sw.bmp);
572 wsi_destroy_image(&chain->base, &image->base);
573 }
574
575 static VkResult
wsi_win32_swapchain_destroy(struct wsi_swapchain * drv_chain,const VkAllocationCallbacks * allocator)576 wsi_win32_swapchain_destroy(struct wsi_swapchain *drv_chain,
577 const VkAllocationCallbacks *allocator)
578 {
579 struct wsi_win32_swapchain *chain =
580 (struct wsi_win32_swapchain *) drv_chain;
581
582 for (uint32_t i = 0; i < chain->base.image_count; i++)
583 wsi_win32_image_finish(chain, allocator, &chain->images[i]);
584
585 DeleteDC(chain->chain_dc);
586
587 if (chain->surface->current_swapchain == chain)
588 chain->surface->current_swapchain = NULL;
589
590 if (chain->dxgi)
591 chain->dxgi->Release();
592
593 wsi_swapchain_finish(&chain->base);
594 vk_free(allocator, chain);
595 return VK_SUCCESS;
596 }
597
598 static struct wsi_image *
wsi_win32_get_wsi_image(struct wsi_swapchain * drv_chain,uint32_t image_index)599 wsi_win32_get_wsi_image(struct wsi_swapchain *drv_chain,
600 uint32_t image_index)
601 {
602 struct wsi_win32_swapchain *chain =
603 (struct wsi_win32_swapchain *) drv_chain;
604
605 return &chain->images[image_index].base;
606 }
607
608 static VkResult
wsi_win32_release_images(struct wsi_swapchain * drv_chain,uint32_t count,const uint32_t * indices)609 wsi_win32_release_images(struct wsi_swapchain *drv_chain,
610 uint32_t count, const uint32_t *indices)
611 {
612 struct wsi_win32_swapchain *chain =
613 (struct wsi_win32_swapchain *)drv_chain;
614
615 if (chain->status == VK_ERROR_SURFACE_LOST_KHR)
616 return chain->status;
617
618 for (uint32_t i = 0; i < count; i++) {
619 uint32_t index = indices[i];
620 assert(index < chain->base.image_count);
621 assert(chain->images[index].state == WSI_IMAGE_DRAWING);
622 chain->images[index].state = WSI_IMAGE_IDLE;
623 }
624
625 return VK_SUCCESS;
626 }
627
628
629 static VkResult
wsi_win32_acquire_next_image(struct wsi_swapchain * drv_chain,const VkAcquireNextImageInfoKHR * info,uint32_t * image_index)630 wsi_win32_acquire_next_image(struct wsi_swapchain *drv_chain,
631 const VkAcquireNextImageInfoKHR *info,
632 uint32_t *image_index)
633 {
634 struct wsi_win32_swapchain *chain =
635 (struct wsi_win32_swapchain *)drv_chain;
636
637 /* Bail early if the swapchain is broken */
638 if (chain->status != VK_SUCCESS)
639 return chain->status;
640
641 for (uint32_t i = 0; i < chain->base.image_count; i++) {
642 if (chain->images[i].state == WSI_IMAGE_IDLE) {
643 *image_index = i;
644 chain->images[i].state = WSI_IMAGE_DRAWING;
645 return VK_SUCCESS;
646 }
647 }
648
649 assert(chain->dxgi);
650 uint32_t index = chain->dxgi->GetCurrentBackBufferIndex();
651 if (chain->images[index].state == WSI_IMAGE_DRAWING) {
652 index = (index + 1) % chain->base.image_count;
653 assert(chain->images[index].state == WSI_IMAGE_QUEUED);
654 }
655 if (chain->wsi->wsi->WaitForFences(chain->base.device, 1,
656 &chain->base.fences[index],
657 false, info->timeout) != VK_SUCCESS)
658 return VK_TIMEOUT;
659
660 *image_index = index;
661 chain->images[index].state = WSI_IMAGE_DRAWING;
662 return VK_SUCCESS;
663 }
664
665 static VkResult
wsi_win32_queue_present_dxgi(struct wsi_win32_swapchain * chain,struct wsi_win32_image * image,const VkPresentRegionKHR * damage)666 wsi_win32_queue_present_dxgi(struct wsi_win32_swapchain *chain,
667 struct wsi_win32_image *image,
668 const VkPresentRegionKHR *damage)
669 {
670 uint32_t rect_count = damage ? damage->rectangleCount : 0;
671 STACK_ARRAY(RECT, rects, rect_count);
672
673 for (uint32_t r = 0; r < rect_count; r++) {
674 rects[r].left = damage->pRectangles[r].offset.x;
675 rects[r].top = damage->pRectangles[r].offset.y;
676 rects[r].right = damage->pRectangles[r].offset.x + damage->pRectangles[r].extent.width;
677 rects[r].bottom = damage->pRectangles[r].offset.y + damage->pRectangles[r].extent.height;
678 }
679
680 DXGI_PRESENT_PARAMETERS params = {
681 rect_count,
682 rects,
683 };
684
685 image->state = WSI_IMAGE_QUEUED;
686 UINT sync_interval = chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR ? 1 : 0;
687 UINT present_flags = chain->base.present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR ?
688 DXGI_PRESENT_ALLOW_TEARING : 0;
689
690 HRESULT hres = chain->dxgi->Present1(sync_interval, present_flags, ¶ms);
691 switch (hres) {
692 case DXGI_ERROR_DEVICE_REMOVED: return VK_ERROR_DEVICE_LOST;
693 case E_OUTOFMEMORY: return VK_ERROR_OUT_OF_DEVICE_MEMORY;
694 default:
695 if (FAILED(hres))
696 return VK_ERROR_OUT_OF_HOST_MEMORY;
697 break;
698 }
699
700 if (chain->surface->current_swapchain != chain) {
701 chain->surface->visual->SetContent(chain->dxgi);
702 chain->wsi->dxgi.dcomp->Commit();
703 chain->surface->current_swapchain = chain;
704 }
705
706 /* Mark the other image idle */
707 chain->status = VK_SUCCESS;
708 return VK_SUCCESS;
709 }
710
711 static VkResult
wsi_win32_queue_present(struct wsi_swapchain * drv_chain,uint32_t image_index,uint64_t present_id,const VkPresentRegionKHR * damage)712 wsi_win32_queue_present(struct wsi_swapchain *drv_chain,
713 uint32_t image_index,
714 uint64_t present_id,
715 const VkPresentRegionKHR *damage)
716 {
717 struct wsi_win32_swapchain *chain = (struct wsi_win32_swapchain *) drv_chain;
718 assert(image_index < chain->base.image_count);
719 struct wsi_win32_image *image = &chain->images[image_index];
720
721 assert(image->state == WSI_IMAGE_DRAWING);
722
723 if (chain->dxgi)
724 return wsi_win32_queue_present_dxgi(chain, image, damage);
725
726 char *ptr = (char *)image->base.cpu_map;
727 char *dptr = (char *)image->sw.ppvBits;
728
729 for (unsigned h = 0; h < chain->extent.height; h++) {
730 memcpy(dptr, ptr, chain->extent.width * 4);
731 dptr += image->sw.bmp_row_pitch;
732 ptr += image->base.row_pitches[0];
733 }
734 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))
735 chain->status = VK_ERROR_MEMORY_MAP_FAILED;
736
737 image->state = WSI_IMAGE_IDLE;
738
739 return chain->status;
740 }
741
742 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)743 wsi_win32_surface_create_swapchain_dxgi(
744 wsi_win32_surface *surface,
745 VkDevice device,
746 struct wsi_win32 *wsi,
747 const VkSwapchainCreateInfoKHR *create_info,
748 struct wsi_win32_swapchain *chain)
749 {
750 IDXGIFactory4 *factory = wsi->dxgi.factory;
751 ID3D12CommandQueue *queue =
752 (ID3D12CommandQueue *)wsi->wsi->win32.get_d3d12_command_queue(device);
753
754 DXGI_SWAP_CHAIN_DESC1 desc = {
755 create_info->imageExtent.width,
756 create_info->imageExtent.height,
757 DXGI_FORMAT_B8G8R8A8_UNORM,
758 create_info->imageArrayLayers > 1, // Stereo
759 { 1 }, // SampleDesc
760 0, // Usage (filled in below)
761 create_info->minImageCount,
762 DXGI_SCALING_STRETCH,
763 DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
764 DXGI_ALPHA_MODE_UNSPECIFIED,
765 chain->base.present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR ?
766 DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0u
767 };
768
769 if (create_info->imageUsage &
770 (VK_IMAGE_USAGE_SAMPLED_BIT |
771 VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))
772 desc.BufferUsage |= DXGI_USAGE_SHADER_INPUT;
773
774 if (create_info->imageUsage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
775 desc.BufferUsage |= DXGI_USAGE_RENDER_TARGET_OUTPUT;
776
777 IDXGISwapChain1 *swapchain1;
778 if (FAILED(factory->CreateSwapChainForComposition(queue, &desc, NULL, &swapchain1)) ||
779 FAILED(swapchain1->QueryInterface(&chain->dxgi)))
780 return VK_ERROR_INITIALIZATION_FAILED;
781
782 swapchain1->Release();
783
784 if (!surface->target &&
785 FAILED(wsi->dxgi.dcomp->CreateTargetForHwnd(surface->base.hwnd, false, &surface->target)))
786 return VK_ERROR_INITIALIZATION_FAILED;
787
788 if (!surface->visual) {
789 if (FAILED(wsi->dxgi.dcomp->CreateVisual(&surface->visual)) ||
790 FAILED(surface->target->SetRoot(surface->visual)) ||
791 FAILED(surface->visual->SetContent(chain->dxgi)) ||
792 FAILED(wsi->dxgi.dcomp->Commit()))
793 return VK_ERROR_INITIALIZATION_FAILED;
794
795 surface->current_swapchain = chain;
796 }
797 return VK_SUCCESS;
798 }
799
800 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)801 wsi_win32_surface_create_swapchain(
802 VkIcdSurfaceBase *icd_surface,
803 VkDevice device,
804 struct wsi_device *wsi_device,
805 const VkSwapchainCreateInfoKHR *create_info,
806 const VkAllocationCallbacks *allocator,
807 struct wsi_swapchain **swapchain_out)
808 {
809 wsi_win32_surface *surface = (wsi_win32_surface *)icd_surface;
810 struct wsi_win32 *wsi =
811 (struct wsi_win32 *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_WIN32];
812
813 assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
814
815 const unsigned num_images = create_info->minImageCount;
816 struct wsi_win32_swapchain *chain;
817 size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]);
818
819 chain = (wsi_win32_swapchain *)vk_zalloc(allocator, size,
820 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
821
822 if (chain == NULL)
823 return VK_ERROR_OUT_OF_HOST_MEMORY;
824
825 struct wsi_dxgi_image_params dxgi_image_params = {
826 { WSI_IMAGE_TYPE_DXGI },
827 };
828 dxgi_image_params.storage_image = (create_info->imageUsage & VK_IMAGE_USAGE_STORAGE_BIT) != 0;
829
830 struct wsi_cpu_image_params cpu_image_params = {
831 { WSI_IMAGE_TYPE_CPU },
832 };
833
834 bool supports_dxgi = wsi->dxgi.factory &&
835 wsi->dxgi.dcomp &&
836 wsi->wsi->win32.get_d3d12_command_queue;
837 struct wsi_base_image_params *image_params = supports_dxgi ?
838 &dxgi_image_params.base : &cpu_image_params.base;
839
840 VkResult result = wsi_swapchain_init(wsi_device, &chain->base, device,
841 create_info, image_params,
842 allocator);
843 if (result != VK_SUCCESS) {
844 vk_free(allocator, chain);
845 return result;
846 }
847
848 chain->base.destroy = wsi_win32_swapchain_destroy;
849 chain->base.get_wsi_image = wsi_win32_get_wsi_image;
850 chain->base.acquire_next_image = wsi_win32_acquire_next_image;
851 chain->base.release_images = wsi_win32_release_images;
852 chain->base.queue_present = wsi_win32_queue_present;
853 chain->base.present_mode = wsi_swapchain_get_present_mode(wsi_device, create_info);
854 chain->extent = create_info->imageExtent;
855
856 chain->wsi = wsi;
857 chain->status = VK_SUCCESS;
858
859 chain->surface = surface;
860
861 if (image_params->image_type == WSI_IMAGE_TYPE_DXGI) {
862 result = wsi_win32_surface_create_swapchain_dxgi(surface, device, wsi, create_info, chain);
863 if (result != VK_SUCCESS)
864 goto fail;
865 }
866
867 for (uint32_t image = 0; image < num_images; image++) {
868 result = wsi_win32_image_init(device, chain,
869 create_info, allocator,
870 &chain->images[image]);
871 if (result != VK_SUCCESS)
872 goto fail;
873
874 chain->base.image_count++;
875 }
876
877 *swapchain_out = &chain->base;
878
879 return VK_SUCCESS;
880
881 fail:
882 if (surface->visual) {
883 surface->visual->SetContent(NULL);
884 surface->current_swapchain = NULL;
885 wsi->dxgi.dcomp->Commit();
886 }
887 wsi_win32_swapchain_destroy(&chain->base, allocator);
888 return result;
889 }
890
891 static IDXGIFactory4 *
dxgi_get_factory(bool debug)892 dxgi_get_factory(bool debug)
893 {
894 HMODULE dxgi_mod = LoadLibraryA("DXGI.DLL");
895 if (!dxgi_mod) {
896 return NULL;
897 }
898
899 typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY2)(UINT flags, REFIID riid, void **ppFactory);
900 PFN_CREATE_DXGI_FACTORY2 CreateDXGIFactory2;
901
902 CreateDXGIFactory2 = (PFN_CREATE_DXGI_FACTORY2)GetProcAddress(dxgi_mod, "CreateDXGIFactory2");
903 if (!CreateDXGIFactory2) {
904 return NULL;
905 }
906
907 UINT flags = 0;
908 if (debug)
909 flags |= DXGI_CREATE_FACTORY_DEBUG;
910
911 IDXGIFactory4 *factory;
912 HRESULT hr = CreateDXGIFactory2(flags, IID_PPV_ARGS(&factory));
913 if (FAILED(hr)) {
914 return NULL;
915 }
916
917 return factory;
918 }
919
920 static IDCompositionDevice *
dcomp_get_device()921 dcomp_get_device()
922 {
923 HMODULE dcomp_mod = LoadLibraryA("DComp.DLL");
924 if (!dcomp_mod) {
925 return NULL;
926 }
927
928 typedef HRESULT (STDAPICALLTYPE *PFN_DCOMP_CREATE_DEVICE)(IDXGIDevice *, REFIID, void **);
929 PFN_DCOMP_CREATE_DEVICE DCompositionCreateDevice;
930
931 DCompositionCreateDevice = (PFN_DCOMP_CREATE_DEVICE)GetProcAddress(dcomp_mod, "DCompositionCreateDevice");
932 if (!DCompositionCreateDevice) {
933 return NULL;
934 }
935
936 IDCompositionDevice *device;
937 HRESULT hr = DCompositionCreateDevice(NULL, IID_PPV_ARGS(&device));
938 if (FAILED(hr)) {
939 return NULL;
940 }
941
942 return device;
943 }
944
945 VkResult
wsi_win32_init_wsi(struct wsi_device * wsi_device,const VkAllocationCallbacks * alloc,VkPhysicalDevice physical_device)946 wsi_win32_init_wsi(struct wsi_device *wsi_device,
947 const VkAllocationCallbacks *alloc,
948 VkPhysicalDevice physical_device)
949 {
950 struct wsi_win32 *wsi;
951 VkResult result;
952
953 wsi = (wsi_win32 *)vk_zalloc(alloc, sizeof(*wsi), 8,
954 VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
955 if (!wsi) {
956 result = VK_ERROR_OUT_OF_HOST_MEMORY;
957 goto fail;
958 }
959
960 wsi->physical_device = physical_device;
961 wsi->alloc = alloc;
962 wsi->wsi = wsi_device;
963
964 if (!wsi_device->sw) {
965 wsi->dxgi.factory = dxgi_get_factory(WSI_DEBUG & WSI_DEBUG_DXGI);
966 if (!wsi->dxgi.factory) {
967 vk_free(alloc, wsi);
968 result = VK_ERROR_INITIALIZATION_FAILED;
969 goto fail;
970 }
971 wsi->dxgi.dcomp = dcomp_get_device();
972 if (!wsi->dxgi.dcomp) {
973 wsi->dxgi.factory->Release();
974 vk_free(alloc, wsi);
975 result = VK_ERROR_INITIALIZATION_FAILED;
976 goto fail;
977 }
978 }
979
980 wsi->base.get_support = wsi_win32_surface_get_support;
981 wsi->base.get_capabilities2 = wsi_win32_surface_get_capabilities2;
982 wsi->base.get_formats = wsi_win32_surface_get_formats;
983 wsi->base.get_formats2 = wsi_win32_surface_get_formats2;
984 wsi->base.get_present_modes = wsi_win32_surface_get_present_modes;
985 wsi->base.get_present_rectangles = wsi_win32_surface_get_present_rectangles;
986 wsi->base.create_swapchain = wsi_win32_surface_create_swapchain;
987
988 wsi_device->wsi[VK_ICD_WSI_PLATFORM_WIN32] = &wsi->base;
989
990 return VK_SUCCESS;
991
992 fail:
993 wsi_device->wsi[VK_ICD_WSI_PLATFORM_WIN32] = NULL;
994
995 return result;
996 }
997
998 void
wsi_win32_finish_wsi(struct wsi_device * wsi_device,const VkAllocationCallbacks * alloc)999 wsi_win32_finish_wsi(struct wsi_device *wsi_device,
1000 const VkAllocationCallbacks *alloc)
1001 {
1002 struct wsi_win32 *wsi =
1003 (struct wsi_win32 *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_WIN32];
1004 if (!wsi)
1005 return;
1006
1007 if (wsi->dxgi.factory)
1008 wsi->dxgi.factory->Release();
1009 if (wsi->dxgi.dcomp)
1010 wsi->dxgi.dcomp->Release();
1011
1012 vk_free(alloc, wsi);
1013 }
1014