Commit 0cb66542 authored by Roderick Colenbrander's avatar Roderick Colenbrander Committed by Alexandre Julliard

winevulkan/winex11: Add Vulkan WSI stubs (VK_KHR_surface / VK_KHR_swapchain).

The WSI APIs are meant for window system integration, so essentially the WGL of Vulkan. Implementation belongs in the graphics drivers as we need to hook into surface and swapchains. Signed-off-by: 's avatarRoderick Colenbrander <thunderbird2k@gmail.com> Signed-off-by: 's avatarJózef Kucia <jkucia@codeweavers.com> Signed-off-by: 's avatarAlexandre Julliard <julliard@winehq.org>
parent c30bf0b6
...@@ -68,10 +68,27 @@ WINE_VULKAN_DRIVER_H = "../../include/wine/vulkan_driver.h" ...@@ -68,10 +68,27 @@ WINE_VULKAN_DRIVER_H = "../../include/wine/vulkan_driver.h"
WINE_VULKAN_THUNKS_C = "vulkan_thunks.c" WINE_VULKAN_THUNKS_C = "vulkan_thunks.c"
WINE_VULKAN_THUNKS_H = "vulkan_thunks.h" WINE_VULKAN_THUNKS_H = "vulkan_thunks.h"
# Extension enum values start at a certain offset (EXT_BASE).
# Relative to the offset each extension has a block (EXT_BLOCK_SIZE)
# of values.
# Start for a given extension is:
# EXT_BASE + (extension_number-1) * EXT_BLOCK_SIZE
EXT_BASE = 1000000000
EXT_BLOCK_SIZE = 1000
# In general instance extensions can't be automatically generated
# and need custom wrappers due to e.g. win32 / X11 specific code.
# List of supported instance extensions.
SUPPORTED_EXTENSIONS = [
"VK_KHR_surface",
"VK_KHR_win32_surface",
"VK_KHR_swapchain",
]
# Functions part of our winevulkan graphics driver interface. # Functions part of our winevulkan graphics driver interface.
# DRIVER_VERSION should be bumped on any change to driver interface # DRIVER_VERSION should be bumped on any change to driver interface
# in FUNCTION_OVERRIDES # in FUNCTION_OVERRIDES
DRIVER_VERSION = 2 DRIVER_VERSION = 3
# Table of functions for which we have a special implementation. # Table of functions for which we have a special implementation.
# This are regular device / instance functions for which we need # This are regular device / instance functions for which we need
...@@ -96,6 +113,24 @@ FUNCTION_OVERRIDES = { ...@@ -96,6 +113,24 @@ FUNCTION_OVERRIDES = {
# Device functions # Device functions
"vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : False}, "vkDestroyDevice" : {"dispatch" : True, "driver" : False, "thunk" : False},
"vkGetDeviceProcAddr" : {"dispatch" : True, "driver" : True, "thunk" : False}, "vkGetDeviceProcAddr" : {"dispatch" : True, "driver" : True, "thunk" : False},
# VK_KHR_surface
"vkDestroySurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
"vkGetPhysicalDeviceSurfaceSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
"vkGetPhysicalDeviceSurfaceFormatsKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
"vkGetPhysicalDeviceSurfacePresentModesKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
# VK_KHR_win32_surface
"vkCreateWin32SurfaceKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
"vkGetPhysicalDeviceWin32PresentationSupportKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
# VK_KHR_swapchain
"vkAcquireNextImageKHR": {"dispatch" : True, "driver" : True, "thunk" : False},
"vkCreateSwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
"vkDestroySwapchainKHR" : {"dispatch" : True, "driver" : True, "thunk" : False},
"vkGetSwapchainImagesKHR": {"dispatch" : True, "driver" : True, "thunk" : False},
"vkQueuePresentKHR": {"dispatch" : True, "driver" : True, "thunk" : False},
} }
...@@ -356,6 +391,11 @@ class VkFunction(object): ...@@ -356,6 +391,11 @@ class VkFunction(object):
def needs_stub(self): def needs_stub(self):
""" Temporary function to limit script hacks until more code is implemented. """ """ Temporary function to limit script hacks until more code is implemented. """
# Temporary hack to pull in VkSwapChainCreateInfoKHR.
if self.name == "vkCreateSwapchainKHR":
return False
if self.params[0].type != "VkPhysicalDevice": if self.params[0].type != "VkPhysicalDevice":
return True return True
...@@ -1997,6 +2037,7 @@ class VkRegistry(object): ...@@ -1997,6 +2037,7 @@ class VkRegistry(object):
# Pull in any required types and functions. # Pull in any required types and functions.
self._parse_features(root) self._parse_features(root)
self._parse_extensions(root)
def _mark_command_required(self, command): def _mark_command_required(self, command):
""" Helper function to mark a certain command and the datatypes it needs as required.""" """ Helper function to mark a certain command and the datatypes it needs as required."""
...@@ -2099,6 +2140,77 @@ class VkRegistry(object): ...@@ -2099,6 +2140,77 @@ class VkRegistry(object):
self.enums = OrderedDict(sorted(enums.items())) self.enums = OrderedDict(sorted(enums.items()))
def _parse_extensions(self, root):
""" Parse extensions section and pull in any types and commands for this extensioin. """
extensions = []
exts = root.findall("./extensions/extension")
for ext in exts:
ext_name = ext.attrib["name"]
# Some extensions are not ready or have numbers reserved as a place holder.
if ext.attrib["supported"] == "disabled":
LOGGER.debug("Skipping disabled extension: {0}".format(ext_name))
continue
# We only support a handful of extensions for now through a whitelist.
if ext_name not in SUPPORTED_EXTENSIONS:
LOGGER.debug("Skipping blacklisted extension: {0}".format(ext_name))
continue
LOGGER.debug("Loading extension: {0}".format(ext_name))
# Extensions can add enum values to Core / extension enums, so add these.
enums = ext.findall("require/enum")
for enum_elem in enums:
if "bitpos" in enum_elem.keys():
# We need to add an extra value to an existing enum type.
# E.g. VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG to VkFormatFeatureFlagBits.
type_name = enum_elem.attrib["extends"]
enum = self.types[type_name]["data"]
enum.add(VkEnumValue(enum_elem.attrib["name"], 1 << int(enum_elem.attrib["bitpos"]), hex=True))
elif "offset" in enum_elem.keys():
ext_number = int(ext.attrib["number"])
offset = int(enum_elem.attrib["offset"])
value = EXT_BASE + (ext_number - 1) * EXT_BLOCK_SIZE + offset
# Deal with negative values.
direction = enum_elem.attrib.get("dir")
if direction is not None:
value = -value
type_name = enum_elem.attrib["extends"]
enum = self.types[type_name]["data"]
enum.add(VkEnumValue(enum_elem.attrib["name"], value))
elif "value" in enum_elem.keys():
# For now skip, it mostly contains extension name and version info.
continue
else:
# This seems to be used to pull in constants e.g. VK_MAX_DEVICE_GROUP_KHX
continue
# Store a list with extensions.
ext_type = ext.attrib["type"]
ext_info = {"name" : ext_name, "type" : ext_type}
extensions.append(ext_info)
commands = ext.findall("require/command")
if not commands:
continue
# Pull in any commands we need. We infer types to pull in from the command
# as well.
for command in commands:
cmd_name = command.attrib["name"]
self._mark_command_required(cmd_name)
# Set extension name on the function call as we were not aware of the
# name during initial parsing.
self.funcs[cmd_name].extension = ext_name
# Sort in alphabetical order.
self.extensions = sorted(extensions, key=lambda ext: ext["name"])
def _parse_features(self, root): def _parse_features(self, root):
""" Parse the feature section, which describes Core commands and types needed. """ """ Parse the feature section, which describes Core commands and types needed. """
requires = root.findall("./feature/require") requires = root.findall("./feature/require")
......
...@@ -162,6 +162,17 @@ static void wine_vk_instance_free(struct VkInstance_T *instance) ...@@ -162,6 +162,17 @@ static void wine_vk_instance_free(struct VkInstance_T *instance)
heap_free(instance); heap_free(instance);
} }
VkResult WINAPI wine_vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain,
uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t *image_index)
{
TRACE("%p, 0x%s, 0x%s, 0x%s, 0x%s, %p\n", device, wine_dbgstr_longlong(swapchain),
wine_dbgstr_longlong(timeout), wine_dbgstr_longlong(semaphore),
wine_dbgstr_longlong(fence), image_index);
return vk_funcs->p_vkAcquireNextImageKHR(device->device, swapchain, timeout,
semaphore, fence, image_index);
}
VkResult WINAPI wine_vkCreateDevice(VkPhysicalDevice phys_dev, VkResult WINAPI wine_vkCreateDevice(VkPhysicalDevice phys_dev,
const VkDeviceCreateInfo *create_info, const VkDeviceCreateInfo *create_info,
const VkAllocationCallbacks *allocator, VkDevice *device) const VkAllocationCallbacks *allocator, VkDevice *device)
...@@ -271,6 +282,72 @@ err: ...@@ -271,6 +282,72 @@ err:
return res; return res;
} }
#if defined(USE_STRUCT_CONVERSION)
static inline void convert_VkSwapchainCreateInfoKHR_win_to_host(const VkSwapchainCreateInfoKHR *in,
VkSwapchainCreateInfoKHR_host *out)
{
if (!in) return;
out->sType = in->sType;
out->pNext = in->pNext;
out->flags = in->flags;
out->surface = in->surface;
out->minImageCount = in->minImageCount;
out->imageFormat = in->imageFormat;
out->imageColorSpace = in->imageColorSpace;
out->imageExtent = in->imageExtent;
out->imageArrayLayers = in->imageArrayLayers;
out->imageUsage = in->imageUsage;
out->imageSharingMode = in->imageSharingMode;
out->queueFamilyIndexCount = in->queueFamilyIndexCount;
out->pQueueFamilyIndices = in->pQueueFamilyIndices;
out->preTransform = in->preTransform;
out->compositeAlpha = in->compositeAlpha;
out->presentMode = in->presentMode;
out->clipped = in->clipped;
out->oldSwapchain = in->oldSwapchain;
}
#endif
VkResult WINAPI wine_vkCreateSwapchainKHR(VkDevice device,
const VkSwapchainCreateInfoKHR *create_info,
const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain)
{
#if defined(USE_STRUCT_CONVERSION)
VkSwapchainCreateInfoKHR_host create_info_host;
TRACE("%p %p %p %p\n", device, create_info, allocator, swapchain);
if (allocator)
FIXME("Support allocation allocators\n");
convert_VkSwapchainCreateInfoKHR_win_to_host(create_info, &create_info_host);
/* Wine graphics driver only uses structs in host format. */
return vk_funcs->p_vkCreateSwapchainKHR(device->device,
(VkSwapchainCreateInfoKHR *)&create_info_host, allocator, swapchain);
#else
TRACE("%p %p %p %p\n", device, create_info, allocator, swapchain);
if (allocator)
FIXME("Support allocation allocators\n");
return vk_funcs->p_vkCreateSwapchainKHR(device->device, create_info, allocator, swapchain);
#endif
}
VkResult WINAPI wine_vkCreateWin32SurfaceKHR(VkInstance instance,
const VkWin32SurfaceCreateInfoKHR *create_info,
const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface)
{
TRACE("%p %p %p %p\n", instance, create_info, allocator, surface);
if (allocator)
FIXME("Support allocation allocators\n");
return vk_funcs->p_vkCreateWin32SurfaceKHR(instance->instance, create_info,
NULL /* allocator */, surface);
}
void WINAPI wine_vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *allocator) void WINAPI wine_vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *allocator)
{ {
TRACE("%p %p\n", device, allocator); TRACE("%p %p\n", device, allocator);
...@@ -291,6 +368,28 @@ void WINAPI wine_vkDestroyInstance(VkInstance instance, const VkAllocationCallba ...@@ -291,6 +368,28 @@ void WINAPI wine_vkDestroyInstance(VkInstance instance, const VkAllocationCallba
wine_vk_instance_free(instance); wine_vk_instance_free(instance);
} }
void WINAPI wine_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface,
const VkAllocationCallbacks *allocator)
{
TRACE("%p, 0x%s, %p\n", instance, wine_dbgstr_longlong(surface), allocator);
if (allocator)
FIXME("Support allocation allocators\n");
vk_funcs->p_vkDestroySurfaceKHR(instance->instance, surface, NULL /* allocator */);
}
void WINAPI wine_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain,
const VkAllocationCallbacks *allocator)
{
TRACE("%p, 0x%s %p\n", device, wine_dbgstr_longlong(swapchain), allocator);
if (allocator)
FIXME("Support allocation allocators\n");
vk_funcs->p_vkDestroySwapchainKHR(device->device, swapchain, NULL /* allocator */);
}
VkResult WINAPI wine_vkEnumerateDeviceExtensionProperties(VkPhysicalDevice phys_dev, VkResult WINAPI wine_vkEnumerateDeviceExtensionProperties(VkPhysicalDevice phys_dev,
const char *layer_name, uint32_t *count, VkExtensionProperties *properties) const char *layer_name, uint32_t *count, VkExtensionProperties *properties)
{ {
...@@ -418,6 +517,60 @@ static PFN_vkVoidFunction WINAPI wine_vkGetInstanceProcAddr(VkInstance instance, ...@@ -418,6 +517,60 @@ static PFN_vkVoidFunction WINAPI wine_vkGetInstanceProcAddr(VkInstance instance,
return NULL; return NULL;
} }
VkResult WINAPI wine_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice phys_dev,
VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR *capabilities)
{
TRACE("%p, 0x%s, %p\n", phys_dev, wine_dbgstr_longlong(surface), capabilities);
return vk_funcs->p_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(phys_dev->phys_dev,
surface, capabilities);
}
VkResult WINAPI wine_vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice phys_dev,
VkSurfaceKHR surface, uint32_t *format_count, VkSurfaceFormatKHR *formats)
{
TRACE("%p, 0x%s, %p, %p\n", phys_dev, wine_dbgstr_longlong(surface), format_count, formats);
return vk_funcs->p_vkGetPhysicalDeviceSurfaceFormatsKHR(phys_dev->phys_dev,
surface, format_count, formats);
}
VkResult WINAPI wine_vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice phys_dev,
VkSurfaceKHR surface, uint32_t *mode_count, VkPresentModeKHR *modes)
{
TRACE("%p, 0x%s, %p, %p\n", phys_dev, wine_dbgstr_longlong(surface), mode_count, modes);
return vk_funcs->p_vkGetPhysicalDeviceSurfacePresentModesKHR(phys_dev->phys_dev,
surface, mode_count, modes);
}
VkResult WINAPI wine_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice phys_dev,
uint32_t queue_family_index, VkSurfaceKHR surface, VkBool32 *supported)
{
TRACE("%p, %u, 0x%s, %p\n", phys_dev, queue_family_index, wine_dbgstr_longlong(surface), supported);
return vk_funcs->p_vkGetPhysicalDeviceSurfaceSupportKHR(phys_dev->phys_dev,
queue_family_index, surface, supported);
}
VkBool32 WINAPI wine_vkGetPhysicalDeviceWin32PresentationSupportKHR(VkPhysicalDevice phys_dev,
uint32_t queue_family_index)
{
TRACE("%p %u\n", phys_dev, queue_family_index);
return vk_funcs->p_vkGetPhysicalDeviceWin32PresentationSupportKHR(phys_dev->phys_dev,
queue_family_index);
}
VkResult WINAPI wine_vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain,
uint32_t *image_count, VkImage *images)
{
TRACE("%p, 0x%s %p %p\n", device, wine_dbgstr_longlong(swapchain), image_count, images);
return vk_funcs->p_vkGetSwapchainImagesKHR(device->device, swapchain,
image_count, images);
}
VkResult WINAPI wine_vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *present_info)
{
FIXME("stub: %p, %p\n", queue, present_info);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
void * WINAPI wine_vk_icdGetInstanceProcAddr(VkInstance instance, const char *name) void * WINAPI wine_vk_icdGetInstanceProcAddr(VkInstance instance, const char *name)
{ {
TRACE("%p %s\n", instance, debugstr_a(name)); TRACE("%p %s\n", instance, debugstr_a(name));
......
...@@ -895,6 +895,7 @@ static VkResult WINAPI wine_vkWaitForFences(VkDevice device, uint32_t fenceCount ...@@ -895,6 +895,7 @@ static VkResult WINAPI wine_vkWaitForFences(VkDevice device, uint32_t fenceCount
static const struct vulkan_func vk_device_dispatch_table[] = static const struct vulkan_func vk_device_dispatch_table[] =
{ {
{"vkAcquireNextImageKHR", &wine_vkAcquireNextImageKHR},
{"vkAllocateCommandBuffers", &wine_vkAllocateCommandBuffers}, {"vkAllocateCommandBuffers", &wine_vkAllocateCommandBuffers},
{"vkAllocateDescriptorSets", &wine_vkAllocateDescriptorSets}, {"vkAllocateDescriptorSets", &wine_vkAllocateDescriptorSets},
{"vkAllocateMemory", &wine_vkAllocateMemory}, {"vkAllocateMemory", &wine_vkAllocateMemory},
...@@ -964,6 +965,7 @@ static const struct vulkan_func vk_device_dispatch_table[] = ...@@ -964,6 +965,7 @@ static const struct vulkan_func vk_device_dispatch_table[] =
{"vkCreateSampler", &wine_vkCreateSampler}, {"vkCreateSampler", &wine_vkCreateSampler},
{"vkCreateSemaphore", &wine_vkCreateSemaphore}, {"vkCreateSemaphore", &wine_vkCreateSemaphore},
{"vkCreateShaderModule", &wine_vkCreateShaderModule}, {"vkCreateShaderModule", &wine_vkCreateShaderModule},
{"vkCreateSwapchainKHR", &wine_vkCreateSwapchainKHR},
{"vkDestroyBuffer", &wine_vkDestroyBuffer}, {"vkDestroyBuffer", &wine_vkDestroyBuffer},
{"vkDestroyBufferView", &wine_vkDestroyBufferView}, {"vkDestroyBufferView", &wine_vkDestroyBufferView},
{"vkDestroyCommandPool", &wine_vkDestroyCommandPool}, {"vkDestroyCommandPool", &wine_vkDestroyCommandPool},
...@@ -983,6 +985,7 @@ static const struct vulkan_func vk_device_dispatch_table[] = ...@@ -983,6 +985,7 @@ static const struct vulkan_func vk_device_dispatch_table[] =
{"vkDestroySampler", &wine_vkDestroySampler}, {"vkDestroySampler", &wine_vkDestroySampler},
{"vkDestroySemaphore", &wine_vkDestroySemaphore}, {"vkDestroySemaphore", &wine_vkDestroySemaphore},
{"vkDestroyShaderModule", &wine_vkDestroyShaderModule}, {"vkDestroyShaderModule", &wine_vkDestroyShaderModule},
{"vkDestroySwapchainKHR", &wine_vkDestroySwapchainKHR},
{"vkDeviceWaitIdle", &wine_vkDeviceWaitIdle}, {"vkDeviceWaitIdle", &wine_vkDeviceWaitIdle},
{"vkEndCommandBuffer", &wine_vkEndCommandBuffer}, {"vkEndCommandBuffer", &wine_vkEndCommandBuffer},
{"vkFlushMappedMemoryRanges", &wine_vkFlushMappedMemoryRanges}, {"vkFlushMappedMemoryRanges", &wine_vkFlushMappedMemoryRanges},
...@@ -1001,10 +1004,12 @@ static const struct vulkan_func vk_device_dispatch_table[] = ...@@ -1001,10 +1004,12 @@ static const struct vulkan_func vk_device_dispatch_table[] =
{"vkGetPipelineCacheData", &wine_vkGetPipelineCacheData}, {"vkGetPipelineCacheData", &wine_vkGetPipelineCacheData},
{"vkGetQueryPoolResults", &wine_vkGetQueryPoolResults}, {"vkGetQueryPoolResults", &wine_vkGetQueryPoolResults},
{"vkGetRenderAreaGranularity", &wine_vkGetRenderAreaGranularity}, {"vkGetRenderAreaGranularity", &wine_vkGetRenderAreaGranularity},
{"vkGetSwapchainImagesKHR", &wine_vkGetSwapchainImagesKHR},
{"vkInvalidateMappedMemoryRanges", &wine_vkInvalidateMappedMemoryRanges}, {"vkInvalidateMappedMemoryRanges", &wine_vkInvalidateMappedMemoryRanges},
{"vkMapMemory", &wine_vkMapMemory}, {"vkMapMemory", &wine_vkMapMemory},
{"vkMergePipelineCaches", &wine_vkMergePipelineCaches}, {"vkMergePipelineCaches", &wine_vkMergePipelineCaches},
{"vkQueueBindSparse", &wine_vkQueueBindSparse}, {"vkQueueBindSparse", &wine_vkQueueBindSparse},
{"vkQueuePresentKHR", &wine_vkQueuePresentKHR},
{"vkQueueSubmit", &wine_vkQueueSubmit}, {"vkQueueSubmit", &wine_vkQueueSubmit},
{"vkQueueWaitIdle", &wine_vkQueueWaitIdle}, {"vkQueueWaitIdle", &wine_vkQueueWaitIdle},
{"vkResetCommandBuffer", &wine_vkResetCommandBuffer}, {"vkResetCommandBuffer", &wine_vkResetCommandBuffer},
...@@ -1021,7 +1026,9 @@ static const struct vulkan_func vk_device_dispatch_table[] = ...@@ -1021,7 +1026,9 @@ static const struct vulkan_func vk_device_dispatch_table[] =
static const struct vulkan_func vk_instance_dispatch_table[] = static const struct vulkan_func vk_instance_dispatch_table[] =
{ {
{"vkCreateDevice", &wine_vkCreateDevice}, {"vkCreateDevice", &wine_vkCreateDevice},
{"vkCreateWin32SurfaceKHR", &wine_vkCreateWin32SurfaceKHR},
{"vkDestroyInstance", &wine_vkDestroyInstance}, {"vkDestroyInstance", &wine_vkDestroyInstance},
{"vkDestroySurfaceKHR", &wine_vkDestroySurfaceKHR},
{"vkEnumerateDeviceExtensionProperties", &wine_vkEnumerateDeviceExtensionProperties}, {"vkEnumerateDeviceExtensionProperties", &wine_vkEnumerateDeviceExtensionProperties},
{"vkEnumerateDeviceLayerProperties", &wine_vkEnumerateDeviceLayerProperties}, {"vkEnumerateDeviceLayerProperties", &wine_vkEnumerateDeviceLayerProperties},
{"vkEnumeratePhysicalDevices", &wine_vkEnumeratePhysicalDevices}, {"vkEnumeratePhysicalDevices", &wine_vkEnumeratePhysicalDevices},
...@@ -1032,6 +1039,11 @@ static const struct vulkan_func vk_instance_dispatch_table[] = ...@@ -1032,6 +1039,11 @@ static const struct vulkan_func vk_instance_dispatch_table[] =
{"vkGetPhysicalDeviceProperties", &wine_vkGetPhysicalDeviceProperties}, {"vkGetPhysicalDeviceProperties", &wine_vkGetPhysicalDeviceProperties},
{"vkGetPhysicalDeviceQueueFamilyProperties", &wine_vkGetPhysicalDeviceQueueFamilyProperties}, {"vkGetPhysicalDeviceQueueFamilyProperties", &wine_vkGetPhysicalDeviceQueueFamilyProperties},
{"vkGetPhysicalDeviceSparseImageFormatProperties", &wine_vkGetPhysicalDeviceSparseImageFormatProperties}, {"vkGetPhysicalDeviceSparseImageFormatProperties", &wine_vkGetPhysicalDeviceSparseImageFormatProperties},
{"vkGetPhysicalDeviceSurfaceCapabilitiesKHR", &wine_vkGetPhysicalDeviceSurfaceCapabilitiesKHR},
{"vkGetPhysicalDeviceSurfaceFormatsKHR", &wine_vkGetPhysicalDeviceSurfaceFormatsKHR},
{"vkGetPhysicalDeviceSurfacePresentModesKHR", &wine_vkGetPhysicalDeviceSurfacePresentModesKHR},
{"vkGetPhysicalDeviceSurfaceSupportKHR", &wine_vkGetPhysicalDeviceSurfaceSupportKHR},
{"vkGetPhysicalDeviceWin32PresentationSupportKHR", &wine_vkGetPhysicalDeviceWin32PresentationSupportKHR},
}; };
void *wine_vk_get_device_proc_addr(const char *name) void *wine_vk_get_device_proc_addr(const char *name)
......
...@@ -13,12 +13,46 @@ void *wine_vk_get_device_proc_addr(const char *name) DECLSPEC_HIDDEN; ...@@ -13,12 +13,46 @@ void *wine_vk_get_device_proc_addr(const char *name) DECLSPEC_HIDDEN;
void *wine_vk_get_instance_proc_addr(const char *name) DECLSPEC_HIDDEN; void *wine_vk_get_instance_proc_addr(const char *name) DECLSPEC_HIDDEN;
/* Functions for which we have custom implementations outside of the thunks. */ /* Functions for which we have custom implementations outside of the thunks. */
VkResult WINAPI wine_vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t *pImageIndex) DECLSPEC_HIDDEN;
VkResult WINAPI wine_vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) DECLSPEC_HIDDEN; VkResult WINAPI wine_vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) DECLSPEC_HIDDEN;
VkResult WINAPI wine_vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain) DECLSPEC_HIDDEN;
VkResult WINAPI wine_vkCreateWin32SurfaceKHR(VkInstance instance, const VkWin32SurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) DECLSPEC_HIDDEN;
void WINAPI wine_vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) DECLSPEC_HIDDEN; void WINAPI wine_vkDestroyDevice(VkDevice device, const VkAllocationCallbacks *pAllocator) DECLSPEC_HIDDEN;
void WINAPI wine_vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator) DECLSPEC_HIDDEN; void WINAPI wine_vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks *pAllocator) DECLSPEC_HIDDEN;
void WINAPI wine_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks *pAllocator) DECLSPEC_HIDDEN;
void WINAPI wine_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks *pAllocator) DECLSPEC_HIDDEN;
VkResult WINAPI wine_vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) DECLSPEC_HIDDEN; VkResult WINAPI wine_vkEnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) DECLSPEC_HIDDEN;
VkResult WINAPI wine_vkEnumeratePhysicalDevices(VkInstance instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices) DECLSPEC_HIDDEN; VkResult WINAPI wine_vkEnumeratePhysicalDevices(VkInstance instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices) DECLSPEC_HIDDEN;
PFN_vkVoidFunction WINAPI wine_vkGetDeviceProcAddr(VkDevice device, const char *pName) DECLSPEC_HIDDEN; PFN_vkVoidFunction WINAPI wine_vkGetDeviceProcAddr(VkDevice device, const char *pName) DECLSPEC_HIDDEN;
VkResult WINAPI wine_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) DECLSPEC_HIDDEN;
VkResult WINAPI wine_vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pSurfaceFormatCount, VkSurfaceFormatKHR *pSurfaceFormats) DECLSPEC_HIDDEN;
VkResult WINAPI wine_vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pPresentModeCount, VkPresentModeKHR *pPresentModes) DECLSPEC_HIDDEN;
VkResult WINAPI wine_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32 *pSupported) DECLSPEC_HIDDEN;
VkBool32 WINAPI wine_vkGetPhysicalDeviceWin32PresentationSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex) DECLSPEC_HIDDEN;
VkResult WINAPI wine_vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t *pSwapchainImageCount, VkImage *pSwapchainImages) DECLSPEC_HIDDEN;
VkResult WINAPI wine_vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo) DECLSPEC_HIDDEN;
typedef struct VkSwapchainCreateInfoKHR_host
{
VkStructureType sType;
const void *pNext;
VkSwapchainCreateFlagsKHR flags;
VkSurfaceKHR surface;
uint32_t minImageCount;
VkFormat imageFormat;
VkColorSpaceKHR imageColorSpace;
VkExtent2D imageExtent;
uint32_t imageArrayLayers;
VkImageUsageFlags imageUsage;
VkSharingMode imageSharingMode;
uint32_t queueFamilyIndexCount;
const uint32_t *pQueueFamilyIndices;
VkSurfaceTransformFlagBitsKHR preTransform;
VkCompositeAlphaFlagBitsKHR compositeAlpha;
VkPresentModeKHR presentMode;
VkBool32 clipped;
VkSwapchainKHR oldSwapchain;
} VkSwapchainCreateInfoKHR_host;
typedef struct VkImageFormatProperties_host typedef struct VkImageFormatProperties_host
{ {
......
...@@ -27,6 +27,10 @@ ...@@ -27,6 +27,10 @@
#include "wine/debug.h" #include "wine/debug.h"
#include "wine/library.h" #include "wine/library.h"
/* We only want host compatible structures and don't need alignment. */
#define WINE_VK_ALIGN(x)
#include "wine/vulkan.h" #include "wine/vulkan.h"
#include "wine/vulkan_driver.h" #include "wine/vulkan_driver.h"
...@@ -34,11 +38,22 @@ ...@@ -34,11 +38,22 @@
WINE_DEFAULT_DEBUG_CHANNEL(vulkan); WINE_DEFAULT_DEBUG_CHANNEL(vulkan);
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
#endif
static VkResult (*pvkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); static VkResult (*pvkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *);
static void (*pvkDestroyInstance)(VkInstance, const VkAllocationCallbacks *); static void (*pvkDestroyInstance)(VkInstance, const VkAllocationCallbacks *);
static void * (*pvkGetDeviceProcAddr)(VkDevice, const char *); static void * (*pvkGetDeviceProcAddr)(VkDevice, const char *);
static void * (*pvkGetInstanceProcAddr)(VkInstance, const char *); static void * (*pvkGetInstanceProcAddr)(VkInstance, const char *);
/* TODO: dynamically generate based on host driver capabilities. */
static const struct VkExtensionProperties winex11_vk_instance_extensions[] =
{
{ "VK_KHR_surface", 1 },
{ "VK_KHR_win32_surface", 1},
};
static BOOL wine_vk_init(void) static BOOL wine_vk_init(void)
{ {
static BOOL init_done = FALSE; static BOOL init_done = FALSE;
...@@ -59,6 +74,16 @@ LOAD_FUNCPTR(vkGetInstanceProcAddr) ...@@ -59,6 +74,16 @@ LOAD_FUNCPTR(vkGetInstanceProcAddr)
return TRUE; return TRUE;
} }
static VkResult X11DRV_vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain,
uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t *index)
{
FIXME("stub: %p, 0x%s, 0x%s, 0x%s, 0x%s, %p\n", device,
wine_dbgstr_longlong(swapchain), wine_dbgstr_longlong(timeout),
wine_dbgstr_longlong(semaphore), wine_dbgstr_longlong(fence), index);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
static VkResult X11DRV_vkCreateInstance(const VkInstanceCreateInfo *create_info, static VkResult X11DRV_vkCreateInstance(const VkInstanceCreateInfo *create_info,
const VkAllocationCallbacks *allocator, VkInstance *instance) const VkAllocationCallbacks *allocator, VkInstance *instance)
{ {
...@@ -77,6 +102,22 @@ static VkResult X11DRV_vkCreateInstance(const VkInstanceCreateInfo *create_info, ...@@ -77,6 +102,22 @@ static VkResult X11DRV_vkCreateInstance(const VkInstanceCreateInfo *create_info,
return pvkCreateInstance(create_info, NULL /* allocator */, instance); return pvkCreateInstance(create_info, NULL /* allocator */, instance);
} }
static VkResult X11DRV_vkCreateSwapchainKHR(VkDevice device,
const VkSwapchainCreateInfoKHR *create_info,
const VkAllocationCallbacks *allocator, VkSwapchainKHR *swapchain)
{
FIXME("stub: %p %p %p %p\n", device, create_info, allocator, swapchain);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
static VkResult X11DRV_vkCreateWin32SurfaceKHR(VkInstance instance,
const VkWin32SurfaceCreateInfoKHR *create_info,
const VkAllocationCallbacks *allocator, VkSurfaceKHR *surface)
{
FIXME("stub: %p %p %p %p\n", instance, create_info, allocator, surface);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
static void X11DRV_vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks *allocator) static void X11DRV_vkDestroyInstance(VkInstance instance, const VkAllocationCallbacks *allocator)
{ {
TRACE("%p %p\n", instance, allocator); TRACE("%p %p\n", instance, allocator);
...@@ -87,10 +128,25 @@ static void X11DRV_vkDestroyInstance(VkInstance instance, const VkAllocationCall ...@@ -87,10 +128,25 @@ static void X11DRV_vkDestroyInstance(VkInstance instance, const VkAllocationCall
pvkDestroyInstance(instance, NULL /* allocator */); pvkDestroyInstance(instance, NULL /* allocator */);
} }
static void X11DRV_vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface,
const VkAllocationCallbacks *allocator)
{
FIXME("stub: %p 0x%s %p\n", instance, wine_dbgstr_longlong(surface), allocator);
}
static void X11DRV_vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain,
const VkAllocationCallbacks *allocator)
{
FIXME("stub: %p, 0x%s %p\n", device, wine_dbgstr_longlong(swapchain), allocator);
}
static VkResult X11DRV_vkEnumerateInstanceExtensionProperties(const char *layer_name, static VkResult X11DRV_vkEnumerateInstanceExtensionProperties(const char *layer_name,
uint32_t *count, VkExtensionProperties* properties) uint32_t *count, VkExtensionProperties* properties)
{ {
TRACE("layer_name %s, count %p, properties %p\n", debugstr_a(layer_name), count, properties); VkResult res;
unsigned int i, num_copies;
TRACE("layer_name %p, count %p, properties %p\n", debugstr_a(layer_name), count, properties);
/* This shouldn't get called with layer_name set, the ICD loader prevents it. */ /* This shouldn't get called with layer_name set, the ICD loader prevents it. */
if (layer_name) if (layer_name)
...@@ -106,16 +162,32 @@ static VkResult X11DRV_vkEnumerateInstanceExtensionProperties(const char *layer_ ...@@ -106,16 +162,32 @@ static VkResult X11DRV_vkEnumerateInstanceExtensionProperties(const char *layer_
* VK_KHR_win32_surface. Long-term this needs to be an intersection * VK_KHR_win32_surface. Long-term this needs to be an intersection
* between what the native library supports and what thunks we have. * between what the native library supports and what thunks we have.
*/ */
*count = 0; *count = ARRAY_SIZE(winex11_vk_instance_extensions);
return VK_SUCCESS; return VK_SUCCESS;
} }
/* When properties is not NULL, we copy the extensions over and set count if (*count < ARRAY_SIZE(winex11_vk_instance_extensions))
* to the number of copied extensions. For now we don't have much to do as {
* we don't support any extensions yet. /* Incomplete is a type of success used to signal the application
* that not all devices got copied.
*/ */
*count = 0; num_copies = *count;
return VK_SUCCESS; res = VK_INCOMPLETE;
}
else
{
num_copies = ARRAY_SIZE(winex11_vk_instance_extensions);
res = VK_SUCCESS;
}
for (i = 0; i < num_copies; i++)
{
memcpy(&properties[i], &winex11_vk_instance_extensions[i], sizeof(winex11_vk_instance_extensions[i]));
}
*count = num_copies;
TRACE("Result %d, extensions copied %u\n", res, num_copies);
return res;
} }
static void * X11DRV_vkGetDeviceProcAddr(VkDevice device, const char *name) static void * X11DRV_vkGetDeviceProcAddr(VkDevice device, const char *name)
...@@ -130,13 +202,74 @@ static void * X11DRV_vkGetInstanceProcAddr(VkInstance instance, const char *name ...@@ -130,13 +202,74 @@ static void * X11DRV_vkGetInstanceProcAddr(VkInstance instance, const char *name
return pvkGetInstanceProcAddr(instance, name); return pvkGetInstanceProcAddr(instance, name);
} }
static VkResult X11DRV_vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice phys_dev,
VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR *capabilities)
{
FIXME("stub: %p, 0x%s, %p\n", phys_dev, wine_dbgstr_longlong(surface), capabilities);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
static VkResult X11DRV_vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice phys_dev,
VkSurfaceKHR surface, uint32_t *count, VkSurfaceFormatKHR *formats)
{
FIXME("stub: %p, 0x%s, %p, %p\n", phys_dev, wine_dbgstr_longlong(surface), count, formats);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
static VkResult X11DRV_vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice phys_dev,
VkSurfaceKHR surface, uint32_t *count, VkPresentModeKHR *modes)
{
FIXME("stub: %p, 0x%s, %p, %p\n", phys_dev, wine_dbgstr_longlong(surface), count, modes);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
static VkResult X11DRV_vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice phys_dev,
uint32_t index, VkSurfaceKHR surface, VkBool32 *supported)
{
FIXME("stub: %p, %u, 0x%s, %p\n", phys_dev, index, wine_dbgstr_longlong(surface), supported);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
static VkBool32 X11DRV_vkGetPhysicalDeviceWin32PresentationSupportKHR(VkPhysicalDevice phys_dev,
uint32_t index)
{
FIXME("stub %p %u\n", phys_dev, index);
return VK_FALSE;
}
static VkResult X11DRV_vkGetSwapchainImagesKHR(VkDevice device,
VkSwapchainKHR swapchain, uint32_t *count, VkImage *images)
{
FIXME("stub: %p, 0x%s %p %p\n", device, wine_dbgstr_longlong(swapchain), count, images);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
static VkResult X11DRV_vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *present_info)
{
FIXME("stub: %p, %p\n", queue, present_info);
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
static const struct vulkan_funcs vulkan_funcs = static const struct vulkan_funcs vulkan_funcs =
{ {
X11DRV_vkAcquireNextImageKHR,
X11DRV_vkCreateInstance, X11DRV_vkCreateInstance,
X11DRV_vkCreateSwapchainKHR,
X11DRV_vkCreateWin32SurfaceKHR,
X11DRV_vkDestroyInstance, X11DRV_vkDestroyInstance,
X11DRV_vkDestroySurfaceKHR,
X11DRV_vkDestroySwapchainKHR,
X11DRV_vkEnumerateInstanceExtensionProperties, X11DRV_vkEnumerateInstanceExtensionProperties,
X11DRV_vkGetDeviceProcAddr, X11DRV_vkGetDeviceProcAddr,
X11DRV_vkGetInstanceProcAddr X11DRV_vkGetInstanceProcAddr,
X11DRV_vkGetPhysicalDeviceSurfaceCapabilitiesKHR,
X11DRV_vkGetPhysicalDeviceSurfaceFormatsKHR,
X11DRV_vkGetPhysicalDeviceSurfacePresentModesKHR,
X11DRV_vkGetPhysicalDeviceSurfaceSupportKHR,
X11DRV_vkGetPhysicalDeviceWin32PresentationSupportKHR,
X11DRV_vkGetSwapchainImagesKHR,
X11DRV_vkQueuePresentKHR
}; };
const struct vulkan_funcs *get_vulkan_driver(UINT version) const struct vulkan_funcs *get_vulkan_driver(UINT version)
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
#define __WINE_VULKAN_DRIVER_H #define __WINE_VULKAN_DRIVER_H
/* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */ /* Wine internal vulkan driver version, needs to be bumped upon vulkan_funcs changes. */
#define WINE_VULKAN_DRIVER_VERSION 2 #define WINE_VULKAN_DRIVER_VERSION 3
struct vulkan_funcs struct vulkan_funcs
{ {
...@@ -12,11 +12,23 @@ struct vulkan_funcs ...@@ -12,11 +12,23 @@ struct vulkan_funcs
* needs to provide. Other function calls will be provided indirectly by dispatch * needs to provide. Other function calls will be provided indirectly by dispatch
* tables part of dispatchable Vulkan objects such as VkInstance or vkDevice. * tables part of dispatchable Vulkan objects such as VkInstance or vkDevice.
*/ */
VkResult (*p_vkAcquireNextImageKHR)(VkDevice, VkSwapchainKHR, uint64_t, VkSemaphore, VkFence, uint32_t *);
VkResult (*p_vkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *); VkResult (*p_vkCreateInstance)(const VkInstanceCreateInfo *, const VkAllocationCallbacks *, VkInstance *);
VkResult (*p_vkCreateSwapchainKHR)(VkDevice, const VkSwapchainCreateInfoKHR *, const VkAllocationCallbacks *, VkSwapchainKHR *);
VkResult (*p_vkCreateWin32SurfaceKHR)(VkInstance, const VkWin32SurfaceCreateInfoKHR *, const VkAllocationCallbacks *, VkSurfaceKHR *);
void (*p_vkDestroyInstance)(VkInstance, const VkAllocationCallbacks *); void (*p_vkDestroyInstance)(VkInstance, const VkAllocationCallbacks *);
void (*p_vkDestroySurfaceKHR)(VkInstance, VkSurfaceKHR, const VkAllocationCallbacks *);
void (*p_vkDestroySwapchainKHR)(VkDevice, VkSwapchainKHR, const VkAllocationCallbacks *);
VkResult (*p_vkEnumerateInstanceExtensionProperties)(const char *, uint32_t *, VkExtensionProperties *); VkResult (*p_vkEnumerateInstanceExtensionProperties)(const char *, uint32_t *, VkExtensionProperties *);
void * (*p_vkGetDeviceProcAddr)(VkDevice, const char *); void * (*p_vkGetDeviceProcAddr)(VkDevice, const char *);
void * (*p_vkGetInstanceProcAddr)(VkInstance, const char *); void * (*p_vkGetInstanceProcAddr)(VkInstance, const char *);
VkResult (*p_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)(VkPhysicalDevice, VkSurfaceKHR, VkSurfaceCapabilitiesKHR *);
VkResult (*p_vkGetPhysicalDeviceSurfaceFormatsKHR)(VkPhysicalDevice, VkSurfaceKHR, uint32_t *, VkSurfaceFormatKHR *);
VkResult (*p_vkGetPhysicalDeviceSurfacePresentModesKHR)(VkPhysicalDevice, VkSurfaceKHR, uint32_t *, VkPresentModeKHR *);
VkResult (*p_vkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice, uint32_t, VkSurfaceKHR, VkBool32 *);
VkBool32 (*p_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice, uint32_t);
VkResult (*p_vkGetSwapchainImagesKHR)(VkDevice, VkSwapchainKHR, uint32_t *, VkImage *);
VkResult (*p_vkQueuePresentKHR)(VkQueue, const VkPresentInfoKHR *);
}; };
extern const struct vulkan_funcs * CDECL __wine_get_vulkan_driver(HDC hdc, UINT version); extern const struct vulkan_funcs * CDECL __wine_get_vulkan_driver(HDC hdc, UINT version);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment