code:
#pragma once

#include "../util/rc/util_rc.h"
#include "../util/rc/util_rc_ptr.h"

#define VK_USE_PLATFORM_WIN32_KHR 1
#include <vulkan/vulkan.h>

#define VULKAN_FN(name) \
  ::PFN_ ## name name = reinterpret_cast<::PFN_ ## name>(sym(#name))

namespace dxvk::vk {

  /**
   * \brief Vulkan library loader
   *
   * Provides methods to load Vulkan functions that
   * can be called before creating a  instance.
   */
  struct LibraryLoader : public RcObject {
    PFN_vkVoidFunction sym(const char* name) const;
  };

  /**
   * \brief Vulkan instance loader
   *
   * Loads Vulkan functions that can be
   * called for a specific instance.
   */
  struct InstanceLoader : public RcObject {
    InstanceLoader(bool owned, VkInstance instance);
    PFN_vkVoidFunction sym(const char* name) const;
    VkInstance instance() const { return m_instance; }
  protected:
    const VkInstance m_instance;
    const bool       m_owned;
  };

  /**
   * \brief Vulkan device loader
   *
   * Loads Vulkan functions for a
   * specific device.
   */
  struct DeviceLoader : public RcObject {
    DeviceLoader(bool owned, VkInstance instance, VkDevice device);
    PFN_vkVoidFunction sym(const char* name) const;
    VkDevice device() const { return m_device; }
  protected:
    const PFN_vkGetDeviceProcAddr m_getDeviceProcAddr;
    const VkDevice                m_device;
    const bool                    m_owned;
  };

  /**
   * \brief Vulkan library functions
   *
   * Vulkan functions that are called before
   * creating an actual Vulkan instance.
   */
  struct LibraryFn : LibraryLoader {
    LibraryFn();
    ~LibraryFn();

    VULKAN_FN(vkCreateInstance);
    VULKAN_FN(vkEnumerateInstanceLayerProperties);
    VULKAN_FN(vkEnumerateInstanceExtensionProperties);
  };

  /**
   * \brief Vulkan instance functions
   *
   * Vulkan functions for a given instance that
   * are independent of any Vulkan devices.
   */
  struct InstanceFn : InstanceLoader {
    InstanceFn(bool owned, VkInstance instance);
    ~InstanceFn();

    VULKAN_FN(vkCreateDevice);
    VULKAN_FN(vkDestroyInstance);
    VULKAN_FN(vkEnumerateDeviceExtensionProperties);
    VULKAN_FN(vkEnumeratePhysicalDevices);
    VULKAN_FN(vkGetPhysicalDeviceExternalSemaphoreProperties);
    VULKAN_FN(vkGetPhysicalDeviceFeatures);
    VULKAN_FN(vkGetPhysicalDeviceFeatures2);
    VULKAN_FN(vkGetPhysicalDeviceFormatProperties);
    VULKAN_FN(vkGetPhysicalDeviceFormatProperties2);
    VULKAN_FN(vkGetPhysicalDeviceProperties2);
    VULKAN_FN(vkGetPhysicalDeviceImageFormatProperties);
    VULKAN_FN(vkGetPhysicalDeviceImageFormatProperties2);
    VULKAN_FN(vkGetPhysicalDeviceMemoryProperties);
    VULKAN_FN(vkGetPhysicalDeviceMemoryProperties2);
    VULKAN_FN(vkGetPhysicalDeviceProperties);
    VULKAN_FN(vkGetPhysicalDeviceQueueFamilyProperties);
    VULKAN_FN(vkGetPhysicalDeviceQueueFamilyProperties2);
    VULKAN_FN(vkGetPhysicalDeviceSparseImageFormatProperties);
    VULKAN_FN(vkGetPhysicalDeviceSparseImageFormatProperties2);

    #ifdef VK_KHR_get_surface_capabilities2
    VULKAN_FN(vkGetPhysicalDeviceSurfaceCapabilities2KHR);
    VULKAN_FN(vkGetPhysicalDeviceSurfaceFormats2KHR);
    #endif

    #ifdef VK_KHR_surface
    #ifdef VK_USE_PLATFORM_XCB_KHR
    VULKAN_FN(vkCreateXcbSurfaceKHR);
    VULKAN_FN(vkGetPhysicalDeviceXcbPresentationSupportKHR);
    #endif

    #ifdef VK_USE_PLATFORM_XLIB_KHR
    VULKAN_FN(vkCreateXlibSurfaceKHR);
    VULKAN_FN(vkGetPhysicalDeviceXlibPresentationSupportKHR);
    #endif

    #ifdef VK_USE_PLATFORM_WAYLAND_KHR
    VULKAN_FN(vkCreateWaylandSurfaceKHR);
    VULKAN_FN(vkGetPhysicalDeviceWaylandPresentationSupportKHR);
    #endif

    #ifdef VK_USE_PLATFORM_WIN32_KHR
    VULKAN_FN(vkCreateWin32SurfaceKHR);
    VULKAN_FN(vkGetPhysicalDeviceWin32PresentationSupportKHR);
    #endif

    VULKAN_FN(vkDestroySurfaceKHR);

    VULKAN_FN(vkGetPhysicalDeviceSurfaceSupportKHR);
    VULKAN_FN(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
    VULKAN_FN(vkGetPhysicalDeviceSurfaceFormatsKHR);
    VULKAN_FN(vkGetPhysicalDeviceSurfacePresentModesKHR);
    #endif

    #ifdef VK_EXT_debug_report
    VULKAN_FN(vkCreateDebugReportCallbackEXT);
    VULKAN_FN(vkDestroyDebugReportCallbackEXT);
    VULKAN_FN(vkDebugReportMessageEXT);
    #endif

    #ifdef VK_EXT_debug_utils
    VULKAN_FN(vkCmdBeginDebugUtilsLabelEXT);
    VULKAN_FN(vkCmdEndDebugUtilsLabelEXT);
    VULKAN_FN(vkCmdInsertDebugUtilsLabelEXT);
    #endif

    #ifdef VK_EXT_full_screen_exclusive
    VULKAN_FN(vkGetPhysicalDeviceSurfacePresentModes2EXT);
    #endif
  };

  /**
   * \brief Vulkan device functions
   *
   * Vulkan functions for a specific Vulkan device.
   * This ensures that no slow dispatch code is executed.
   */
  struct DeviceFn : DeviceLoader {
    DeviceFn(bool owned, VkInstance instance, VkDevice device);
    ~DeviceFn();

    VULKAN_FN(vkDestroyDevice);
    VULKAN_FN(vkGetDeviceQueue);
    VULKAN_FN(vkQueueSubmit);
    VULKAN_FN(vkQueueWaitIdle);
    VULKAN_FN(vkDeviceWaitIdle);
    VULKAN_FN(vkAllocateMemory);
    VULKAN_FN(vkFreeMemory);
    VULKAN_FN(vkMapMemory);
    VULKAN_FN(vkUnmapMemory);
    VULKAN_FN(vkFlushMappedMemoryRanges);
    VULKAN_FN(vkInvalidateMappedMemoryRanges);
    VULKAN_FN(vkGetDeviceMemoryCommitment);
    VULKAN_FN(vkBindBufferMemory);
    VULKAN_FN(vkBindImageMemory);
    VULKAN_FN(vkGetBufferMemoryRequirements);
    VULKAN_FN(vkGetBufferMemoryRequirements2);
    VULKAN_FN(vkGetImageMemoryRequirements);
    VULKAN_FN(vkGetImageMemoryRequirements2);
    VULKAN_FN(vkGetImageSparseMemoryRequirements);
    VULKAN_FN(vkGetImageSparseMemoryRequirements2);
    VULKAN_FN(vkQueueBindSparse);
    VULKAN_FN(vkCreateFence);
    VULKAN_FN(vkDestroyFence);
    VULKAN_FN(vkResetFences);
    VULKAN_FN(vkGetFenceStatus);
    VULKAN_FN(vkWaitForFences);
    VULKAN_FN(vkCreateSemaphore);
    VULKAN_FN(vkDestroySemaphore);
    VULKAN_FN(vkCreateEvent);
    VULKAN_FN(vkDestroyEvent);
    VULKAN_FN(vkGetEventStatus);
    VULKAN_FN(vkSetEvent);
    VULKAN_FN(vkResetEvent);
    VULKAN_FN(vkCreateQueryPool);
    VULKAN_FN(vkDestroyQueryPool);
    VULKAN_FN(vkGetQueryPoolResults);
    VULKAN_FN(vkCreateBuffer);
    VULKAN_FN(vkDestroyBuffer);
    VULKAN_FN(vkCreateBufferView);
    VULKAN_FN(vkDestroyBufferView);
    VULKAN_FN(vkCreateImage);
    VULKAN_FN(vkDestroyImage);
    VULKAN_FN(vkGetImageSubresourceLayout);
    VULKAN_FN(vkCreateImageView);
    VULKAN_FN(vkDestroyImageView);
    VULKAN_FN(vkCreateShaderModule);
    VULKAN_FN(vkDestroyShaderModule);
    VULKAN_FN(vkCreatePipelineCache);
    VULKAN_FN(vkDestroyPipelineCache);
    VULKAN_FN(vkGetPipelineCacheData);
    VULKAN_FN(vkMergePipelineCaches);
    VULKAN_FN(vkCreateGraphicsPipelines);
    VULKAN_FN(vkCreateComputePipelines);
    VULKAN_FN(vkDestroyPipeline);
    VULKAN_FN(vkCreatePipelineLayout);
    VULKAN_FN(vkDestroyPipelineLayout);
    VULKAN_FN(vkCreateSampler);
    VULKAN_FN(vkDestroySampler);
    VULKAN_FN(vkCreateDescriptorSetLayout);
    VULKAN_FN(vkDestroyDescriptorSetLayout);
    VULKAN_FN(vkCreateDescriptorPool);
    VULKAN_FN(vkDestroyDescriptorPool);
    VULKAN_FN(vkResetDescriptorPool);
    VULKAN_FN(vkAllocateDescriptorSets);
    VULKAN_FN(vkFreeDescriptorSets);
    VULKAN_FN(vkUpdateDescriptorSets);
    VULKAN_FN(vkCreateFramebuffer);
    VULKAN_FN(vkDestroyFramebuffer);
    VULKAN_FN(vkCreateRenderPass);
    VULKAN_FN(vkDestroyRenderPass);
    VULKAN_FN(vkGetRenderAreaGranularity);
    VULKAN_FN(vkCreateCommandPool);
    VULKAN_FN(vkDestroyCommandPool);
    VULKAN_FN(vkResetCommandPool);
    VULKAN_FN(vkAllocateCommandBuffers);
    VULKAN_FN(vkFreeCommandBuffers);
    VULKAN_FN(vkBeginCommandBuffer);
    VULKAN_FN(vkEndCommandBuffer);
    VULKAN_FN(vkResetCommandBuffer);
    VULKAN_FN(vkCreateDescriptorUpdateTemplate);
    VULKAN_FN(vkDestroyDescriptorUpdateTemplate);
    VULKAN_FN(vkUpdateDescriptorSetWithTemplate);
    VULKAN_FN(vkCmdBindPipeline);
    VULKAN_FN(vkCmdSetViewport);
    VULKAN_FN(vkCmdSetScissor);
    VULKAN_FN(vkCmdSetLineWidth);
    VULKAN_FN(vkCmdSetDepthBias);
    VULKAN_FN(vkCmdSetBlendConstants);
    VULKAN_FN(vkCmdSetDepthBounds);
    VULKAN_FN(vkCmdSetStencilCompareMask);
    VULKAN_FN(vkCmdSetStencilWriteMask);
    VULKAN_FN(vkCmdSetStencilReference);
    VULKAN_FN(vkCmdBindDescriptorSets);
    VULKAN_FN(vkCmdBindIndexBuffer);
    VULKAN_FN(vkCmdBindVertexBuffers);
    VULKAN_FN(vkCmdDraw);
    VULKAN_FN(vkCmdDrawIndexed);
    VULKAN_FN(vkCmdDrawIndirect);
    VULKAN_FN(vkCmdDrawIndexedIndirect);
    VULKAN_FN(vkCmdDispatch);
    VULKAN_FN(vkCmdDispatchIndirect);
    VULKAN_FN(vkCmdCopyBuffer);
    VULKAN_FN(vkCmdCopyImage);
    VULKAN_FN(vkCmdBlitImage);
    VULKAN_FN(vkCmdCopyBufferToImage);
    VULKAN_FN(vkCmdCopyImageToBuffer);
    VULKAN_FN(vkCmdUpdateBuffer);
    VULKAN_FN(vkCmdFillBuffer);
    VULKAN_FN(vkCmdClearColorImage);
    VULKAN_FN(vkCmdClearDepthStencilImage);
    VULKAN_FN(vkCmdClearAttachments);
    VULKAN_FN(vkCmdResolveImage);
    VULKAN_FN(vkCmdSetEvent);
    VULKAN_FN(vkCmdResetEvent);
    VULKAN_FN(vkCmdWaitEvents);
    VULKAN_FN(vkCmdPipelineBarrier);
    VULKAN_FN(vkCmdBeginQuery);
    VULKAN_FN(vkCmdEndQuery);
    VULKAN_FN(vkCmdResetQueryPool);
    VULKAN_FN(vkCmdWriteTimestamp);
    VULKAN_FN(vkCmdCopyQueryPoolResults);
    VULKAN_FN(vkCmdPushConstants);
    VULKAN_FN(vkCmdBeginRenderPass);
    VULKAN_FN(vkCmdNextSubpass);
    VULKAN_FN(vkCmdEndRenderPass);
    VULKAN_FN(vkCmdExecuteCommands);

    #ifdef VK_KHR_create_renderpass2
    VULKAN_FN(vkCreateRenderPass2KHR);
    VULKAN_FN(vkCmdBeginRenderPass2KHR);
    VULKAN_FN(vkCmdNextSubpass2KHR);
    VULKAN_FN(vkCmdEndRenderPass2KHR);
    #endif

    #ifdef VK_KHR_draw_indirect_count
    VULKAN_FN(vkCmdDrawIndirectCountKHR);
    VULKAN_FN(vkCmdDrawIndexedIndirectCountKHR);
    #endif

    #ifdef VK_KHR_swapchain
    VULKAN_FN(vkCreateSwapchainKHR);
    VULKAN_FN(vkDestroySwapchainKHR);
    VULKAN_FN(vkGetSwapchainImagesKHR);
    VULKAN_FN(vkAcquireNextImageKHR);
    VULKAN_FN(vkQueuePresentKHR);
    #endif

    #ifdef VK_KHR_timeline_semaphore
    VULKAN_FN(vkGetSemaphoreCounterValueKHR);
    VULKAN_FN(vkSignalSemaphoreKHR);
    VULKAN_FN(vkWaitSemaphoresKHR);
    #endif

    #ifdef VK_EXT_conditional_rendering
    VULKAN_FN(vkCmdBeginConditionalRenderingEXT);
    VULKAN_FN(vkCmdEndConditionalRenderingEXT);
    #endif

    #ifdef VK_EXT_extended_dynamic_state
    VULKAN_FN(vkCmdBindVertexBuffers2EXT);
    VULKAN_FN(vkCmdSetCullModeEXT);
    VULKAN_FN(vkCmdSetDepthBoundsTestEnableEXT);
    VULKAN_FN(vkCmdSetDepthCompareOpEXT);
    VULKAN_FN(vkCmdSetDepthTestEnableEXT);
    VULKAN_FN(vkCmdSetDepthWriteEnableEXT);
    VULKAN_FN(vkCmdSetFrontFaceEXT);
    VULKAN_FN(vkCmdSetPrimitiveTopologyEXT);
    VULKAN_FN(vkCmdSetScissorWithCountEXT);
    VULKAN_FN(vkCmdSetStencilOpEXT);
    VULKAN_FN(vkCmdSetStencilTestEnableEXT);
    VULKAN_FN(vkCmdSetViewportWithCountEXT);
    #endif

    #ifdef VK_EXT_full_screen_exclusive
    VULKAN_FN(vkAcquireFullScreenExclusiveModeEXT);
    VULKAN_FN(vkReleaseFullScreenExclusiveModeEXT);
    VULKAN_FN(vkGetDeviceGroupSurfacePresentModes2EXT);
    #endif

    #ifdef VK_EXT_host_query_reset
    VULKAN_FN(vkResetQueryPoolEXT);
    #endif

    #ifdef VK_EXT_transform_feedback
    VULKAN_FN(vkCmdBindTransformFeedbackBuffersEXT);
    VULKAN_FN(vkCmdBeginTransformFeedbackEXT);
    VULKAN_FN(vkCmdEndTransformFeedbackEXT);
    VULKAN_FN(vkCmdDrawIndirectByteCountEXT);
    VULKAN_FN(vkCmdBeginQueryIndexedEXT);
    VULKAN_FN(vkCmdEndQueryIndexedEXT);
    #endif

    #ifdef VK_NVX_image_view_handle
    VULKAN_FN(vkGetImageViewHandleNVX);
    VULKAN_FN(vkGetImageViewAddressNVX);
    #endif

    #ifdef VK_NVX_binary_import
    VULKAN_FN(vkCreateCuModuleNVX);
    VULKAN_FN(vkCreateCuFunctionNVX);
    VULKAN_FN(vkDestroyCuModuleNVX);
    VULKAN_FN(vkDestroyCuFunctionNVX);
    VULKAN_FN(vkCmdCuLaunchKernelNVX);
    #endif

    #ifdef VK_KHR_buffer_device_address
    VULKAN_FN(vkGetBufferDeviceAddressKHR);
    #endif

    #ifdef VK_KHR_external_memory_win32
    VULKAN_FN(vkGetMemoryWin32HandleKHR);
    VULKAN_FN(vkGetMemoryWin32HandlePropertiesKHR);
    #endif

    #ifdef VK_KHR_external_semaphore_win32
    VULKAN_FN(vkGetSemaphoreWin32HandleKHR);
    VULKAN_FN(vkImportSemaphoreWin32HandleKHR);
    #endif
  };

}


code:
#include "vulkan_names.h"

std::ostream& operator << (std::ostream& os, VkPipelineCacheHeaderVersion e) {
  switch (e) {
    ENUM_NAME(VK_PIPELINE_CACHE_HEADER_VERSION_ONE);
    ENUM_DEFAULT(e);
  }
  return os;
}

std::ostream& operator << (std::ostream& os, VkResult e) {
  switch (e) {
    ENUM_NAME(VK_SUCCESS);
    ENUM_NAME(VK_NOT_READY);
    ENUM_NAME(VK_TIMEOUT);
    ENUM_NAME(VK_EVENT_SET);
    ENUM_NAME(VK_EVENT_RESET);
    ENUM_NAME(VK_INCOMPLETE);
    ENUM_NAME(VK_ERROR_OUT_OF_HOST_MEMORY);
    ENUM_NAME(VK_ERROR_OUT_OF_DEVICE_MEMORY);
    ENUM_NAME(VK_ERROR_INITIALIZATION_FAILED);
    ENUM_NAME(VK_ERROR_DEVICE_LOST);
    ENUM_NAME(VK_ERROR_MEMORY_MAP_FAILED);
    ENUM_NAME(VK_ERROR_LAYER_NOT_PRESENT);
    ENUM_NAME(VK_ERROR_EXTENSION_NOT_PRESENT);
    ENUM_NAME(VK_ERROR_FEATURE_NOT_PRESENT);
    ENUM_NAME(VK_ERROR_INCOMPATIBLE_DRIVER);
    ENUM_NAME(VK_ERROR_TOO_MANY_OBJECTS);
    ENUM_NAME(VK_ERROR_FORMAT_NOT_SUPPORTED);
    ENUM_NAME(VK_ERROR_FRAGMENTED_POOL);
    ENUM_NAME(VK_ERROR_SURFACE_LOST_KHR);
    ENUM_NAME(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR);
    ENUM_NAME(VK_SUBOPTIMAL_KHR);
    ENUM_NAME(VK_ERROR_OUT_OF_DATE_KHR);
    ENUM_NAME(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR);
    ENUM_NAME(VK_ERROR_VALIDATION_FAILED_EXT);
    ENUM_NAME(VK_ERROR_INVALID_SHADER_NV);
    ENUM_NAME(VK_ERROR_OUT_OF_POOL_MEMORY_KHR);
    ENUM_NAME(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR);
    ENUM_DEFAULT(e);
  }
  return os;
}

std::ostream& operator << (std::ostream& os, VkFormat e) {
  switch (e) {
    ENUM_NAME(VK_FORMAT_UNDEFINED);
    ENUM_NAME(VK_FORMAT_R4G4_UNORM_PACK8);
    ENUM_NAME(VK_FORMAT_R4G4B4A4_UNORM_PACK16);
    ENUM_NAME(VK_FORMAT_B4G4R4A4_UNORM_PACK16);
    ENUM_NAME(VK_FORMAT_R5G6B5_UNORM_PACK16);
    ENUM_NAME(VK_FORMAT_B5G6R5_UNORM_PACK16);
    ENUM_NAME(VK_FORMAT_R5G5B5A1_UNORM_PACK16);
    ENUM_NAME(VK_FORMAT_B5G5R5A1_UNORM_PACK16);
    ENUM_NAME(VK_FORMAT_A1R5G5B5_UNORM_PACK16);
    ENUM_NAME(VK_FORMAT_R8_UNORM);
    ENUM_NAME(VK_FORMAT_R8_SNORM);
    ENUM_NAME(VK_FORMAT_R8_USCALED);
    ENUM_NAME(VK_FORMAT_R8_SSCALED);
    ENUM_NAME(VK_FORMAT_R8_UINT);
    ENUM_NAME(VK_FORMAT_R8_SINT);
    ENUM_NAME(VK_FORMAT_R8_SRGB);
    ENUM_NAME(VK_FORMAT_R8G8_UNORM);
    ENUM_NAME(VK_FORMAT_R8G8_SNORM);
    ENUM_NAME(VK_FORMAT_R8G8_USCALED);
    ENUM_NAME(VK_FORMAT_R8G8_SSCALED);
    ENUM_NAME(VK_FORMAT_R8G8_UINT);
    ENUM_NAME(VK_FORMAT_R8G8_SINT);
    ENUM_NAME(VK_FORMAT_R8G8_SRGB);
    ENUM_NAME(VK_FORMAT_R8G8B8_UNORM);
    ENUM_NAME(VK_FORMAT_R8G8B8_SNORM);
    ENUM_NAME(VK_FORMAT_R8G8B8_USCALED);
    ENUM_NAME(VK_FORMAT_R8G8B8_SSCALED);
    ENUM_NAME(VK_FORMAT_R8G8B8_UINT);
    ENUM_NAME(VK_FORMAT_R8G8B8_SINT);
    ENUM_NAME(VK_FORMAT_R8G8B8_SRGB);
    ENUM_NAME(VK_FORMAT_B8G8R8_UNORM);
    ENUM_NAME(VK_FORMAT_B8G8R8_SNORM);
    ENUM_NAME(VK_FORMAT_B8G8R8_USCALED);
    ENUM_NAME(VK_FORMAT_B8G8R8_SSCALED);
    ENUM_NAME(VK_FORMAT_B8G8R8_UINT);
    ENUM_NAME(VK_FORMAT_B8G8R8_SINT);
    ENUM_NAME(VK_FORMAT_B8G8R8_SRGB);
    ENUM_NAME(VK_FORMAT_R8G8B8A8_UNORM);
    ENUM_NAME(VK_FORMAT_R8G8B8A8_SNORM);
    ENUM_NAME(VK_FORMAT_R8G8B8A8_USCALED);
    ENUM_NAME(VK_FORMAT_R8G8B8A8_SSCALED);
    ENUM_NAME(VK_FORMAT_R8G8B8A8_UINT);
    ENUM_NAME(VK_FORMAT_R8G8B8A8_SINT);
    ENUM_NAME(VK_FORMAT_R8G8B8A8_SRGB);
    ENUM_NAME(VK_FORMAT_B8G8R8A8_UNORM);
    ENUM_NAME(VK_FORMAT_B8G8R8A8_SNORM);
    ENUM_NAME(VK_FORMAT_B8G8R8A8_USCALED);
    ENUM_NAME(VK_FORMAT_B8G8R8A8_SSCALED);
    ENUM_NAME(VK_FORMAT_B8G8R8A8_UINT);
    ENUM_NAME(VK_FORMAT_B8G8R8A8_SINT);
    ENUM_NAME(VK_FORMAT_B8G8R8A8_SRGB);
    ENUM_NAME(VK_FORMAT_A8B8G8R8_UNORM_PACK32);
    ENUM_NAME(VK_FORMAT_A8B8G8R8_SNORM_PACK32);
    ENUM_NAME(VK_FORMAT_A8B8G8R8_USCALED_PACK32);
    ENUM_NAME(VK_FORMAT_A8B8G8R8_SSCALED_PACK32);
    ENUM_NAME(VK_FORMAT_A8B8G8R8_UINT_PACK32);
    ENUM_NAME(VK_FORMAT_A8B8G8R8_SINT_PACK32);
    ENUM_NAME(VK_FORMAT_A8B8G8R8_SRGB_PACK32);
    ENUM_NAME(VK_FORMAT_A2R10G10B10_UNORM_PACK32);
    ENUM_NAME(VK_FORMAT_A2R10G10B10_SNORM_PACK32);
    ENUM_NAME(VK_FORMAT_A2R10G10B10_USCALED_PACK32);
    ENUM_NAME(VK_FORMAT_A2R10G10B10_SSCALED_PACK32);
    ENUM_NAME(VK_FORMAT_A2R10G10B10_UINT_PACK32);
    ENUM_NAME(VK_FORMAT_A2R10G10B10_SINT_PACK32);
    ENUM_NAME(VK_FORMAT_A2B10G10R10_UNORM_PACK32);
    ENUM_NAME(VK_FORMAT_A2B10G10R10_SNORM_PACK32);
    ENUM_NAME(VK_FORMAT_A2B10G10R10_USCALED_PACK32);
    ENUM_NAME(VK_FORMAT_A2B10G10R10_SSCALED_PACK32);
    ENUM_NAME(VK_FORMAT_A2B10G10R10_UINT_PACK32);
    ENUM_NAME(VK_FORMAT_A2B10G10R10_SINT_PACK32);
    ENUM_NAME(VK_FORMAT_R16_UNORM);
    ENUM_NAME(VK_FORMAT_R16_SNORM);
    ENUM_NAME(VK_FORMAT_R16_USCALED);
    ENUM_NAME(VK_FORMAT_R16_SSCALED);
    ENUM_NAME(VK_FORMAT_R16_UINT);
    ENUM_NAME(VK_FORMAT_R16_SINT);
    ENUM_NAME(VK_FORMAT_R16_SFLOAT);
    ENUM_NAME(VK_FORMAT_R16G16_UNORM);
    ENUM_NAME(VK_FORMAT_R16G16_SNORM);
    ENUM_NAME(VK_FORMAT_R16G16_USCALED);
    ENUM_NAME(VK_FORMAT_R16G16_SSCALED);
    ENUM_NAME(VK_FORMAT_R16G16_UINT);
    ENUM_NAME(VK_FORMAT_R16G16_SINT);
    ENUM_NAME(VK_FORMAT_R16G16_SFLOAT);
    ENUM_NAME(VK_FORMAT_R16G16B16_UNORM);
    ENUM_NAME(VK_FORMAT_R16G16B16_SNORM);
    ENUM_NAME(VK_FORMAT_R16G16B16_USCALED);
    ENUM_NAME(VK_FORMAT_R16G16B16_SSCALED);
    ENUM_NAME(VK_FORMAT_R16G16B16_UINT);
    ENUM_NAME(VK_FORMAT_R16G16B16_SINT);
    ENUM_NAME(VK_FORMAT_R16G16B16_SFLOAT);
    ENUM_NAME(VK_FORMAT_R16G16B16A16_UNORM);
    ENUM_NAME(VK_FORMAT_R16G16B16A16_SNORM);
    ENUM_NAME(VK_FORMAT_R16G16B16A16_USCALED);
    ENUM_NAME(VK_FORMAT_R16G16B16A16_SSCALED);
    ENUM_NAME(VK_FORMAT_R16G16B16A16_UINT);
    ENUM_NAME(VK_FORMAT_R16G16B16A16_SINT);
    ENUM_NAME(VK_FORMAT_R16G16B16A16_SFLOAT);
    ENUM_NAME(VK_FORMAT_R32_UINT);
    ENUM_NAME(VK_FORMAT_R32_SINT);
    ENUM_NAME(VK_FORMAT_R32_SFLOAT);
    ENUM_NAME(VK_FORMAT_R32G32_UINT);
    ENUM_NAME(VK_FORMAT_R32G32_SINT);
    ENUM_NAME(VK_FORMAT_R32G32_SFLOAT);
    ENUM_NAME(VK_FORMAT_R32G32B32_UINT);
    ENUM_NAME(VK_FORMAT_R32G32B32_SINT);
    ENUM_NAME(VK_FORMAT_R32G32B32_SFLOAT);
    ENUM_NAME(VK_FORMAT_R32G32B32A32_UINT);
    ENUM_NAME(VK_FORMAT_R32G32B32A32_SINT);
    ENUM_NAME(VK_FORMAT_R32G32B32A32_SFLOAT);
    ENUM_NAME(VK_FORMAT_R64_UINT);
    ENUM_NAME(VK_FORMAT_R64_SINT);
    ENUM_NAME(VK_FORMAT_R64_SFLOAT);
    ENUM_NAME(VK_FORMAT_R64G64_UINT);
    ENUM_NAME(VK_FORMAT_R64G64_SINT);
    ENUM_NAME(VK_FORMAT_R64G64_SFLOAT);
    ENUM_NAME(VK_FORMAT_R64G64B64_UINT);
    ENUM_NAME(VK_FORMAT_R64G64B64_SINT);
    ENUM_NAME(VK_FORMAT_R64G64B64_SFLOAT);
    ENUM_NAME(VK_FORMAT_R64G64B64A64_UINT);
    ENUM_NAME(VK_FORMAT_R64G64B64A64_SINT);
    ENUM_NAME(VK_FORMAT_R64G64B64A64_SFLOAT);
    ENUM_NAME(VK_FORMAT_B10G11R11_UFLOAT_PACK32);
    ENUM_NAME(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32);
    ENUM_NAME(VK_FORMAT_D16_UNORM);
    ENUM_NAME(VK_FORMAT_X8_D24_UNORM_PACK32);
    ENUM_NAME(VK_FORMAT_D32_SFLOAT);
    ENUM_NAME(VK_FORMAT_S8_UINT);
    ENUM_NAME(VK_FORMAT_D16_UNORM_S8_UINT);
    ENUM_NAME(VK_FORMAT_D24_UNORM_S8_UINT);
    ENUM_NAME(VK_FORMAT_D32_SFLOAT_S8_UINT);
    ENUM_NAME(VK_FORMAT_BC1_RGB_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_BC1_RGB_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_BC1_RGBA_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_BC1_RGBA_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_BC2_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_BC2_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_BC3_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_BC3_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_BC4_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_BC4_SNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_BC5_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_BC5_SNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_BC6H_UFLOAT_BLOCK);
    ENUM_NAME(VK_FORMAT_BC6H_SFLOAT_BLOCK);
    ENUM_NAME(VK_FORMAT_BC7_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_BC7_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_EAC_R11_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_EAC_R11_SNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_EAC_R11G11_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_EAC_R11G11_SNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_4x4_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_4x4_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_5x4_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_5x4_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_5x5_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_5x5_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_6x5_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_6x5_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_6x6_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_6x6_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_8x5_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_8x5_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_8x6_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_8x6_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_8x8_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_8x8_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_10x5_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_10x5_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_10x6_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_10x6_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_10x8_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_10x8_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_10x10_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_10x10_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_12x10_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_12x10_SRGB_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_12x12_UNORM_BLOCK);
    ENUM_NAME(VK_FORMAT_ASTC_12x12_SRGB_BLOCK);
    ENUM_DEFAULT(e);
  }
  return os;
}

std::ostream& operator << (std::ostream& os, VkImageType e) {
  switch (e) {
    ENUM_NAME(VK_IMAGE_TYPE_1D);
    ENUM_NAME(VK_IMAGE_TYPE_2D);
    ENUM_NAME(VK_IMAGE_TYPE_3D);
    ENUM_DEFAULT(e);
  }
  return os;
}

std::ostream& operator << (std::ostream& os, VkImageTiling e) {
  switch (e) {
    ENUM_NAME(VK_IMAGE_TILING_OPTIMAL);
    ENUM_NAME(VK_IMAGE_TILING_LINEAR);
    ENUM_DEFAULT(e);
  }
  return os;
}

std::ostream& operator << (std::ostream& os, VkImageLayout e) {
  switch (e) {
    ENUM_NAME(VK_IMAGE_LAYOUT_UNDEFINED);
    ENUM_NAME(VK_IMAGE_LAYOUT_GENERAL);
    ENUM_NAME(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
    ENUM_NAME(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
    ENUM_NAME(VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL);
    ENUM_NAME(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
    ENUM_NAME(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
    ENUM_NAME(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
    ENUM_NAME(VK_IMAGE_LAYOUT_PREINITIALIZED);
    ENUM_NAME(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
    ENUM_NAME(VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR);
    ENUM_DEFAULT(e);
  }
  return os;
}

std::ostream& operator << (std::ostream& os, VkImageViewType e) {
  switch (e) {
    ENUM_NAME(VK_IMAGE_VIEW_TYPE_1D);
    ENUM_NAME(VK_IMAGE_VIEW_TYPE_2D);
    ENUM_NAME(VK_IMAGE_VIEW_TYPE_3D);
    ENUM_NAME(VK_IMAGE_VIEW_TYPE_CUBE);
    ENUM_NAME(VK_IMAGE_VIEW_TYPE_1D_ARRAY);
    ENUM_NAME(VK_IMAGE_VIEW_TYPE_2D_ARRAY);
    ENUM_NAME(VK_IMAGE_VIEW_TYPE_CUBE_ARRAY);
    ENUM_DEFAULT(e);
  }
  return os;
}

std::ostream& operator << (std::ostream& os, VkPresentModeKHR e) {
  switch (e) {
    ENUM_NAME(VK_PRESENT_MODE_IMMEDIATE_KHR);
    ENUM_NAME(VK_PRESENT_MODE_MAILBOX_KHR);
    ENUM_NAME(VK_PRESENT_MODE_FIFO_KHR);
    ENUM_NAME(VK_PRESENT_MODE_FIFO_RELAXED_KHR);
    ENUM_DEFAULT(e);
  }
  return os;
}

std::ostream& operator << (std::ostream& os, VkColorSpaceKHR e) {
  switch (e) {
    ENUM_NAME(VK_COLOR_SPACE_SRGB_NONLINEAR_KHR);
    ENUM_DEFAULT(e);
  }
  return os;
}

std::ostream& operator << (std::ostream& os, VkOffset2D e) {
  return os << "(" << e.x << "," << e.y << ")";
}

std::ostream& operator << (std::ostream& os, VkOffset3D e) {
  return os << "(" << e.x << "," << e.y << "," << e.z << ")";
}

std::ostream& operator << (std::ostream& os, VkExtent2D e) {
  return os << "(" << e.width << "," << e.height << ")";
}

std::ostream& operator << (std::ostream& os, VkExtent3D e) {
  return os << "(" << e.width << "," << e.height << "," << e.depth << ")";
}

repo_name: orochi/dxvk


code:
#pragma once

#include <ostream>

#include "../util/util_enum.h"

#include "vulkan_loader.h"

std::ostream& operator << (std::ostream& os, VkPipelineCacheHeaderVersion e);
std::ostream& operator << (std::ostream& os, VkResult e);
std::ostream& operator << (std::ostream& os, VkFormat e);
std::ostream& operator << (std::ostream& os, VkImageType e);
std::ostream& operator << (std::ostream& os, VkImageTiling e);
std::ostream& operator << (std::ostream& os, VkImageLayout e);
std::ostream& operator << (std::ostream& os, VkImageViewType e);
std::ostream& operator << (std::ostream& os, VkPresentModeKHR e);
std::ostream& operator << (std::ostream& os, VkColorSpaceKHR e);
std::ostream& operator << (std::ostream& os, VkOffset2D e);
std::ostream& operator << (std::ostream& os, VkOffset3D e);
std::ostream& operator << (std::ostream& os, VkExtent2D e);
std::ostream& operator << (std::ostream& os, VkExtent3D e);


code:
#include "vulkan_presenter.h"

#include "../dxvk/dxvk_format.h"

namespace dxvk::vk {

  Presenter::Presenter(
          HWND            window,
    const Rc<InstanceFn>& vki,
    const Rc<DeviceFn>&   vkd,
          PresenterDevice device,
    const PresenterDesc&  desc)
  : m_vki(vki), m_vkd(vkd), m_device(device), m_window(window) {
    // As of Wine 5.9, winevulkan provides this extension, but does
    // not filter the pNext chain for VkSwapchainCreateInfoKHR properly
    // before passing it to the Linux sude, which breaks RenderDoc.
    if (m_device.features.fullScreenExclusive && ::GetModuleHandle("winevulkan.dll")) {
      Logger::warn("winevulkan detected, disabling exclusive fullscreen support");
      m_device.features.fullScreenExclusive = false;
    }

    if (createSurface() != VK_SUCCESS)
      throw DxvkError("Failed to create surface");

    if (recreateSwapChain(desc) != VK_SUCCESS)
      throw DxvkError("Failed to create swap chain");
  }

  Presenter::~Presenter() {
    destroySwapchain();
    destroySurface();
  }

  PresenterInfo Presenter::info() const {
    return m_info;
  }

  PresenterImage Presenter::getImage(uint32_t index) const {
    return m_images.at(index);
  }

  VkResult Presenter::acquireNextImage(PresenterSync& sync, uint32_t& index) {
    sync = m_semaphores.at(m_frameIndex);

    // Don't acquire more than one image at a time
    if (m_acquireStatus == VK_NOT_READY) {
      m_acquireStatus = m_vkd->vkAcquireNextImageKHR(m_vkd->device(),
        m_swapchain, std::numeric_limits<uint64_t>::max(),
        sync.acquire, VK_NULL_HANDLE, &m_imageIndex);
    }

    if (m_acquireStatus != VK_SUCCESS && m_acquireStatus != VK_SUBOPTIMAL_KHR)
      return m_acquireStatus;

    index = m_imageIndex;
    return m_acquireStatus;
  }

  VkResult Presenter::presentImage() {
    PresenterSync sync = m_semaphores.at(m_frameIndex);

    VkPresentInfoKHR info;
    info.sType              = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
    info.pNext              = nullptr;
    info.waitSemaphoreCount = 1;
    info.pWaitSemaphores    = &sync.present;
    info.swapchainCount     = 1;
    info.pSwapchains        = &m_swapchain;
    info.pImageIndices      = &m_imageIndex;
    info.pResults           = nullptr;

    VkResult status = m_vkd->vkQueuePresentKHR(m_device.queue, &info);

    if (status != VK_SUCCESS && status != VK_SUBOPTIMAL_KHR)
      return status;

    // Try to acquire next image already, in order to hide
    // potential delays from the application thread.
    m_frameIndex += 1;
    m_frameIndex %= m_semaphores.size();

    sync = m_semaphores.at(m_frameIndex);

    m_acquireStatus = m_vkd->vkAcquireNextImageKHR(m_vkd->device(),
      m_swapchain, std::numeric_limits<uint64_t>::max(),
      sync.acquire, VK_NULL_HANDLE, &m_imageIndex);

    bool vsync = m_info.presentMode == VK_PRESENT_MODE_FIFO_KHR
              || m_info.presentMode == VK_PRESENT_MODE_FIFO_RELAXED_KHR;

    m_fpsLimiter.delay(vsync);
    return status;
  }

  VkResult Presenter::recreateSwapChain(const PresenterDesc& desc) {
    if (m_swapchain)
      destroySwapchain();

    // Query surface capabilities. Some properties might
    // have changed, including the size limits and supported
    // present modes, so we'll just query everything again.
    VkSurfaceCapabilitiesKHR        caps;
    std::vector<VkSurfaceFormatKHR> formats;
    std::vector<VkPresentModeKHR>   modes;

    VkResult status;

    if ((status = m_vki->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
        m_device.adapter, m_surface, &caps)) != VK_SUCCESS) {
      if (status == VK_ERROR_SURFACE_LOST_KHR) {
        // Recreate the surface and try again.
        if (m_surface)
          destroySurface();
        if ((status = createSurface()) != VK_SUCCESS)
          return status;
        status = m_vki->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
            m_device.adapter, m_surface, &caps);
      }
      if (status != VK_SUCCESS)
        return status;
    }

    if ((status = getSupportedFormats(formats, desc)) != VK_SUCCESS)
      return status;

    if ((status = getSupportedPresentModes(modes, desc)) != VK_SUCCESS)
      return status;

    // Select actual swap chain properties and create swap chain
    m_info.format       = pickFormat(formats.size(), formats.data(), desc.numFormats, desc.formats);
    m_info.presentMode  = pickPresentMode(modes.size(), modes.data(), desc.numPresentModes, desc.presentModes);
    m_info.imageExtent  = pickImageExtent(caps, desc.imageExtent);
    m_info.imageCount   = pickImageCount(caps, m_info.presentMode, desc.imageCount);

    if (!m_info.imageExtent.width || !m_info.imageExtent.height) {
      m_info.imageCount = 0;
      m_info.format     = { VK_FORMAT_UNDEFINED, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
      return VK_SUCCESS;
    }

    VkSurfaceFullScreenExclusiveInfoEXT fullScreenInfo;
    fullScreenInfo.sType            = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT;
    fullScreenInfo.pNext            = nullptr;
    fullScreenInfo.fullScreenExclusive = desc.fullScreenExclusive;

    VkSwapchainCreateInfoKHR swapInfo;
    swapInfo.sType                  = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
    swapInfo.pNext                  = nullptr;
    swapInfo.flags                  = 0;
    swapInfo.surface                = m_surface;
    swapInfo.minImageCount          = m_info.imageCount;
    swapInfo.imageFormat            = m_info.format.format;
    swapInfo.imageColorSpace        = m_info.format.colorSpace;
    swapInfo.imageExtent            = m_info.imageExtent;
    swapInfo.imageArrayLayers       = 1;
    swapInfo.imageUsage             = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
                                    | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    swapInfo.imageSharingMode       = VK_SHARING_MODE_EXCLUSIVE;
    swapInfo.queueFamilyIndexCount  = 0;
    swapInfo.pQueueFamilyIndices    = nullptr;
    swapInfo.preTransform           = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
    swapInfo.compositeAlpha         = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
    swapInfo.presentMode            = m_info.presentMode;
    swapInfo.clipped                = VK_TRUE;
    swapInfo.oldSwapchain           = VK_NULL_HANDLE;

    if (m_device.features.fullScreenExclusive)
      swapInfo.pNext = &fullScreenInfo;

    Logger::info(str::format(
      "Presenter: Actual swap chain properties:"
      "\n  Format:       ", m_info.format.format,
      "\n  Present mode: ", m_info.presentMode,
      "\n  Buffer size:  ", m_info.imageExtent.width, "x", m_info.imageExtent.height,
      "\n  Image count:  ", m_info.imageCount,
      "\n  Exclusive FS: ", desc.fullScreenExclusive));

    if ((status = m_vkd->vkCreateSwapchainKHR(m_vkd->device(),
        &swapInfo, nullptr, &m_swapchain)) != VK_SUCCESS)
      return status;

    // Acquire images and create views
    std::vector<VkImage> images;

    if ((status = getSwapImages(images)) != VK_SUCCESS)
      return status;

    // Update actual image count
    m_info.imageCount = images.size();
    m_images.resize(m_info.imageCount);

    for (uint32_t i = 0; i < m_info.imageCount; i++) {
      m_images[i].image = images[i];

      VkImageViewCreateInfo viewInfo;
      viewInfo.sType    = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
      viewInfo.pNext    = nullptr;
      viewInfo.flags    = 0;
      viewInfo.image    = images[i];
      viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
      viewInfo.format   = m_info.format.format;
      viewInfo.components = VkComponentMapping {
        VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
        VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
      viewInfo.subresourceRange = {
        VK_IMAGE_ASPECT_COLOR_BIT,
        0, 1, 0, 1 };

      if ((status = m_vkd->vkCreateImageView(m_vkd->device(),
          &viewInfo, nullptr, &m_images[i].view)) != VK_SUCCESS)
        return status;
    }

    // Create one set of semaphores per swap image
    m_semaphores.resize(m_info.imageCount);

    for (uint32_t i = 0; i < m_semaphores.size(); i++) {
      VkSemaphoreCreateInfo semInfo;
      semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
      semInfo.pNext = nullptr;
      semInfo.flags = 0;

      if ((status = m_vkd->vkCreateSemaphore(m_vkd->device(),
          &semInfo, nullptr, &m_semaphores[i].acquire)) != VK_SUCCESS)
        return status;

      if ((status = m_vkd->vkCreateSemaphore(m_vkd->device(),
          &semInfo, nullptr, &m_semaphores[i].present)) != VK_SUCCESS)
        return status;
    }

    // Invalidate indices
    m_imageIndex = 0;
    m_frameIndex = 0;
    m_acquireStatus = VK_NOT_READY;
    return VK_SUCCESS;
  }

  void Presenter::setFrameRateLimit(double frameRate) {
    m_fpsLimiter.setTargetFrameRate(frameRate);
  }

  VkResult Presenter::getSupportedFormats(std::vector<VkSurfaceFormatKHR>& formats, const PresenterDesc& desc) {
    uint32_t numFormats = 0;

    VkSurfaceFullScreenExclusiveInfoEXT fullScreenInfo;
    fullScreenInfo.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT;
    fullScreenInfo.pNext = nullptr;
    fullScreenInfo.fullScreenExclusive = desc.fullScreenExclusive;

    VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo;
    surfaceInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR;
    surfaceInfo.pNext = &fullScreenInfo;
    surfaceInfo.surface = m_surface;

    VkResult status;

    if (m_device.features.fullScreenExclusive) {
      status = m_vki->vkGetPhysicalDeviceSurfaceFormats2KHR(
        m_device.adapter, &surfaceInfo, &numFormats, nullptr);
    } else {
      status = m_vki->vkGetPhysicalDeviceSurfaceFormatsKHR(
        m_device.adapter, m_surface, &numFormats, nullptr);
    }

    if (status != VK_SUCCESS)
      return status;

    formats.resize(numFormats);

    if (m_device.features.fullScreenExclusive) {
      std::vector<VkSurfaceFormat2KHR> tmpFormats(numFormats,
        { VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR, nullptr, VkSurfaceFormatKHR() });

      status = m_vki->vkGetPhysicalDeviceSurfaceFormats2KHR(
        m_device.adapter, &surfaceInfo, &numFormats, tmpFormats.data());

      for (uint32_t i = 0; i < numFormats; i++)
        formats[i] = tmpFormats[i].surfaceFormat;
    } else {
      status = m_vki->vkGetPhysicalDeviceSurfaceFormatsKHR(
        m_device.adapter, m_surface, &numFormats, formats.data());
    }

    return status;
  }

  VkResult Presenter::getSupportedPresentModes(std::vector<VkPresentModeKHR>& modes, const PresenterDesc& desc) {
    uint32_t numModes = 0;

    VkSurfaceFullScreenExclusiveInfoEXT fullScreenInfo;
    fullScreenInfo.sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT;
    fullScreenInfo.pNext = nullptr;
    fullScreenInfo.fullScreenExclusive = desc.fullScreenExclusive;

    VkPhysicalDeviceSurfaceInfo2KHR surfaceInfo;
    surfaceInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR;
    surfaceInfo.pNext = &fullScreenInfo;
    surfaceInfo.surface = m_surface;

    VkResult status;

    if (m_device.features.fullScreenExclusive) {
      status = m_vki->vkGetPhysicalDeviceSurfacePresentModes2EXT(
        m_device.adapter, &surfaceInfo, &numModes, nullptr);
    } else {
      status = m_vki->vkGetPhysicalDeviceSurfacePresentModesKHR(
        m_device.adapter, m_surface, &numModes, nullptr);
    }

    if (status != VK_SUCCESS)
      return status;

    modes.resize(numModes);

    if (m_device.features.fullScreenExclusive) {
      status = m_vki->vkGetPhysicalDeviceSurfacePresentModes2EXT(
        m_device.adapter, &surfaceInfo, &numModes, modes.data());
    } else {
      status = m_vki->vkGetPhysicalDeviceSurfacePresentModesKHR(
        m_device.adapter, m_surface, &numModes, modes.data());
    }

    return status;
  }

  VkResult Presenter::getSwapImages(std::vector<VkImage>& images) {
    uint32_t imageCount = 0;

    VkResult status = m_vkd->vkGetSwapchainImagesKHR(
      m_vkd->device(), m_swapchain, &imageCount, nullptr);

    if (status != VK_SUCCESS)
      return status;

    images.resize(imageCount);

    return m_vkd->vkGetSwapchainImagesKHR(
      m_vkd->device(), m_swapchain, &imageCount, images.data());
  }

  VkSurfaceFormatKHR Presenter::pickFormat(
          uint32_t                  numSupported,
    const VkSurfaceFormatKHR*       pSupported,
          uint32_t                  numDesired,
    const VkSurfaceFormatKHR*       pDesired) {
    if (numDesired > 0) {
      // If the implementation allows us to freely choose
      // the format, we'll just use the preferred format.
      if (numSupported == 1 && pSupported[0].format == VK_FORMAT_UNDEFINED)
        return pDesired[0];

      // If the preferred format is explicitly listed in
      // the array of supported surface formats, use it
      for (uint32_t i = 0; i < numDesired; i++) {
        for (uint32_t j = 0; j < numSupported; j++) {
          if (pSupported[j].format     == pDesired[i].format
           && pSupported[j].colorSpace == pDesired[i].colorSpace)
            return pSupported[j];
        }
      }

      // If that didn't work, we'll fall back to a format
      // which has similar properties to the preferred one
      DxvkFormatFlags prefFlags = imageFormatInfo(pDesired[0].format)->flags;

      for (uint32_t j = 0; j < numSupported; j++) {
        auto currFlags = imageFormatInfo(pSupported[j].format)->flags;

        if ((currFlags & DxvkFormatFlag::ColorSpaceSrgb)
         == (prefFlags & DxvkFormatFlag::ColorSpaceSrgb))
          return pSupported[j];
      }
    }

    // Otherwise, fall back to the first supported format
    return pSupported[0];
  }

  VkPresentModeKHR Presenter::pickPresentMode(
          uint32_t                  numSupported,
    const VkPresentModeKHR*         pSupported,
          uint32_t                  numDesired,
    const VkPresentModeKHR*         pDesired) {
    // Just pick the first desired and supported mode
    for (uint32_t i = 0; i < numDesired; i++) {
      for (uint32_t j = 0; j < numSupported; j++) {
        if (pSupported[j] == pDesired[i])
          return pSupported[j];
      }
    }

    // Guaranteed to be available
    return VK_PRESENT_MODE_FIFO_KHR;
  }

  VkExtent2D Presenter::pickImageExtent(
    const VkSurfaceCapabilitiesKHR& caps,
          VkExtent2D                desired) {
    if (caps.currentExtent.width != std::numeric_limits<uint32_t>::max())
      return caps.currentExtent;

    VkExtent2D actual;
    actual.width  = clamp(desired.width,  caps.minImageExtent.width,  caps.maxImageExtent.width);
    actual.height = clamp(desired.height, caps.minImageExtent.height, caps.maxImageExtent.height);
    return actual;
  }

  uint32_t Presenter::pickImageCount(
    const VkSurfaceCapabilitiesKHR& caps,
          VkPresentModeKHR          presentMode,
          uint32_t                  desired) {
    uint32_t count = caps.minImageCount;

    if (presentMode != VK_PRESENT_MODE_IMMEDIATE_KHR)
      count = caps.minImageCount + 1;

    if (count < desired)
      count = desired;

    if (count > caps.maxImageCount && caps.maxImageCount != 0)
      count = caps.maxImageCount;

    return count;
  }

  VkResult Presenter::createSurface() {
    HINSTANCE instance = reinterpret_cast<HINSTANCE>(
      GetWindowLongPtr(m_window, GWLP_HINSTANCE));

    VkWin32SurfaceCreateInfoKHR info;
    info.sType      = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
    info.pNext      = nullptr;
    info.flags      = 0;
    info.hinstance  = instance;
    info.hwnd       = m_window;

    VkResult status = m_vki->vkCreateWin32SurfaceKHR(
      m_vki->instance(), &info, nullptr, &m_surface);

    if (status != VK_SUCCESS)
      return status;

    VkBool32 supportStatus = VK_FALSE;

    if ((status = m_vki->vkGetPhysicalDeviceSurfaceSupportKHR(m_device.adapter,
        m_device.queueFamily, m_surface, &supportStatus)) != VK_SUCCESS)
      return status;

    if (!supportStatus) {
      m_vki->vkDestroySurfaceKHR(m_vki->instance(), m_surface, nullptr);
      return VK_ERROR_OUT_OF_HOST_MEMORY; // just abuse this
    }

    return VK_SUCCESS;
  }

  void Presenter::destroySwapchain() {
    for (const auto& img : m_images)
      m_vkd->vkDestroyImageView(m_vkd->device(), img.view, nullptr);

    for (const auto& sem : m_semaphores) {
      m_vkd->vkDestroySemaphore(m_vkd->device(), sem.acquire, nullptr);
      m_vkd->vkDestroySemaphore(m_vkd->device(), sem.present, nullptr);
    }

    m_vkd->vkDestroySwapchainKHR(m_vkd->device(), m_swapchain, nullptr);

    m_images.clear();
    m_semaphores.clear();

    m_swapchain = VK_NULL_HANDLE;
  }

  void Presenter::destroySurface() {
    m_vki->vkDestroySurfaceKHR(m_vki->instance(), m_surface, nullptr);
  }

}


code:
#pragma once

#include <vector>

#include "../util/log/log.h"

#include "../util/util_error.h"
#include "../util/util_fps_limiter.h"
#include "../util/util_math.h"
#include "../util/util_string.h"

#include "vulkan_loader.h"

namespace dxvk::vk {

  /**
   * \brief Presenter description
   *
   * Contains the desired properties of
   * the swap chain. This is passed as
   * an input during swap chain creation.
   */
  struct PresenterDesc {
    VkExtent2D          imageExtent;
    uint32_t            imageCount;
    uint32_t            numFormats;
    VkSurfaceFormatKHR  formats[4];
    uint32_t            numPresentModes;
    VkPresentModeKHR    presentModes[4];
    VkFullScreenExclusiveEXT fullScreenExclusive;
  };

  /**
   * \brief Presenter properties
   *
   * Contains the actual properties
   * of the underlying swap chain.
   */
  struct PresenterInfo {
    VkSurfaceFormatKHR  format;
    VkPresentModeKHR    presentMode;
    VkExtent2D          imageExtent;
    uint32_t            imageCount;
  };

  /**
   * \brief Presenter features
   */
  struct PresenterFeatures {
    bool                fullScreenExclusive : 1;
  };

  /**
   * \brief Adapter and queue
   */
  struct PresenterDevice {
    uint32_t            queueFamily = 0;
    VkQueue             queue       = VK_NULL_HANDLE;
    VkPhysicalDevice    adapter     = VK_NULL_HANDLE;
    PresenterFeatures   features    = { };
  };

  /**
   * \brief Swap image and view
   */
  struct PresenterImage {
    VkImage     image = VK_NULL_HANDLE;
    VkImageView view  = VK_NULL_HANDLE;
  };

  /**
   * \brief Presenter semaphores
   *
   * Pair of semaphores used for acquire and present
   * operations, including the command buffers used
   * in between. Also stores a fence to signal on
   * image acquisition.
   */
  struct PresenterSync {
    VkSemaphore acquire;
    VkSemaphore present;
  };

  /**
   * \brief Vulkan presenter
   *
   * Provides abstractions for some of the
   * more complicated aspects of Vulkan's
   * window system integration.
   */
  class Presenter : public RcObject {

  public:

    Presenter(
            HWND            window,
      const Rc<InstanceFn>& vki,
      const Rc<DeviceFn>&   vkd,
            PresenterDevice device,
      const PresenterDesc&  desc);

    ~Presenter();

    /**
     * \brief Actual presenter info
     * \returns Swap chain properties
     */
    PresenterInfo info() const;

    /**
     * \brief Retrieves image by index
     *
     * Can be used to create per-image objects.
     * \param [in] index Image index
     * \returns Image handle
     */
    PresenterImage getImage(
            uint32_t        index) const;

    /**
     * \brief Acquires next image
     *
     * Potentially blocks the calling thread.
     * If this returns an error, the swap chain
     * must be recreated and a new image must
     * be acquired before proceeding.
     * \param [out] sync Synchronization semaphores
     * \param [out] index Acquired image index
     * \returns Status of the operation
     */
    VkResult acquireNextImage(
            PresenterSync&  sync,
            uint32_t&       index);

    /**
     * \brief Presents current image
     *
     * Presents the current image. If this returns
     * an error, the swap chain must be recreated,
     * but do not present before acquiring an image.
     * \returns Status of the operation
     */
    VkResult presentImage();

    /**
     * \brief Changes presenter properties
     *
     * Recreates the swap chain immediately. Note that
     * no swap chain resources must be in use by the
     * GPU at the time this is called.
     * \param [in] desc Swap chain description
     */
    VkResult recreateSwapChain(
      const PresenterDesc&  desc);

    /**
     * \brief Changes maximum frame rate
     *
     * \param [in] frameRate Target frame rate. Set
     *    to 0 in order to disable the limiter.
     */
    void setFrameRateLimit(double frameRate);

    /**
     * \brief Checks whether a Vulkan swap chain exists
     *
     * On Windows, there are situations where we cannot create
     * a swap chain as the surface size can reach zero, and no
     * presentation can be performed.
     * \returns \c true if the presenter has a swap chain.
     */
    bool hasSwapChain() const {
      return m_swapchain;
    }

  private:

    Rc<InstanceFn>    m_vki;
    Rc<DeviceFn>      m_vkd;

    PresenterDevice   m_device;
    PresenterInfo     m_info;

    HWND              m_window      = nullptr;
    VkSurfaceKHR      m_surface     = VK_NULL_HANDLE;
    VkSwapchainKHR    m_swapchain   = VK_NULL_HANDLE;

    std::vector<PresenterImage> m_images;
    std::vector<PresenterSync>  m_semaphores;

    uint32_t m_imageIndex = 0;
    uint32_t m_frameIndex = 0;

    VkResult m_acquireStatus = VK_NOT_READY;

    FpsLimiter m_fpsLimiter;

    VkResult getSupportedFormats(
            std::vector<VkSurfaceFormatKHR>& formats,
      const PresenterDesc&            desc);

    VkResult getSupportedPresentModes(
            std::vector<VkPresentModeKHR>& modes,
      const PresenterDesc&            desc);

    VkResult getSwapImages(
            std::vector<VkImage>&     images);

    VkSurfaceFormatKHR pickFormat(
            uint32_t                  numSupported,
      const VkSurfaceFormatKHR*       pSupported,
            uint32_t                  numDesired,
      const VkSurfaceFormatKHR*       pDesired);

    VkPresentModeKHR pickPresentMode(
            uint32_t                  numSupported,
      const VkPresentModeKHR*         pSupported,
            uint32_t                  numDesired,
      const VkPresentModeKHR*         pDesired);

    VkExtent2D pickImageExtent(
      const VkSurfaceCapabilitiesKHR& caps,
            VkExtent2D                desired);

    uint32_t pickImageCount(
      const VkSurfaceCapabilitiesKHR& caps,
            VkPresentModeKHR          presentMode,
            uint32_t                  desired);

    VkResult createSurface();

    void destroySwapchain();

    void destroySurface();

  };

}


code:
#pragma once

#include <utility>

#include "vulkan_loader.h"

namespace dxvk::vk {

  inline VkImageSubresourceRange makeSubresourceRange(
    const VkImageSubresourceLayers& layers) {
    VkImageSubresourceRange range;
    range.aspectMask      = layers.aspectMask;
    range.baseMipLevel    = layers.mipLevel;
    range.levelCount      = 1;
    range.baseArrayLayer  = layers.baseArrayLayer;
    range.layerCount      = layers.layerCount;
    return range;
  }

  inline VkImageSubresourceRange makeSubresourceRange(
    const VkImageSubresource& subres) {
    VkImageSubresourceRange range;
    range.aspectMask      = subres.aspectMask;
    range.baseMipLevel    = subres.mipLevel;
    range.levelCount      = 1;
    range.baseArrayLayer  = subres.arrayLayer;
    range.layerCount      = 1;
    return range;
  }

  inline VkImageSubresourceLayers makeSubresourceLayers(
    const VkImageSubresource& subres) {
    VkImageSubresourceLayers layers;
    layers.aspectMask     = subres.aspectMask;
    layers.mipLevel       = subres.mipLevel;
    layers.baseArrayLayer = subres.arrayLayer;
    layers.layerCount     = 1;
    return layers;
  }

  inline VkImageSubresourceLayers pickSubresourceLayers(
    const VkImageSubresourceRange&  range,
          uint32_t                  level) {
    VkImageSubresourceLayers layers;
    layers.aspectMask     = range.aspectMask;
    layers.mipLevel       = range.baseMipLevel + level;
    layers.baseArrayLayer = range.baseArrayLayer;
    layers.layerCount     = range.layerCount;
    return layers;
  }

  inline VkImageSubresource pickSubresource(
    const VkImageSubresourceLayers& range,
          uint32_t                  layer) {
    VkImageSubresource subres;
    subres.aspectMask = range.aspectMask;
    subres.mipLevel   = range.mipLevel;
    subres.arrayLayer = range.baseArrayLayer + layer;
    return subres;
  }

  inline VkImageSubresource pickSubresource(
    const VkImageSubresourceRange&  range,
          uint32_t                  level,
          uint32_t                  layer) {
    VkImageSubresource subres;
    subres.aspectMask = range.aspectMask;
    subres.mipLevel   = range.baseMipLevel   + level;
    subres.arrayLayer = range.baseArrayLayer + layer;
    return subres;
  }

  inline bool checkSubresourceRangeOverlap(
    const VkImageSubresourceRange&  a,
    const VkImageSubresourceRange&  b) {
    return a.baseMipLevel < b.baseMipLevel + b.levelCount
        && a.baseMipLevel + a.levelCount > b.baseMipLevel
        && a.baseArrayLayer < b.baseArrayLayer + b.layerCount
        && a.baseArrayLayer + a.layerCount > b.baseArrayLayer;
  }

  inline bool checkSubresourceRangeSuperset(
    const VkImageSubresourceRange&  a,
    const VkImageSubresourceRange&  b) {
    return a.baseMipLevel                <= b.baseMipLevel
        && a.baseMipLevel + a.levelCount >= b.baseMipLevel + b.levelCount
        && a.baseArrayLayer                <= b.baseArrayLayer
        && a.baseArrayLayer + a.layerCount >= b.baseArrayLayer + b.layerCount;
  }

  inline VkImageAspectFlags getWritableAspectsForLayout(VkImageLayout layout) {
    switch (layout) {
      case VK_IMAGE_LAYOUT_GENERAL:
        return VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
      case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
        return VK_IMAGE_ASPECT_COLOR_BIT;
      case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
        return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
      case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL:
        return VK_IMAGE_ASPECT_DEPTH_BIT;
      case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL:
        return VK_IMAGE_ASPECT_STENCIL_BIT;
      case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
      case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
        return 0;
      default:
        Logger::err(str::format("Unhandled image layout ", layout));
        return 0;
    }
  }

  inline uint32_t getPlaneCount(VkImageAspectFlags aspects) {
    // Use a 16-bit integer as a lookup table. This works because
    // plane aspects use consecutive bits in the image aspect enum.
    const uint32_t shift = (aspects / VK_IMAGE_ASPECT_PLANE_0_BIT) * 2;
    const uint32_t counts = 0xffa5;
    return (counts >> shift) & 0x3;
  }

  inline uint32_t getPlaneIndex(VkImageAspectFlags aspect) {
    // Works for up to PLANE_2_BIT due to enum poperties
    return aspect / VK_IMAGE_ASPECT_PLANE_1_BIT;
  }

  inline VkImageAspectFlagBits getPlaneAspect(uint32_t plane) {
    return VkImageAspectFlagBits(VK_IMAGE_ASPECT_PLANE_0_BIT << plane);
  }

  inline VkImageAspectFlags getNextAspect(VkImageAspectFlags& mask) {
    if (likely(mask & (VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT))) {
      // Depth-stencil isn't considered multi-planar
      return std::exchange(mask, VkImageAspectFlags(0));
    } else {
      VkImageAspectFlags result = mask & -mask;
      mask &= ~result;
      return result;
    }
  }

  template<typename T>
  struct ChainStruct {
    VkStructureType sType;
    T*              pNext;
  };

  template<typename T>
  void removeStructFromPNextChain(T** ppNext, VkStructureType sType) {
    while (*ppNext) {
      auto pStruct = reinterpret_cast<ChainStruct<T>*>(*ppNext);

      if (pStruct->sType == sType) {
        *ppNext = pStruct->pNext;
        return;
      }

      ppNext = &pStruct->pNext;
    }
  }

}

inline bool operator == (
  const VkImageSubresourceRange& a,
  const VkImageSubresourceRange& b) {
  return a.aspectMask     == b.aspectMask
      && a.baseMipLevel   == b.baseMipLevel
      && a.levelCount     == b.levelCount
      && a.baseArrayLayer == b.baseArrayLayer
      && a.layerCount     == b.layerCount;
}

inline bool operator != (
  const VkImageSubresourceRange& a,
  const VkImageSubresourceRange& b) {
  return !operator == (a, b);
}

inline bool operator == (
  const VkImageSubresourceLayers& a,
  const VkImageSubresourceLayers& b) {
  return a.aspectMask     == b.aspectMask
      && a.mipLevel       == b.mipLevel
      && a.baseArrayLayer == b.baseArrayLayer
      && a.layerCount     == b.layerCount;
}

inline bool operator != (
  const VkImageSubresourceLayers& a,
  const VkImageSubresourceLayers& b) {
  return !operator == (a, b);
}

inline bool operator == (VkExtent3D a, VkExtent3D b) {
  return a.width  == b.width
      && a.height == b.height
      && a.depth  == b.depth;
}

inline bool operator != (VkExtent3D a, VkExtent3D b) {
  return a.width  != b.width
      || a.height != b.height
      || a.depth  != b.depth;
}

inline bool operator == (VkExtent2D a, VkExtent2D b) {
  return a.width  == b.width
      && a.height == b.height;
}

inline bool operator != (VkExtent2D a, VkExtent2D b) {
  return a.width  != b.width
      || a.height != b.height;
}

inline bool operator == (VkOffset3D a, VkOffset3D b) {
  return a.x == b.x
      && a.y == b.y
      && a.z == b.z;
}

inline bool operator != (VkOffset3D a, VkOffset3D b) {
  return a.x != b.x
      || a.y != b.y
      || a.z != b.z;
}

inline bool operator == (VkOffset2D a, VkOffset2D b) {
  return a.x == b.x
      && a.y == b.y;
}

inline bool operator != (VkOffset2D a, VkOffset2D b) {
  return a.x != b.x
      || a.y != b.y;
}


code:
test_d3d11_deps = [ util_dep, lib_dxgi, lib_d3d11, lib_d3dcompiler_47 ]

executable('d3d11-compute'+exe_ext,   files('test_d3d11_compute.cpp'),   dependencies : test_d3d11_deps, install : true, gui_app : true)
executable('d3d11-formats'+exe_ext,   files('test_d3d11_formats.cpp'),   dependencies : test_d3d11_deps, install : true, gui_app : true)
executable('d3d11-map-read'+exe_ext,  files('test_d3d11_map_read.cpp'),  dependencies : test_d3d11_deps, install : true, gui_app : true)
executable('d3d11-streamout'+exe_ext, files('test_d3d11_streamout.cpp'), dependencies : test_d3d11_deps, install : true, gui_app : true)
executable('d3d11-triangle'+exe_ext,  files('test_d3d11_triangle.cpp'),  dependencies : test_d3d11_deps, install : true, gui_app : true)
executable('d3d11-video'+exe_ext,     files('test_d3d11_video.cpp'),     dependencies : test_d3d11_deps, install : true, gui_app : true)

install_data('video_image.raw', install_dir : get_option('bindir'))


code:
#include <cstring>

#include <d3dcompiler.h>
#include <d3d11.h>

#include <windows.h>
#include <windowsx.h>

#include "../test_utils.h"

using namespace dxvk;

const std::string g_computeShaderCode =
  "StructuredBuffer<uint> buf_in : register(t0);\n"
  "RWStructuredBuffer<uint> buf_out : register(u0);\n"
  "groupshared uint tmp[64];\n"
  "[numthreads(64,1,1)]\n"
  "void main(uint localId : SV_GroupIndex, uint3 globalId : SV_DispatchThreadID) {\n"
  "  tmp[localId] = buf_in[2 * globalId.x + 0]\n"
  "               + buf_in[2 * globalId.x + 1];\n"
  "  GroupMemoryBarrierWithGroupSync();\n"
  "  uint activeGroups = 32;\n"
  "  while (activeGroups != 0) {\n"
  "    if (localId < activeGroups)\n"
  "      tmp[localId] += tmp[localId + activeGroups];\n"
  "    GroupMemoryBarrierWithGroupSync();\n"
  "    activeGroups >>= 1;\n"
  "  }\n"
  "  if (localId == 0)\n"
  "    buf_out[0] = tmp[0];\n"
  "}\n";

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
  Com<ID3D11Device>         device;
  Com<ID3D11DeviceContext>  context;
  Com<ID3D11ComputeShader>  computeShader;

  Com<ID3D11Buffer> srcBuffer;
  Com<ID3D11Buffer> dstBuffer;
  Com<ID3D11Buffer> readBuffer;

  Com<ID3D11ShaderResourceView> srcView;
  Com<ID3D11UnorderedAccessView> dstView;

  if (FAILED(D3D11CreateDevice(
        nullptr, D3D_DRIVER_TYPE_HARDWARE,
        nullptr, 0, nullptr, 0, D3D11_SDK_VERSION,
        &device, nullptr, &context))) {
    std::cerr << "Failed to create D3D11 device" << std::endl;
    return 1;
  }

  Com<ID3DBlob> computeShaderBlob;

  if (FAILED(D3DCompile(
        g_computeShaderCode.data(),
        g_computeShaderCode.size(),
        "Compute shader",
        nullptr, nullptr,
        "main", "cs_5_0", 0, 0,
        &computeShaderBlob,
        nullptr))) {
    std::cerr << "Failed to compile compute shader" << std::endl;
    return 1;
  }

  if (FAILED(device->CreateComputeShader(
        computeShaderBlob->GetBufferPointer(),
        computeShaderBlob->GetBufferSize(),
        nullptr, &computeShader))) {
    std::cerr << "Failed to create compute shader" << std::endl;
    return 1;
  }

  std::array<uint32_t, 128> srcData;
  for (uint32_t i = 0; i < srcData.size(); i++)
    srcData[i] = i + 1;

  D3D11_BUFFER_DESC srcBufferDesc;
  srcBufferDesc.ByteWidth            = sizeof(uint32_t) * srcData.size();
  srcBufferDesc.Usage                = D3D11_USAGE_IMMUTABLE;
  srcBufferDesc.BindFlags            = D3D11_BIND_SHADER_RESOURCE;
  srcBufferDesc.CPUAccessFlags       = 0;
  srcBufferDesc.MiscFlags            = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
  srcBufferDesc.StructureByteStride  = sizeof(uint32_t);

  D3D11_SUBRESOURCE_DATA srcDataInfo;
  srcDataInfo.pSysMem          = srcData.data();
  srcDataInfo.SysMemPitch      = 0;
  srcDataInfo.SysMemSlicePitch = 0;

  if (FAILED(device->CreateBuffer(&srcBufferDesc, &srcDataInfo, &srcBuffer))) {
    std::cerr << "Failed to create source buffer" << std::endl;
    return 1;
  }

  D3D11_BUFFER_DESC dstBufferDesc;
  dstBufferDesc.ByteWidth            = sizeof(uint32_t);
  dstBufferDesc.Usage                = D3D11_USAGE_DEFAULT;
  dstBufferDesc.BindFlags            = D3D11_BIND_UNORDERED_ACCESS;
  dstBufferDesc.CPUAccessFlags       = 0;
  dstBufferDesc.MiscFlags            = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
  dstBufferDesc.StructureByteStride  = sizeof(uint32_t);

  if (FAILED(device->CreateBuffer(&dstBufferDesc, &srcDataInfo, &dstBuffer))) {
    std::cerr << "Failed to create destination buffer" << std::endl;
    return 1;
  }

  D3D11_BUFFER_DESC readBufferDesc;
  readBufferDesc.ByteWidth            = sizeof(uint32_t);
  readBufferDesc.Usage                = D3D11_USAGE_STAGING;
  readBufferDesc.BindFlags            = 0;
  readBufferDesc.CPUAccessFlags       = D3D11_CPU_ACCESS_READ;
  readBufferDesc.MiscFlags            = 0;
  readBufferDesc.StructureByteStride  = 0;

  if (FAILED(device->CreateBuffer(&readBufferDesc, nullptr, &readBuffer))) {
    std::cerr << "Failed to create readback buffer" << std::endl;
    return 1;
  }

  D3D11_SHADER_RESOURCE_VIEW_DESC srcViewDesc;
  srcViewDesc.Format                = DXGI_FORMAT_UNKNOWN;
  srcViewDesc.ViewDimension         = D3D11_SRV_DIMENSION_BUFFEREX;
  srcViewDesc.BufferEx.FirstElement = 0;
  srcViewDesc.BufferEx.NumElements  = srcData.size();
  srcViewDesc.BufferEx.Flags        = 0;

  if (FAILED(device->CreateShaderResourceView(srcBuffer.ptr(), &srcViewDesc, &srcView))) {
    std::cerr << "Failed to create shader resource view" << std::endl;
    return 1;
  }

  D3D11_UNORDERED_ACCESS_VIEW_DESC dstViewDesc;
  dstViewDesc.Format                = DXGI_FORMAT_UNKNOWN;
  dstViewDesc.ViewDimension         = D3D11_UAV_DIMENSION_BUFFER;
  dstViewDesc.Buffer.FirstElement   = 0;
  dstViewDesc.Buffer.NumElements    = 1;
  dstViewDesc.Buffer.Flags          = 0;

  if (FAILED(device->CreateUnorderedAccessView(dstBuffer.ptr(), &dstViewDesc, &dstView))) {
    std::cerr << "Failed to create unordered access view" << std::endl;
    return 1;
  }

  // Compute sum of the source buffer values
  context->CSSetShader(computeShader.ptr(), nullptr, 0);
  context->CSSetShaderResources(0, 1, &srcView);
  context->CSSetUnorderedAccessViews(0, 1, &dstView, nullptr);
  context->Dispatch(1, 1, 1);

  // Write data to the readback buffer and query the result
  context->CopyResource(readBuffer.ptr(), dstBuffer.ptr());

  D3D11_MAPPED_SUBRESOURCE mappedResource;
  if (FAILED(context->Map(readBuffer.ptr(), 0, D3D11_MAP_READ, 0, &mappedResource))) {
    std::cerr << "Failed to map readback buffer" << std::endl;
    return 1;
  }

  uint32_t result = 0;
  std::memcpy(&result, mappedResource.pData, sizeof(result));
  context->Unmap(readBuffer.ptr(), 0);

  std::cout << "Sum of the numbers 1 to " << srcData.size() << " = " << result << std::endl;
  context->ClearState();
  return 0;
}


code:
#include <cstring>

#include <d3dcompiler.h>
#include <d3d11_4.h>

#include <windows.h>
#include <windowsx.h>

#include "../test_utils.h"

#undef ENUM_NAME
#define ENUM_NAME(e) case e: return #e;

using namespace dxvk;

std::string GetFormatName(DXGI_FORMAT Format) {
  switch (Format) {
    ENUM_NAME(DXGI_FORMAT_UNKNOWN);
    ENUM_NAME(DXGI_FORMAT_R32G32B32A32_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_R32G32B32A32_FLOAT);
    ENUM_NAME(DXGI_FORMAT_R32G32B32A32_UINT);
    ENUM_NAME(DXGI_FORMAT_R32G32B32A32_SINT);
    ENUM_NAME(DXGI_FORMAT_R32G32B32_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_R32G32B32_FLOAT);
    ENUM_NAME(DXGI_FORMAT_R32G32B32_UINT);
    ENUM_NAME(DXGI_FORMAT_R32G32B32_SINT);
    ENUM_NAME(DXGI_FORMAT_R16G16B16A16_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_R16G16B16A16_FLOAT);
    ENUM_NAME(DXGI_FORMAT_R16G16B16A16_UNORM);
    ENUM_NAME(DXGI_FORMAT_R16G16B16A16_UINT);
    ENUM_NAME(DXGI_FORMAT_R16G16B16A16_SNORM);
    ENUM_NAME(DXGI_FORMAT_R16G16B16A16_SINT);
    ENUM_NAME(DXGI_FORMAT_R32G32_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_R32G32_FLOAT);
    ENUM_NAME(DXGI_FORMAT_R32G32_UINT);
    ENUM_NAME(DXGI_FORMAT_R32G32_SINT);
    ENUM_NAME(DXGI_FORMAT_R32G8X24_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_D32_FLOAT_S8X24_UINT);
    ENUM_NAME(DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_X32_TYPELESS_G8X24_UINT);
    ENUM_NAME(DXGI_FORMAT_R10G10B10A2_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_R10G10B10A2_UNORM);
    ENUM_NAME(DXGI_FORMAT_R10G10B10A2_UINT);
    ENUM_NAME(DXGI_FORMAT_R11G11B10_FLOAT);
    ENUM_NAME(DXGI_FORMAT_R8G8B8A8_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_R8G8B8A8_UNORM);
    ENUM_NAME(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB);
    ENUM_NAME(DXGI_FORMAT_R8G8B8A8_UINT);
    ENUM_NAME(DXGI_FORMAT_R8G8B8A8_SNORM);
    ENUM_NAME(DXGI_FORMAT_R8G8B8A8_SINT);
    ENUM_NAME(DXGI_FORMAT_R16G16_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_R16G16_FLOAT);
    ENUM_NAME(DXGI_FORMAT_R16G16_UNORM);
    ENUM_NAME(DXGI_FORMAT_R16G16_UINT);
    ENUM_NAME(DXGI_FORMAT_R16G16_SNORM);
    ENUM_NAME(DXGI_FORMAT_R16G16_SINT);
    ENUM_NAME(DXGI_FORMAT_R32_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_D32_FLOAT);
    ENUM_NAME(DXGI_FORMAT_R32_FLOAT);
    ENUM_NAME(DXGI_FORMAT_R32_UINT);
    ENUM_NAME(DXGI_FORMAT_R32_SINT);
    ENUM_NAME(DXGI_FORMAT_R24G8_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_D24_UNORM_S8_UINT);
    ENUM_NAME(DXGI_FORMAT_R24_UNORM_X8_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_X24_TYPELESS_G8_UINT);
    ENUM_NAME(DXGI_FORMAT_R8G8_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_R8G8_UNORM);
    ENUM_NAME(DXGI_FORMAT_R8G8_UINT);
    ENUM_NAME(DXGI_FORMAT_R8G8_SNORM);
    ENUM_NAME(DXGI_FORMAT_R8G8_SINT);
    ENUM_NAME(DXGI_FORMAT_R16_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_R16_FLOAT);
    ENUM_NAME(DXGI_FORMAT_D16_UNORM);
    ENUM_NAME(DXGI_FORMAT_R16_UNORM);
    ENUM_NAME(DXGI_FORMAT_R16_UINT);
    ENUM_NAME(DXGI_FORMAT_R16_SNORM);
    ENUM_NAME(DXGI_FORMAT_R16_SINT);
    ENUM_NAME(DXGI_FORMAT_R8_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_R8_UNORM);
    ENUM_NAME(DXGI_FORMAT_R8_UINT);
    ENUM_NAME(DXGI_FORMAT_R8_SNORM);
    ENUM_NAME(DXGI_FORMAT_R8_SINT);
    ENUM_NAME(DXGI_FORMAT_A8_UNORM);
    ENUM_NAME(DXGI_FORMAT_R1_UNORM);
    ENUM_NAME(DXGI_FORMAT_R9G9B9E5_SHAREDEXP);
    ENUM_NAME(DXGI_FORMAT_R8G8_B8G8_UNORM);
    ENUM_NAME(DXGI_FORMAT_G8R8_G8B8_UNORM);
    ENUM_NAME(DXGI_FORMAT_BC1_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_BC1_UNORM);
    ENUM_NAME(DXGI_FORMAT_BC1_UNORM_SRGB);
    ENUM_NAME(DXGI_FORMAT_BC2_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_BC2_UNORM);
    ENUM_NAME(DXGI_FORMAT_BC2_UNORM_SRGB);
    ENUM_NAME(DXGI_FORMAT_BC3_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_BC3_UNORM);
    ENUM_NAME(DXGI_FORMAT_BC3_UNORM_SRGB);
    ENUM_NAME(DXGI_FORMAT_BC4_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_BC4_UNORM);
    ENUM_NAME(DXGI_FORMAT_BC4_SNORM);
    ENUM_NAME(DXGI_FORMAT_BC5_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_BC5_UNORM);
    ENUM_NAME(DXGI_FORMAT_BC5_SNORM);
    ENUM_NAME(DXGI_FORMAT_B5G6R5_UNORM);
    ENUM_NAME(DXGI_FORMAT_B5G5R5A1_UNORM);
    ENUM_NAME(DXGI_FORMAT_B8G8R8A8_UNORM);
    ENUM_NAME(DXGI_FORMAT_B8G8R8X8_UNORM);
    ENUM_NAME(DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM);
    ENUM_NAME(DXGI_FORMAT_B8G8R8A8_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB);
    ENUM_NAME(DXGI_FORMAT_B8G8R8X8_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_B8G8R8X8_UNORM_SRGB);
    ENUM_NAME(DXGI_FORMAT_BC6H_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_BC6H_UF16);
    ENUM_NAME(DXGI_FORMAT_BC6H_SF16);
    ENUM_NAME(DXGI_FORMAT_BC7_TYPELESS);
    ENUM_NAME(DXGI_FORMAT_BC7_UNORM);
    ENUM_NAME(DXGI_FORMAT_BC7_UNORM_SRGB);
    default: return std::to_string(Format);
  }
}

std::string GetFormatFlagName(D3D11_FORMAT_SUPPORT Flag) {
  switch (Flag) {
    ENUM_NAME(D3D11_FORMAT_SUPPORT_BUFFER);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_IA_VERTEX_BUFFER);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_IA_INDEX_BUFFER);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_SO_BUFFER);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_TEXTURE1D);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_TEXTURE2D);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_TEXTURE3D);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_TEXTURECUBE);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_SHADER_LOAD);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_SHADER_SAMPLE);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_SHADER_SAMPLE_COMPARISON);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_SHADER_SAMPLE_MONO_TEXT);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_MIP);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_MIP_AUTOGEN);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_RENDER_TARGET);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_BLENDABLE);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_DEPTH_STENCIL);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_CPU_LOCKABLE);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_DISPLAY);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_CAST_WITHIN_BIT_LAYOUT);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_MULTISAMPLE_LOAD);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_SHADER_GATHER);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_BACK_BUFFER_CAST);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_TYPED_UNORDERED_ACCESS_VIEW);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_SHADER_GATHER_COMPARISON);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_DECODER_OUTPUT);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_OUTPUT);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_VIDEO_PROCESSOR_INPUT);
    ENUM_NAME(D3D11_FORMAT_SUPPORT_VIDEO_ENCODER);
    default: return std::to_string(Flag);
  }
}

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
  Com<ID3D11Device> device;

  if (FAILED(D3D11CreateDevice(
        nullptr, D3D_DRIVER_TYPE_HARDWARE,
        nullptr, 0, nullptr, 0, D3D11_SDK_VERSION,
        &device, nullptr, nullptr))) {
    std::cerr << "Failed to create D3D11 device" << std::endl;
    return 1;
  }

  D3D11_FEATURE_DATA_THREADING                    featureThreading     = { };
  D3D11_FEATURE_DATA_DOUBLES                      featureDoubles       = { };
  D3D11_FEATURE_DATA_SHADER_MIN_PRECISION_SUPPORT featureMinPrecision  = { };
  D3D11_FEATURE_DATA_D3D11_OPTIONS                featureD3D11Options  = { };
  D3D11_FEATURE_DATA_D3D11_OPTIONS1               featureD3D11Options1 = { };
  D3D11_FEATURE_DATA_D3D11_OPTIONS2               featureD3D11Options2 = { };
  D3D11_FEATURE_DATA_D3D11_OPTIONS3               featureD3D11Options3 = { };
  D3D11_FEATURE_DATA_D3D11_OPTIONS4               featureD3D11Options4 = { };

  if (SUCCEEDED(device->CheckFeatureSupport(D3D11_FEATURE_THREADING, &featureThreading, sizeof(featureThreading)))) {
    std::cout << "D3D11_FEATURE_THREADING:" << std::endl
              << "  DriverConcurrentCreates:          " << featureThreading.DriverConcurrentCreates << std::endl
              << "  DriverCommandLists:               " << featureThreading.DriverCommandLists << std::endl;
  }

  if (SUCCEEDED(device->CheckFeatureSupport(D3D11_FEATURE_DOUBLES, &featureDoubles, sizeof(featureDoubles)))) {
    std::cout << "D3D11_FEATURE_DOUBLES:" << std::endl
              << "  DoublePrecisionFloatShaderOps:    " << featureDoubles.DoublePrecisionFloatShaderOps << std::endl;
  }

  if (SUCCEEDED(device->CheckFeatureSupport(D3D11_FEATURE_SHADER_MIN_PRECISION_SUPPORT, &featureMinPrecision, sizeof(featureMinPrecision)))) {
    std::cout << "D3D11_FEATURE_SHADER_MIN_PRECISION_SUPPORT:" << std::endl
              << "  PixelShaderMinPrecision:          " << featureMinPrecision.PixelShaderMinPrecision << std::endl
              << "  AllOtherShaderStagesMinPrecision: " << featureMinPrecision.AllOtherShaderStagesMinPrecision << std::endl;
  }

  if (SUCCEEDED(device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS, &featureD3D11Options, sizeof(featureD3D11Options)))) {
    std::cout << "D3D11_FEATURE_D3D11_OPTIONS:" << std::endl
              << "  OutputMergerLogicOp:              " << featureD3D11Options.OutputMergerLogicOp << std::endl
              << "  UAVOnlyRenderingForcedSampleCount: " << featureD3D11Options.UAVOnlyRenderingForcedSampleCount << std::endl
              << "  DiscardAPIsSeenByDriver:          " << featureD3D11Options.DiscardAPIsSeenByDriver << std::endl
              << "  FlagsForUpdateAndCopySeenByDriver: " << featureD3D11Options.FlagsForUpdateAndCopySeenByDriver << std::endl
              << "  ClearView:                        " << featureD3D11Options.ClearView << std::endl
              << "  CopyWithOverlap:                  " << featureD3D11Options.CopyWithOverlap << std::endl
              << "  ConstantBufferPartialUpdate:      " << featureD3D11Options.ConstantBufferPartialUpdate << std::endl
              << "  ConstantBufferOffsetting:         " << featureD3D11Options.ConstantBufferOffsetting << std::endl
              << "  MapNoOverwriteOnDynamicConstantBuffer: " << featureD3D11Options.MapNoOverwriteOnDynamicConstantBuffer << std::endl
              << "  MapNoOverwriteOnDynamicBufferSRV: " << featureD3D11Options.MapNoOverwriteOnDynamicBufferSRV << std::endl
              << "  MultisampleRTVWithForcedSampleCountOne: " << featureD3D11Options.MultisampleRTVWithForcedSampleCountOne << std::endl
              << "  SAD4ShaderInstructions:           " << featureD3D11Options.SAD4ShaderInstructions << std::endl
              << "  ExtendedDoublesShaderInstructions: " << featureD3D11Options.ExtendedDoublesShaderInstructions << std::endl
              << "  ExtendedResourceSharing:          " << featureD3D11Options.ExtendedResourceSharing << std::endl;
  }

  if (SUCCEEDED(device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS1, &featureD3D11Options1, sizeof(featureD3D11Options1)))) {
    std::cout << "D3D11_FEATURE_D3D11_OPTIONS1:" << std::endl
              << "  TiledResourcesTier:               " << featureD3D11Options1.TiledResourcesTier << std::endl
              << "  MinMaxFiltering:                  " << featureD3D11Options1.MinMaxFiltering << std::endl
              << "  ClearViewAlsoSupportsDepthOnlyFormats: " << featureD3D11Options1.ClearViewAlsoSupportsDepthOnlyFormats << std::endl
              << "  MapOnDefaultBuffers:              " << featureD3D11Options1.MapOnDefaultBuffers << std::endl;

  }

  if (SUCCEEDED(device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS2, &featureD3D11Options2, sizeof(featureD3D11Options2)))) {
    std::cout << "D3D11_FEATURE_D3D11_OPTIONS2:" << std::endl
              << "  PSSpecifiedStencilRefSupported:   " << featureD3D11Options2.PSSpecifiedStencilRefSupported << std::endl
              << "  TypedUAVLoadAdditionalFormats:    " << featureD3D11Options2.TypedUAVLoadAdditionalFormats << std::endl
              << "  ROVsSupported:                    " << featureD3D11Options2.ROVsSupported << std::endl
              << "  ConservativeRasterizationTier:    " << featureD3D11Options2.ConservativeRasterizationTier << std::endl
              << "  MapOnDefaultTextures:             " << featureD3D11Options2.MapOnDefaultTextures << std::endl
              << "  TiledResourcesTier:               " << featureD3D11Options2.TiledResourcesTier << std::endl
              << "  StandardSwizzle:                  " << featureD3D11Options2.StandardSwizzle << std::endl
              << "  UnifiedMemoryArchitecture:        " << featureD3D11Options2.UnifiedMemoryArchitecture << std::endl;
  }

  if (SUCCEEDED(device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS3, &featureD3D11Options3, sizeof(featureD3D11Options3)))) {
    std::cout << "D3D11_FEATURE_D3D11_OPTIONS3:" << std::endl
              << "  VPAndRTArrayIndexFromAnyShaderFeedingRasterizer: " << featureD3D11Options3.VPAndRTArrayIndexFromAnyShaderFeedingRasterizer << std::endl;
  }

  if (SUCCEEDED(device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS4, &featureD3D11Options4, sizeof(featureD3D11Options4)))) {
    std::cout << "D3D11_FEATURE_D3D11_OPTIONS4:" << std::endl
              << "  ExtendedNV12SharedTextureSupported: " << featureD3D11Options4.ExtendedNV12SharedTextureSupported << std::endl;
  }

  for (UINT i  = UINT(DXGI_FORMAT_UNKNOWN);
            i <= UINT(DXGI_FORMAT_BC7_UNORM_SRGB);
            i++) {
    DXGI_FORMAT format = DXGI_FORMAT(i);
    UINT        flags  = 0;

    std::cout << GetFormatName(format) << ": " << std::endl;

    if (SUCCEEDED(device->CheckFormatSupport(format, &flags))) {
      for (uint32_t i = 0; i < 32; i++) {
        if (flags & (1 << i)) {
          std::cout << "  "
                    << GetFormatFlagName(D3D11_FORMAT_SUPPORT(1 << i))
                    << std::endl;
        }
      }

    } else {
      std::cout << "  Not supported" << std::endl;
    }
  }

  return 0;
}


code:
#include <array>
#include <cstring>

#include <d3dcompiler.h>
#include <d3d11.h>

#include <windows.h>
#include <windowsx.h>

#include "../test_utils.h"

using namespace dxvk;

const std::string g_vsCode =
  "float4 main(float4 v_pos : VS_POSITION) : SV_POSITION {\n"
  "  return v_pos;\n"
  "}\n";

Com<ID3D11Device>           g_d3d11Device;
Com<ID3D11DeviceContext>    g_d3d11Context;

Com<ID3D11VertexShader>     g_vertShader;
Com<ID3D11InputLayout>      g_inputLayout;

Com<ID3D11Buffer>           g_vertexBuffer;

Com<ID3D11Texture2D>        g_depthRender;
Com<ID3D11Texture2D>        g_depthRead;
Com<ID3D11DepthStencilView> g_depthView;
Com<ID3D11DepthStencilState>g_depthState;

struct Vertex {
  float x, y, z, w;
};

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
  if (FAILED(D3D11CreateDevice(
        nullptr, D3D_DRIVER_TYPE_HARDWARE,
        nullptr, 0, nullptr, 0, D3D11_SDK_VERSION,
        &g_d3d11Device, nullptr, &g_d3d11Context))) {
    std::cerr << "Failed to create D3D11 device" << std::endl;
    return 1;
  }

  Com<ID3DBlob> vsBlob;
  Com<ID3DBlob> gsBlob;

  if (FAILED(D3DCompile(g_vsCode.data(), g_vsCode.size(),
      "Vertex shader", nullptr, nullptr, "main", "vs_4_0",
      0, 0, &vsBlob, nullptr))) {
    std::cerr << "Failed to compile vertex shader" << std::endl;
    return 1;
  }

  if (FAILED(g_d3d11Device->CreateVertexShader(
      vsBlob->GetBufferPointer(),
      vsBlob->GetBufferSize(),
      nullptr, &g_vertShader))) {
    std::cerr << "Failed to create vertex shader" << std::endl;
    return 1;
  }

  std::array<D3D11_INPUT_ELEMENT_DESC, 1> iaElements = {{
    { "VS_POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
  }};

  if (FAILED(g_d3d11Device->CreateInputLayout(
      iaElements.data(),
      iaElements.size(),
      vsBlob->GetBufferPointer(),
      vsBlob->GetBufferSize(),
      &g_inputLayout))) {
    std::cerr << "Failed to create input layout" << std::endl;
    return 1;
  }

  std::array<Vertex, 4> vertexData = {{
    { -1.0f, -1.0f, 0.00f, 1.0f },
    { -1.0f,  1.0f, 0.66f, 1.0f },
    {  1.0f, -1.0f, 0.33f, 1.0f },
    {  1.0f,  1.0f, 1.00f, 1.0f },
  }};

  D3D11_BUFFER_DESC vertexDesc;
  vertexDesc.ByteWidth           = vertexData.size() * sizeof(Vertex);
  vertexDesc.Usage               = D3D11_USAGE_IMMUTABLE;
  vertexDesc.BindFlags           = D3D11_BIND_VERTEX_BUFFER;
  vertexDesc.CPUAccessFlags      = 0;
  vertexDesc.MiscFlags           = 0;
  vertexDesc.StructureByteStride = 0;

  D3D11_SUBRESOURCE_DATA vertexInfo;
  vertexInfo.pSysMem             = vertexData.data();
  vertexInfo.SysMemPitch         = vertexDesc.ByteWidth;
  vertexInfo.SysMemSlicePitch    = vertexDesc.ByteWidth;

  if (FAILED(g_d3d11Device->CreateBuffer(&vertexDesc, &vertexInfo, &g_vertexBuffer))) {
    std::cerr << "Failed to create vertex buffer" << std::endl;
    return 1;
  }

  D3D11_TEXTURE2D_DESC depthDesc;
  depthDesc.Width           = 16;
  depthDesc.Height          = 16;
  depthDesc.MipLevels       = 1;
  depthDesc.ArraySize       = 1;
  depthDesc.Format          = DXGI_FORMAT_D24_UNORM_S8_UINT;
  // depthDesc.Format          = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
  depthDesc.SampleDesc      = { 1, 0 };
  depthDesc.Usage           = D3D11_USAGE_DEFAULT;
  depthDesc.BindFlags       = D3D11_BIND_DEPTH_STENCIL;
  depthDesc.CPUAccessFlags  = 0;
  depthDesc.MiscFlags       = 0;

  if (FAILED(g_d3d11Device->CreateTexture2D(&depthDesc, nullptr, &g_depthRender))) {
    std::cerr << "Failed to create render buffer" << std::endl;
    return 1;
  }

  depthDesc.Usage           = D3D11_USAGE_STAGING;
  depthDesc.BindFlags       = 0;
  depthDesc.CPUAccessFlags  = D3D11_CPU_ACCESS_READ;

  if (FAILED(g_d3d11Device->CreateTexture2D(&depthDesc, nullptr, &g_depthRead))) {
    std::cerr << "Failed to create readback buffer" << std::endl;
    return 1;
  }

  if (FAILED(g_d3d11Device->CreateDepthStencilView(g_depthRender.ptr(), nullptr, &g_depthView))) {
    std::cerr << "Failed to create depth-stencil view" << std::endl;
    return 1;
  }

  D3D11_DEPTH_STENCIL_DESC dsDesc;
  dsDesc.DepthEnable      = TRUE;
  dsDesc.DepthWriteMask   = D3D11_DEPTH_WRITE_MASK_ALL;
  dsDesc.DepthFunc        = D3D11_COMPARISON_ALWAYS;
  dsDesc.StencilEnable    = FALSE;
  dsDesc.StencilReadMask  = 0;
  dsDesc.StencilWriteMask = 0;
  dsDesc.FrontFace        = { };
  dsDesc.BackFace         = { };

  if (FAILED(g_d3d11Device->CreateDepthStencilState(&dsDesc, &g_depthState))) {
    std::cerr << "Failed to create depth-stencil state" << std::endl;
    return 1;
  }

  FLOAT omBlendFactor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };

  D3D11_VIEWPORT omViewport;
  omViewport.TopLeftX =  0.0f;
  omViewport.TopLeftY =  0.0f;
  omViewport.Width    = 16.0f;
  omViewport.Height   = 16.0f;
  omViewport.MinDepth =  0.0f;
  omViewport.MaxDepth =  1.0f;

  UINT vbOffset = 0;
  UINT vbStride = sizeof(Vertex);

  g_d3d11Context->RSSetState(nullptr);
  g_d3d11Context->RSSetViewports(1, &omViewport);

  g_d3d11Context->OMSetRenderTargets(0, nullptr, g_depthView.ptr());
  g_d3d11Context->OMSetBlendState(nullptr, omBlendFactor, 0xFFFFFFFF);
  g_d3d11Context->OMSetDepthStencilState(g_depthState.ptr(), 0);

  g_d3d11Context->ClearDepthStencilView(g_depthView.ptr(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 0.5f, 0x80);

  g_d3d11Context->IASetInputLayout(g_inputLayout.ptr());
  g_d3d11Context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
  g_d3d11Context->IASetVertexBuffers(0, 1, &g_vertexBuffer, &vbStride, &vbOffset);

  g_d3d11Context->VSSetShader(g_vertShader.ptr(), nullptr, 0);
  g_d3d11Context->Draw(4, 0);

  g_d3d11Context->CopyResource(g_depthRead.ptr(), g_depthRender.ptr());

  D3D11_MAPPED_SUBRESOURCE mapped;

  if (FAILED(g_d3d11Context->Map(g_depthRead.ptr(), 0, D3D11_MAP_READ, 0, &mapped))) {
    std::cerr << "Failed to map image" << std::endl;
    return 1;
  }

  for (uint32_t y = 0; y < 16; y++) {
    auto data = reinterpret_cast<const uint32_t*>(mapped.pData)
      + (y * mapped.RowPitch / 4);

    for (uint32_t x = 0; x < 16; x++)
      std::cout << std::hex << std::setfill('0') << std::setw(8) << data[x] << "  ";

    std::cout << std::endl;
  }

  g_d3d11Context->Unmap(g_depthRead.ptr(), 0);
  g_d3d11Context->ClearState();
  return 0;
}


code:
#include <array>
#include <cstring>

#include <d3dcompiler.h>
#include <d3d11.h>

#include <windows.h>
#include <windowsx.h>

#include "../test_utils.h"

using namespace dxvk;

const std::string g_vsCode =
  "struct VS_IFACE {\n"
  "  float4 pos : VS_POSITION;\n"
  "};\n"
  "VS_IFACE main(VS_IFACE ia_in) {\n"
  "  return ia_in;\n"
  "}\n";

const std::string g_gsCode =
  "struct GS_IN {\n"
  "  float4 pos : VS_POSITION;\n"
  "};\n"
  "struct GS_OUT_NORMAL {\n"
  "  float3 nor : GS_NORMAL;\n"
  "  float  len : GS_LENGTH;\n"
  "};\n"
  "[maxvertexcount(1)]\n"
  "void main(triangle GS_IN vs_in[3], inout PointStream<GS_OUT_NORMAL> o_normals) {\n"
  "  float3 ds1 = vs_in[1].pos.xyz - vs_in[0].pos.xyz;\n"
  "  float3 ds2 = vs_in[2].pos.xyz - vs_in[0].pos.xyz;\n"
  "  float3 cv = cross(ds1, ds2);\n"
  "  float  cl = length(cv);\n"
  "  GS_OUT_NORMAL normal;\n"
  "  normal.nor = cv / cl;\n"
  "  normal.len = cl;"
  "  o_normals.Append(normal);\n"
  "}\n";

Com<ID3D11Device>           g_d3d11Device;
Com<ID3D11DeviceContext>    g_d3d11Context;

Com<ID3D11VertexShader>     g_vertShader;
Com<ID3D11GeometryShader>   g_geomShader;

Com<ID3D11InputLayout>      g_inputLayout;

Com<ID3D11Buffer>           g_vertexBuffer;
Com<ID3D11Buffer>           g_normalBuffer;
Com<ID3D11Buffer>           g_readBuffer;

Com<ID3D11Query>            g_soStream;
Com<ID3D11Query>            g_soOverflow;

struct Vertex {
  float x, y, z, w;
};

struct Normal {
  float x, y, z, len;
};

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow) {
  if (FAILED(D3D11CreateDevice(
        nullptr, D3D_DRIVER_TYPE_HARDWARE,
        nullptr, 0, nullptr, 0, D3D11_SDK_VERSION,
        &g_d3d11Device, nullptr, &g_d3d11Context))) {
    std::cerr << "Failed to create D3D11 device" << std::endl;
    return 1;
  }

  Com<ID3DBlob> vsBlob;
  Com<ID3DBlob> gsBlob;

  if (FAILED(D3DCompile(g_vsCode.data(), g_vsCode.size(),
      "Vertex shader", nullptr, nullptr, "main", "vs_4_0",
      0, 0, &vsBlob, nullptr))) {
    std::cerr << "Failed to compile vertex shader" << std::endl;
    return 1;
  }

  if (FAILED(D3DCompile(g_gsCode.data(), g_gsCode.size(),
      "Geometry shader", nullptr, nullptr, "main", "gs_4_0",
      0, 0, &gsBlob, nullptr))) {
    std::cerr << "Failed to compile geometry shader" << std::endl;
    return 1;
  }

  if (FAILED(g_d3d11Device->CreateVertexShader(
      vsBlob->GetBufferPointer(),
      vsBlob->GetBufferSize(),
      nullptr, &g_vertShader))) {
    std::cerr << "Failed to create vertex shader" << std::endl;
    return 1;
  }

  std::array<D3D11_SO_DECLARATION_ENTRY, 2> soDeclarations = {{
    { 0, "GS_NORMAL", 0, 0, 3, 0 },
    { 0, "GS_LENGTH", 0, 0, 1, 0 },
  }};

  std::array<UINT, 1> soBufferStrides = {{
    sizeof(Normal),
  }};

  if (FAILED(g_d3d11Device->CreateGeometryShaderWithStreamOutput(
      gsBlob->GetBufferPointer(),
      gsBlob->GetBufferSize(),
      soDeclarations.data(),
      soDeclarations.size(),
      soBufferStrides.data(),
      soBufferStrides.size(),
      D3D11_SO_NO_RASTERIZED_STREAM,
      nullptr, &g_geomShader))) {
    std::cerr << "Failed to create geometry shader" << std::endl;
    return 1;
  }

  std::array<D3D11_INPUT_ELEMENT_DESC, 1> iaElements = {{
    { "VS_POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
  }};

  if (FAILED(g_d3d11Device->CreateInputLayout(
      iaElements.data(),
      iaElements.size(),
      vsBlob->GetBufferPointer(),
      vsBlob->GetBufferSize(),
      &g_inputLayout))) {
    std::cerr << "Failed to create input layout" << std::endl;
    return 1;
  }

  std::array<Vertex, 9> vertexData = {{
    { 0.0f, 0.0f, 0.0f, 1.0f },
    { 1.0f, 0.0f, 0.0f, 1.0f },
    { 0.0f, 1.0f, 0.0f, 1.0f },

    { 0.5f,-1.0f,-0.2f, 1.0f },
    { 3.2f, 2.0f, 0.0f, 1.0f },
    {-1.0f,-1.0f, 0.4f, 1.0f },

    { 0.7f,-0.5f,-0.8f, 1.0f },
    { 1.2f, 1.0f,-1.0f, 1.0f },
    {-0.1f, 1.0f,-2.7f, 1.0f },
  }};

  D3D11_BUFFER_DESC vertexDesc;
  vertexDesc.ByteWidth           = vertexData.size() * sizeof(Vertex);
  vertexDesc.Usage               = D3D11_USAGE_IMMUTABLE;
  vertexDesc.BindFlags           = D3D11_BIND_VERTEX_BUFFER;
  vertexDesc.CPUAccessFlags      = 0;
  vertexDesc.MiscFlags           = 0;
  vertexDesc.StructureByteStride = 0;

  D3D11_SUBRESOURCE_DATA vertexInfo;
  vertexInfo.pSysMem             = vertexData.data();
  vertexInfo.SysMemPitch         = vertexDesc.ByteWidth;
  vertexInfo.SysMemSlicePitch    = vertexDesc.ByteWidth;

  if (FAILED(g_d3d11Device->CreateBuffer(&vertexDesc, &vertexInfo, &g_vertexBuffer))) {
    std::cerr << "Failed to create vertex buffer" << std::endl;
    return 1;
  }

  std::array<Normal, 2> normalData = { };

  D3D11_BUFFER_DESC normalDesc;
  normalDesc.ByteWidth           = normalData.size() * sizeof(Normal);
  normalDesc.Usage               = D3D11_USAGE_DEFAULT;
  normalDesc.BindFlags           = D3D11_BIND_STREAM_OUTPUT;
  normalDesc.CPUAccessFlags      = 0;
  normalDesc.MiscFlags           = 0;
  normalDesc.StructureByteStride = 0;

  D3D11_SUBRESOURCE_DATA normalInfo;
  normalInfo.pSysMem             = normalData.data();
  normalInfo.SysMemPitch         = normalDesc.ByteWidth;
  normalInfo.SysMemSlicePitch    = normalDesc.ByteWidth;

  if (FAILED(g_d3d11Device->CreateBuffer(&normalDesc, &normalInfo, &g_normalBuffer))) {
    std::cerr << "Failed to create normal buffer" << std::endl;
    return 1;
  }

  D3D11_BUFFER_DESC readDesc;
  readDesc.ByteWidth           = normalDesc.ByteWidth;
  readDesc.Usage               = D3D11_USAGE_STAGING;
  readDesc.BindFlags           = 0;
  readDesc.CPUAccessFlags      = D3D11_CPU_ACCESS_READ;
  readDesc.MiscFlags           = 0;
  readDesc.StructureByteStride = 0;

  if (FAILED(g_d3d11Device->CreateBuffer(&readDesc, nullptr, &g_readBuffer))) {
    std::cerr << "Failed to create readback buffer" << std::endl;
    return 1;
  }

  D3D11_QUERY_DESC soQueryDesc;
  soQueryDesc.Query = D3D11_QUERY_SO_STATISTICS_STREAM0;
  soQueryDesc.MiscFlags = 0;

  if (FAILED(g_d3d11Device->CreateQuery(&soQueryDesc, &g_soStream))) {
    std::cerr << "Failed to create streamout query" << std::endl;
    return 1;
  }

  soQueryDesc.Query = D3D11_QUERY_SO_OVERFLOW_PREDICATE_STREAM0;
  if (FAILED(g_d3d11Device->CreateQuery(&soQueryDesc, &g_soOverflow))) {
    std::cerr << "Failed to create streamout overflow query" << std::endl;
    return 1;
  }

  UINT soOffset = 0;
  UINT vbOffset = 0;
  UINT vbStride = sizeof(Vertex);

  FLOAT omBlendFactor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };

  D3D11_VIEWPORT omViewport;
  omViewport.TopLeftX =   0.0f;
  omViewport.TopLeftY =   0.0f;
  omViewport.Width    = 256.0f;
  omViewport.Height   = 256.0f;
  omViewport.MinDepth =   0.0f;
  omViewport.MaxDepth =   1.0f;

  g_d3d11Context->IASetInputLayout(g_inputLayout.ptr());
  g_d3d11Context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
  g_d3d11Context->IASetVertexBuffers(0, 1, &g_vertexBuffer, &vbStride, &vbOffset);

  g_d3d11Context->RSSetState(nullptr);
  g_d3d11Context->RSSetViewports(1, &omViewport);

  g_d3d11Context->OMSetRenderTargets(0, nullptr, nullptr);
  g_d3d11Context->OMSetBlendState(nullptr, omBlendFactor, 0xFFFFFFFF);
  g_d3d11Context->OMSetDepthStencilState(nullptr, 0);

  g_d3d11Context->SOSetTargets(1, &g_normalBuffer, &soOffset);

  g_d3d11Context->VSSetShader(g_vertShader.ptr(), nullptr, 0);
  g_d3d11Context->GSSetShader(g_geomShader.ptr(), nullptr, 0);

  g_d3d11Context->Begin(g_soStream.ptr());
  g_d3d11Context->Begin(g_soOverflow.ptr());

  g_d3d11Context->Draw(vertexData.size(), 0);

  g_d3d11Context->End(g_soOverflow.ptr());
  g_d3d11Context->End(g_soStream.ptr());

  g_d3d11Context->CopyResource(
    g_readBuffer.ptr(),
    g_normalBuffer.ptr());

  D3D11_QUERY_DATA_SO_STATISTICS soQueryData = { };
  BOOL soOverflowData = false;

  while (g_d3d11Context->GetData(g_soStream.ptr(), &soQueryData, sizeof(soQueryData), 0) != S_OK
      || g_d3d11Context->GetData(g_soOverflow.ptr(), &soOverflowData, sizeof(soOverflowData), 0) != S_OK)
    continue;

  std::cout << "Written:  " << soQueryData.NumPrimitivesWritten << std::endl;
  std::cout << "Needed:   " << soQueryData.PrimitivesStorageNeeded << std::endl;
  std::cout << "Overflow: " << (soOverflowData ? "Yes" : "No") << std::endl;

  D3D11_MAPPED_SUBRESOURCE mapInfo;

  if (FAILED(g_d3d11Context->Map(g_readBuffer.ptr(), 0, D3D11_MAP_READ, 0, &mapInfo))) {
    std::cerr << "Failed to map readback buffer" << std::endl;
    return 1;
  }

  std::memcpy(normalData.data(), mapInfo.pData, normalDesc.ByteWidth);
  g_d3d11Context->Unmap(g_readBuffer.ptr(), 0);

  for (uint32_t i = 0; i < normalData.size(); i++) {
    std::cout << i << ": " << normalData[i].x << ","
      << normalData[i].y << "," << normalData[i].z << ","
      << normalData[i].len << std::endl;
  }

  return 0;
}