From patchwork Wed May 31 22:41:04 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Keith Packard X-Patchwork-Id: 9758333 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 6E6E9602BF for ; Wed, 31 May 2017 22:41:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4FE7928453 for ; Wed, 31 May 2017 22:41:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 43F95284BD; Wed, 31 May 2017 22:41:16 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 63224284D2 for ; Wed, 31 May 2017 22:41:13 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 212FC6E1FC; Wed, 31 May 2017 22:41:09 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from elaine.keithp.com (home.keithp.com [63.227.221.253]) by gabe.freedesktop.org (Postfix) with ESMTP id 0247C6E1FC for ; Wed, 31 May 2017 22:41:07 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by elaine.keithp.com (Postfix) with ESMTP id E6E1A3F204C8 for ; Wed, 31 May 2017 15:41:06 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at keithp.com Received: from elaine.keithp.com ([127.0.0.1]) by localhost (elaine.keithp.com [127.0.0.1]) (amavisd-new, port 10024) with LMTP id lXLqvje2p_Gf for ; Wed, 31 May 2017 15:41:04 -0700 (PDT) Received: from hiro.keithp.com (hiro.keithp.com [10.0.0.36]) by elaine.keithp.com (Postfix) with ESMTPSA id 28FFA3F201D9 for ; Wed, 31 May 2017 15:41:04 -0700 (PDT) Received: by hiro.keithp.com (Postfix, from userid 1001) id D60067461F0; Wed, 31 May 2017 15:41:04 -0700 (PDT) From: keithp@keithp.com ("Keith Packard") To: dri-devel@lists.freedesktop.org Subject: [PATCH 1/1] Adding VK_KHR_display to mesa based on KMS Date: Wed, 31 May 2017 15:41:04 -0700 Message-ID: <86r2z43b0f.fsf@hiro.keithp.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP As part of my adventures to implement 'DRM leases', it turns out to be easiest to just implement 'real' VK_KHR_display in mesa now, rather than a pile of ugly kludges. Because DRM leases just provide a DRM master fd with limited resource access, mesa doesn't have to care whether you provide a 'real' master or a lease. That means all of the mesa code is separate from DRM leases, and can also be used to run applications directly on bare hardware. The implementation of the KHR_display extension lives pretty much entirely within the common vulkan code in src/vulkan/wsi/wsi_common_display.c The driver functions in radv simply call those functions, which means this should be 'trivial' to enable in the anv driver, but I haven't done that work. To get the master FD into the driver, I've created a new extension, VK_KEITHP_kms_display which provides a DRM master file descriptor that the driver uses in place of creating it's own render node file descriptor. There's a bit of a dance involved in making sure we use the right driver; that could probably be improved. There are a few bits of unimplemented functionality within the KHR_display implementation: 1) I don't dig the monitor name out of EDID. I'm thinking this should be done by the kernel and exposed as a property. It already has the code for this... 2) plane and surface capabilities are 'faked'. These expose the range of source surface sizes and positions which can be used; obviously the hardware can do a lot more than display an image at full size. 3) Planes in general. I'm exposing only a single plane per connector at this point. Obviously we'd want more. 4) bpp/depth. For now, I'm assuming depth 24, 32bpp. I think I just need a driver hook to get this out of the image; I sure don't want to try and parse the format. I haven't rebased this in a while; it's a few revs back on master. I'm looking for general feedback and comments about the plan at this point; this is the second implementation of the code, I'm entirely willing to do at least one more to make it 'right'. git://people.freedesktop.org/~keithp/mesa.git khr_display There are also some simple changes in the vulkan looader (replicated in the relevant files in mesa) needed to gain access to the VK_KEITHP_kms_display extension (thanks, Vulkan, for not exposing vendor instance extensions by default) git://people.freedesktop.org/~keithp/Vulkan-LoaderAndValidationLayers.git drm-leases From 06088071af2a396ce58b0490ceb228e3aa079a09 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 17 May 2017 23:02:33 -0700 Subject: [PATCH] Add VK_KHR_display and VK_KEITHP_kms_display to radv vulkan driver [v2] Implements VK_KHR_display using DRM/KMS. Expose ability to get to that using VK_KEITHP_kms_display, which allows the application to provide a master fd to the library which it uses instead of opening its own. Signed-off-by: Keith Packard --- configure.ac | 1 + include/vulkan/vulkan.h | 3 +- include/vulkan/vulkan_keithp.h | 40 + src/Makefile.am | 7 + src/amd/vulkan/Makefile.am | 9 + src/amd/vulkan/Makefile.sources | 3 + src/amd/vulkan/radv_device.c | 71 +- src/amd/vulkan/radv_entrypoints_gen.py | 2 + src/amd/vulkan/radv_private.h | 7 + src/amd/vulkan/radv_radeon_winsys.h | 4 + src/amd/vulkan/radv_wsi.c | 37 +- src/amd/vulkan/radv_wsi_display.c | 144 ++++ src/amd/vulkan/winsys/amdgpu/radv_amdgpu_bo.c | 18 + src/intel/vulkan/anv_wsi.c | 2 +- src/vulkan/Makefile.am | 9 + src/vulkan/Makefile.sources | 4 + src/vulkan/registry/vk.xml | 18 + src/vulkan/wsi/wsi_common.h | 12 +- src/vulkan/wsi/wsi_common_display.c | 1063 +++++++++++++++++++++++++ src/vulkan/wsi/wsi_common_display.h | 80 ++ src/vulkan/wsi/wsi_common_wayland.c | 2 +- src/vulkan/wsi/wsi_common_x11.c | 4 +- 22 files changed, 1526 insertions(+), 14 deletions(-) create mode 100644 include/vulkan/vulkan_keithp.h create mode 100644 src/amd/vulkan/radv_wsi_display.c create mode 100644 src/vulkan/wsi/wsi_common_display.c create mode 100644 src/vulkan/wsi/wsi_common_display.h diff --git a/configure.ac b/configure.ac index df3eb6b29a..4712efcd5e 100644 --- a/configure.ac +++ b/configure.ac @@ -2238,6 +2238,7 @@ fi AM_CONDITIONAL(HAVE_PLATFORM_X11, echo "$egl_platforms" | grep -q 'x11') AM_CONDITIONAL(HAVE_PLATFORM_WAYLAND, echo "$egl_platforms" | grep -q 'wayland') +AM_CONDITIONAL(HAVE_PLATFORM_DISPLAY, echo "$egl_platforms" | grep -q 'drm') AM_CONDITIONAL(HAVE_EGL_PLATFORM_DRM, echo "$egl_platforms" | grep -q 'drm') AM_CONDITIONAL(HAVE_EGL_PLATFORM_SURFACELESS, echo "$egl_platforms" | grep -q 'surfaceless') AM_CONDITIONAL(HAVE_EGL_PLATFORM_ANDROID, echo "$egl_platforms" | grep -q 'android') diff --git a/include/vulkan/vulkan.h b/include/vulkan/vulkan.h index ef0c246780..8b5be61e29 100644 --- a/include/vulkan/vulkan.h +++ b/include/vulkan/vulkan.h @@ -43,7 +43,7 @@ extern "C" { #define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) #define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) // Version of this file -#define VK_HEADER_VERSION 46 +#define VK_HEADER_VERSION 48 #define VK_NULL_HANDLE 0 @@ -214,6 +214,7 @@ typedef enum VkStructureType { VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000, VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, + VK_STRUCTURE_TYPE_KMS_DISPLAY_INFO_KEITHP = 1000010000, VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000, VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD = 1000018000, VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000, diff --git a/include/vulkan/vulkan_keithp.h b/include/vulkan/vulkan_keithp.h new file mode 100644 index 0000000000..acf59fa4e1 --- /dev/null +++ b/include/vulkan/vulkan_keithp.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2017 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _VULKAN_KEITHP_H_ +#define _VULKAN_KEITHP_H_ + +#include "vulkan.h" +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +typedef struct VkKmsDisplayInfoKEITHP { + VkStructureType sType; /* VK_STRUCTURE_TYPE_KMS_DISPLAY_INFO_KEITHP */ + const void* pNext; + int fd; +} VkKmsDisplayInfoKEITHP; + +#define VK_KEITHP_KMS_DISPLAY_SPEC_VERSION 1 +#define VK_KEITHP_KMS_DISPLAY_EXTENSION_NAME "VK_KEITHP_kms_display" + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _VULKAN_KEITHP_H_ */ diff --git a/src/Makefile.am b/src/Makefile.am index aa5f8aaf7d..6fcd8b9d24 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -85,6 +85,13 @@ SUBDIRS += vulkan endif EXTRA_DIST += vulkan/registry/vk.xml +EXTRA_DIST += include/vulkan/vulkan_keithp.h + +vulkan_includedir = $(includedir)/vulkan + +vulkan_include_HEADERS = \ + $(top_srcdir)/include/vulkan/vulkan_keithp.h + if HAVE_AMD_DRIVERS SUBDIRS += amd endif diff --git a/src/amd/vulkan/Makefile.am b/src/amd/vulkan/Makefile.am index fbd9f5a030..cf1f5219b9 100644 --- a/src/amd/vulkan/Makefile.am +++ b/src/amd/vulkan/Makefile.am @@ -61,6 +61,15 @@ VULKAN_SOURCES = \ VULKAN_LIB_DEPS = +if HAVE_PLATFORM_DISPLAY +AM_CPPFLAGS += \ + -DVK_USE_PLATFORM_DISPLAY_KHR \ + -DVK_USE_PLATFORM_KMS_KEITHP + +VULKAN_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) + +VULKAN_LIB_DEPS += $(LIBDRM_LIBS) +endif if HAVE_PLATFORM_X11 AM_CPPFLAGS += \ diff --git a/src/amd/vulkan/Makefile.sources b/src/amd/vulkan/Makefile.sources index d3e0c81e9a..1119e908d1 100644 --- a/src/amd/vulkan/Makefile.sources +++ b/src/amd/vulkan/Makefile.sources @@ -72,6 +72,9 @@ VULKAN_WSI_WAYLAND_FILES := \ VULKAN_WSI_X11_FILES := \ radv_wsi_x11.c +VULKAN_WSI_DISPLAY_FILES := \ + radv_wsi_display.c + VULKAN_GENERATED_FILES := \ radv_entrypoints.c \ radv_entrypoints.h diff --git a/src/amd/vulkan/radv_device.c b/src/amd/vulkan/radv_device.c index 921b8e48f5..1a30992d48 100644 --- a/src/amd/vulkan/radv_device.c +++ b/src/amd/vulkan/radv_device.c @@ -88,6 +88,18 @@ static const VkExtensionProperties instance_extensions[] = { .extensionName = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, .specVersion = 1, }, + { + .extensionName = VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME, + .specVersion = VK_EXT_DIRECT_MODE_DISPLAY_SPEC_VERSION + }, + { + .extensionName = VK_KHR_DISPLAY_EXTENSION_NAME, + .specVersion = VK_KHR_DISPLAY_SPEC_VERSION + }, + { + .extensionName = VK_KEITHP_KMS_DISPLAY_EXTENSION_NAME, + .specVersion = 1, + } }; static const VkExtensionProperties common_device_extensions[] = { @@ -187,6 +199,12 @@ is_extension_enabled(const VkExtensionProperties *extensions, return false; } +static bool +is_same_device(dev_t a, dev_t b) +{ + return major(a) == major(b) && (minor(a) & 0x3f) == (minor(b) & 0x3f); +} + static VkResult radv_physical_device_init(struct radv_physical_device *device, struct radv_instance *instance, @@ -194,9 +212,26 @@ radv_physical_device_init(struct radv_physical_device *device, { VkResult result; drmVersionPtr version; - int fd; - - fd = open(path, O_RDWR | O_CLOEXEC); + int fd = -1; + struct stat stat_info, stat_path; + + if (instance->master_fd >= 0) { + if (fstat(instance->master_fd, &stat_info) >= 0 && + stat(path, &stat_path) >= 0 && + S_ISCHR(stat_info.st_mode) && + S_ISCHR(stat_path.st_mode) && + is_same_device(stat_info.st_rdev, stat_path.st_rdev)) + { + printf("matched physical device to provided device info\n"); + fd = dup(instance->master_fd); + } else { + printf("no match for provided device info\n"); + return VK_ERROR_INCOMPATIBLE_DRIVER; + } + } + + if (fd < 0) + fd = open(path, O_RDWR | O_CLOEXEC); if (fd < 0) return VK_ERROR_INCOMPATIBLE_DRIVER; @@ -366,6 +401,21 @@ VkResult radv_CreateInstance( instance->debug_flags = parse_debug_string(getenv("RADV_DEBUG"), radv_debug_options); + instance->master_fd = -1; + + vk_foreach_struct(ext, pCreateInfo->pNext) { + VkKmsDisplayInfoKEITHP *vk_kms; + switch (ext->sType) { + case VK_STRUCTURE_TYPE_KMS_DISPLAY_INFO_KEITHP: + vk_kms = (void *) ext; + fprintf(stderr, "Found kms display info keithp struct fd %d\n", vk_kms->fd); + instance->master_fd = vk_kms->fd; + break; + default: + break; + } + } + *pInstance = radv_instance_to_handle(instance); return VK_SUCCESS; @@ -2004,6 +2054,21 @@ bool radv_get_memory_fd(struct radv_device *device, pFD); } +bool radv_get_memory_handle(struct radv_device *device, + struct radv_device_memory *memory, + uint32_t *pHandle) +{ + struct radeon_bo_metadata metadata; + + if (memory->image) { + radv_init_metadata(device, memory->image, &metadata); + device->ws->buffer_set_metadata(memory->bo, &metadata); + } + + return device->ws->buffer_get_handle(device->ws, memory->bo, + pHandle); +} + VkResult radv_AllocateMemory( VkDevice _device, const VkMemoryAllocateInfo* pAllocateInfo, diff --git a/src/amd/vulkan/radv_entrypoints_gen.py b/src/amd/vulkan/radv_entrypoints_gen.py index 3474c789ea..e8e24b8da7 100644 --- a/src/amd/vulkan/radv_entrypoints_gen.py +++ b/src/amd/vulkan/radv_entrypoints_gen.py @@ -42,6 +42,8 @@ supported_extensions = [ 'VK_KHR_wayland_surface', 'VK_KHR_xcb_surface', 'VK_KHR_xlib_surface', + 'VK_KEITHP_kms_display', + 'VK_KHR_display' ] # We generate a static hash table for entry point lookup diff --git a/src/amd/vulkan/radv_private.h b/src/amd/vulkan/radv_private.h index 088d24a706..7a2e1442d7 100644 --- a/src/amd/vulkan/radv_private.h +++ b/src/amd/vulkan/radv_private.h @@ -67,11 +67,13 @@ typedef uint32_t xcb_window_t; #include #include +#include #include #include "radv_entrypoints.h" #include "wsi_common.h" +#include "wsi_common_display.h" #define MAX_VBS 32 #define MAX_VERTEX_ATTRIBS 32 @@ -281,6 +283,8 @@ struct radv_instance { int physicalDeviceCount; struct radv_physical_device physicalDevices[RADV_MAX_DRM_DEVICES]; + int master_fd; + uint64_t debug_flags; }; @@ -894,6 +898,9 @@ void radv_cmd_buffer_trace_emit(struct radv_cmd_buffer *cmd_buffer); bool radv_get_memory_fd(struct radv_device *device, struct radv_device_memory *memory, int *pFD); +bool radv_get_memory_handle(struct radv_device *device, + struct radv_device_memory *memory, + uint32_t *pHandle); /* * Takes x,y,z as exact numbers of invocations, instead of blocks. * diff --git a/src/amd/vulkan/radv_radeon_winsys.h b/src/amd/vulkan/radv_radeon_winsys.h index 84b1d73780..cc7bca73bc 100644 --- a/src/amd/vulkan/radv_radeon_winsys.h +++ b/src/amd/vulkan/radv_radeon_winsys.h @@ -283,6 +283,10 @@ struct radeon_winsys { struct radeon_winsys_bo *bo, int *fd); + bool (*buffer_get_handle)(struct radeon_winsys *ws, + struct radeon_winsys_bo *bo, + uint32_t *handle); + void (*buffer_unmap)(struct radeon_winsys_bo *bo); uint64_t (*buffer_get_va)(struct radeon_winsys_bo *bo); diff --git a/src/amd/vulkan/radv_wsi.c b/src/amd/vulkan/radv_wsi.c index 3a8617fd8f..85efb2c279 100644 --- a/src/amd/vulkan/radv_wsi.c +++ b/src/amd/vulkan/radv_wsi.c @@ -57,6 +57,23 @@ radv_init_wsi(struct radv_physical_device *physical_device) } #endif +#ifdef VK_USE_PLATFORM_DISPLAY_KHR + result = wsi_display_init_wsi(&physical_device->wsi_device, + &physical_device->instance->alloc, + radv_physical_device_to_handle(physical_device), + physical_device->instance->master_fd >= 0 ? physical_device->local_fd : -1, + &wsi_cbs); + if (result != VK_SUCCESS) { +#ifdef VK_USE_PLATFORM_XCB_KHR + wsi_x11_finish_wsi(&physical_device->wsi_device, &physical_device->instance->alloc); +#endif +#ifdef VK_USE_PLATFORM_WAYLAND_KHR + wsi_wayland_finish_wsi(&physical_device->wsi_device, &physical_device->instance->alloc); +#endif + return result; + } +#endif + return VK_SUCCESS; } @@ -69,6 +86,9 @@ radv_finish_wsi(struct radv_physical_device *physical_device) #ifdef VK_USE_PLATFORM_XCB_KHR wsi_x11_finish_wsi(&physical_device->wsi_device, &physical_device->instance->alloc); #endif +#ifdef VK_USE_PLATFORM_DISPLAY_KHR + wsi_display_finish_wsi(&physical_device->wsi_device, &physical_device->instance->alloc); +#endif } void radv_DestroySurfaceKHR( @@ -147,7 +167,7 @@ radv_wsi_image_create(VkDevice device_h, VkDeviceMemory *memory_p, uint32_t *size, uint32_t *offset, - uint32_t *row_pitch, int *fd_p) + uint32_t *row_pitch, int *fd_p, uint32_t *handle_p) { VkResult result = VK_SUCCESS; struct radeon_surf *surface; @@ -213,9 +233,18 @@ radv_wsi_image_create(VkDevice device_h, if (!needs_linear_copy || (needs_linear_copy && linear)) { RADV_FROM_HANDLE(radv_device, device, device_h); RADV_FROM_HANDLE(radv_device_memory, memory, memory_h); - if (!radv_get_memory_fd(device, memory, &fd)) - goto fail_alloc_memory; - *fd_p = fd; + + if (fd_p) { + if (!radv_get_memory_fd(device, memory, &fd)) + goto fail_alloc_memory; + *fd_p = fd; + } + if (handle_p) { + uint32_t handle; + if (!radv_get_memory_handle(device, memory, &handle)) + goto fail_alloc_memory; + *handle_p = handle; + } } surface = &image->surface; diff --git a/src/amd/vulkan/radv_wsi_display.c b/src/amd/vulkan/radv_wsi_display.c new file mode 100644 index 0000000000..7113b78593 --- /dev/null +++ b/src/amd/vulkan/radv_wsi_display.c @@ -0,0 +1,144 @@ +/* + * Copyright © 2017 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include "radv_private.h" +#include "radv_cs.h" +#include "util/disk_cache.h" +#include "util/strtod.h" +#include "util/vk_util.h" +#include +#include +#include +#include +#include "amdgpu_id.h" +#include "winsys/amdgpu/radv_amdgpu_winsys_public.h" +#include "ac_llvm_util.h" +#include "vk_format.h" +#include "sid.h" +#include "util/debug.h" +#include "wsi_common_display.h" + +#define MM_PER_PIXEL (1.0/96.0 * 25.4) + +VkResult +radv_GetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physical_device, + uint32_t *property_count, + VkDisplayPropertiesKHR *properties) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_display_get_physical_device_display_properties(physical_device, + &pdevice->wsi_device, + property_count, + properties); +} + +VkResult +radv_GetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice physical_device, + uint32_t *property_count, + VkDisplayPlanePropertiesKHR *properties) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_display_get_physical_device_display_plane_properties(physical_device, + &pdevice->wsi_device, + property_count, + properties); +} + +VkResult +radv_GetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physical_device, + uint32_t plane_index, + uint32_t *display_count, + VkDisplayKHR *displays) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_display_get_display_plane_supported_displays(physical_device, + &pdevice->wsi_device, + plane_index, + display_count, + displays); +} + + +VkResult +radv_GetDisplayModePropertiesKHR(VkPhysicalDevice physical_device, + VkDisplayKHR display, + uint32_t *property_count, + VkDisplayModePropertiesKHR *properties) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_display_get_display_mode_properties(physical_device, + &pdevice->wsi_device, + display, + property_count, + properties); +} + +VkResult +radv_CreateDisplayModeKHR(VkPhysicalDevice physical_device, + VkDisplayKHR display, + const VkDisplayModeCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + VkDisplayModeKHR *mode) +{ + return VK_ERROR_INITIALIZATION_FAILED; +} + + +VkResult +radv_GetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physical_device, + VkDisplayModeKHR mode_khr, + uint32_t plane_index, + VkDisplayPlaneCapabilitiesKHR *capabilities) +{ + RADV_FROM_HANDLE(radv_physical_device, pdevice, physical_device); + + return wsi_get_display_plane_capabilities(physical_device, + &pdevice->wsi_device, + mode_khr, + plane_index, + capabilities); +} + +VkResult +radv_CreateDisplayPlaneSurfaceKHR(VkInstance _instance, + const VkDisplaySurfaceCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + VkSurfaceKHR *surface) +{ + RADV_FROM_HANDLE(radv_instance, instance, _instance); + const VkAllocationCallbacks *alloc; + + if (allocator) + alloc = allocator; + else + alloc = &instance->alloc; + + return wsi_create_display_surface(_instance, alloc, create_info, surface); +} diff --git a/src/amd/vulkan/winsys/amdgpu/radv_amdgpu_bo.c b/src/amd/vulkan/winsys/amdgpu/radv_amdgpu_bo.c index 7b679450cb..eeae79cb76 100644 --- a/src/amd/vulkan/winsys/amdgpu/radv_amdgpu_bo.c +++ b/src/amd/vulkan/winsys/amdgpu/radv_amdgpu_bo.c @@ -445,6 +445,23 @@ radv_amdgpu_winsys_get_fd(struct radeon_winsys *_ws, return true; } +static bool +radv_amdgpu_winsys_get_handle(struct radeon_winsys *_ws, + struct radeon_winsys_bo *_bo, + uint32_t *handle) +{ + struct radv_amdgpu_winsys_bo *bo = radv_amdgpu_winsys_bo(_bo); + enum amdgpu_bo_handle_type type = amdgpu_bo_handle_type_kms; + int r; + + r = amdgpu_bo_export(bo->bo, type, handle); + if (r) + return false; + + bo->is_shared = true; + return true; +} + static unsigned radv_eg_tile_split_rev(unsigned eg_tile_split) { switch (eg_tile_split) { @@ -503,6 +520,7 @@ void radv_amdgpu_bo_init_functions(struct radv_amdgpu_winsys *ws) ws->base.buffer_unmap = radv_amdgpu_winsys_bo_unmap; ws->base.buffer_from_fd = radv_amdgpu_winsys_bo_from_fd; ws->base.buffer_get_fd = radv_amdgpu_winsys_get_fd; + ws->base.buffer_get_handle = radv_amdgpu_winsys_get_handle; ws->base.buffer_set_metadata = radv_amdgpu_winsys_bo_set_metadata; ws->base.buffer_virtual_bind = radv_amdgpu_winsys_bo_virtual_bind; } diff --git a/src/intel/vulkan/anv_wsi.c b/src/intel/vulkan/anv_wsi.c index 2ab37c30b7..d274493599 100644 --- a/src/intel/vulkan/anv_wsi.c +++ b/src/intel/vulkan/anv_wsi.c @@ -151,7 +151,7 @@ anv_wsi_image_create(VkDevice device_h, VkDeviceMemory *memory_p, uint32_t *size, uint32_t *offset, - uint32_t *row_pitch, int *fd_p) + uint32_t *row_pitch, int *fd_p, uint32_t *handle_p) { struct anv_device *device = anv_device_from_handle(device_h); VkImage image_h; diff --git a/src/vulkan/Makefile.am b/src/vulkan/Makefile.am index bd66901255..d7b654d266 100644 --- a/src/vulkan/Makefile.am +++ b/src/vulkan/Makefile.am @@ -48,6 +48,15 @@ VULKAN_WSI_SOURCES += $(VULKAN_WSI_X11_FILES) VULKAN_LIB_DEPS += $(XCB_DRI3_LIBS) -lX11-xcb endif +if HAVE_PLATFORM_DISPLAY +AM_CPPFLAGS += \ + -DVK_USE_PLATFORM_DISPLAY + +VULKAN_WSI_SOURCES += $(VULKAN_WSI_DISPLAY_FILES) + +VULKAN_LIB_DEPS += $(LIBDRM_LIBS) +endif + BUILT_SOURCES += $(VULKAN_WSI_WAYLAND_GENERATED_FILES) CLEANFILES = $(BUILT_SOURCES) diff --git a/src/vulkan/Makefile.sources b/src/vulkan/Makefile.sources index 63f4ac1162..109aa83e6b 100644 --- a/src/vulkan/Makefile.sources +++ b/src/vulkan/Makefile.sources @@ -15,6 +15,10 @@ VULKAN_WSI_X11_FILES := \ wsi/wsi_common_x11.c \ wsi/wsi_common_x11.h +VULKAN_WSI_DISPLAY_FILES := \ + wsi/wsi_common_display.c \ + wsi/wsi_common_display.h + VULKAN_UTIL_GENERATED_FILES := \ util/vk_enum_to_str.c \ util/vk_enum_to_str.h diff --git a/src/vulkan/registry/vk.xml b/src/vulkan/registry/vk.xml index b233108a36..d9c01f6e52 100644 --- a/src/vulkan/registry/vk.xml +++ b/src/vulkan/registry/vk.xml @@ -67,6 +67,7 @@ maintained in the master branch of the Khronos Vulkan GitHub project. + @@ -1538,6 +1539,16 @@ maintained in the master branch of the Khronos Vulkan GitHub project. xcb_connection_t* connection xcb_window_t window + + VkStructureType sType + VkStructureType sType + constvoid* pNext + int fd + uint32_t crtc_id + uint32_t* connector_ids + int connector_count + drmModeModeInfoPtr mode + VkFormat format VkColorSpaceKHR colorSpace @@ -5117,6 +5128,13 @@ maintained in the master branch of the Khronos Vulkan GitHub project. + + + + + + + diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h index 5e77518c09..ccf91ecac9 100644 --- a/src/vulkan/wsi/wsi_common.h +++ b/src/vulkan/wsi/wsi_common.h @@ -42,7 +42,8 @@ struct wsi_image_fns { uint32_t *size_p, uint32_t *offset_p, uint32_t *row_pitch_p, - int *fd_p); + int *fd_p, + uint32_t *handle_p); void (*free_wsi_image)(VkDevice device, const VkAllocationCallbacks *pAllocator, VkImage image_h, @@ -104,7 +105,7 @@ struct wsi_interface { struct wsi_swapchain **swapchain); }; -#define VK_ICD_WSI_PLATFORM_MAX 5 +#define VK_ICD_WSI_PLATFORM_MAX 6 struct wsi_device { struct wsi_interface * wsi[VK_ICD_WSI_PLATFORM_MAX]; @@ -162,5 +163,12 @@ VkResult wsi_wl_init_wsi(struct wsi_device *wsi_device, void wsi_wl_finish_wsi(struct wsi_device *wsi_device, const VkAllocationCallbacks *alloc); +VkResult wsi_display_init_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc, + VkPhysicalDevice physical_device, + int fd, + const struct wsi_callbacks *cbs); +void wsi_display_finish_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc); #endif diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c new file mode 100644 index 0000000000..9336b2ae67 --- /dev/null +++ b/src/vulkan/wsi/wsi_common_display.c @@ -0,0 +1,1063 @@ +/* + * Copyright © 2017 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include "util/macros.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util/hash_table.h" +#include "util/list.h" + +#include "wsi_common.h" +#include "wsi_common_queue.h" +#include "wsi_common_display.h" + +/* These have lifetime equal to the instance, so they effectively + * never go away. This means we must keep track of them separately + * from all other resources. + */ +typedef struct wsi_display_mode { + struct list_head list; + struct wsi_display_connector *connector; + drmModeModeInfo mode; +} wsi_display_mode; + +typedef struct wsi_display_connector { + struct list_head list; + struct wsi_display *wsi; + uint32_t id; + uint32_t crtc_id; + char *name; + bool active; + drmModeConnectorPtr connector; + drmModePropertyBlobPtr edid_blob; +} wsi_display_connector; + +struct wsi_display { + struct wsi_interface base; + + const VkAllocationCallbacks *alloc; + VkPhysicalDevice physical_device; + + const struct wsi_callbacks *cbs; + + int master_fd; + + struct list_head connectors; + + struct list_head display_modes; + + drmModeResPtr mode_res; +}; + +struct wsi_display_image { + struct wsi_display_swapchain *chain; + VkImage image; + VkImage linear_image; + VkDeviceMemory memory; + VkDeviceMemory linear_memory; + bool busy; + unsigned int frame; + unsigned int sec; + unsigned int usec; + uint32_t bo_handle; + uint32_t fb_id; +}; + +struct wsi_display_swapchain { + struct wsi_swapchain base; + int fd; + VkIcdSurfaceDisplay *surface; + struct wsi_display_image images[0]; +}; + +ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_mode, VkDisplayModeKHR) +ICD_DEFINE_NONDISP_HANDLE_CASTS(wsi_display_connector, VkDisplayKHR) + +VkResult +wsi_display_set_master_fd(VkInstance instance, + struct wsi_device *wsi_device, + int master_fd) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + wsi->master_fd = master_fd; + + return VK_SUCCESS; +} + +static bool +same_mode(drmModeModeInfoPtr a, + drmModeModeInfoPtr b) +{ + return memcmp(a, b, sizeof (drmModeModeInfo)) == 0; +} + +static struct wsi_display_mode * +wsi_display_find_mode(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + drmModeModeInfoPtr mode) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + size_t len; + struct wsi_display_mode *display_mode; + + /* clear name bytes beyond string len */ + len = strnlen(mode->name, sizeof(mode->name)); + memset(mode->name + len, '\0', sizeof(mode->name) - len); + + LIST_FOR_EACH_ENTRY(display_mode, &wsi->display_modes, list) { + if (display_mode->connector == connector && + same_mode(&display_mode->mode, mode)) + return display_mode; + } + return NULL; +} + +static drmModeModeInfoPtr +wsi_display_find_mode_info(struct wsi_display_connector *connector, + struct wsi_display_mode *display_mode) +{ + int m; + + for (m = 0; m < connector->connector->count_modes; m++) { + if (same_mode(&connector->connector->modes[m], &display_mode->mode)) + return &connector->connector->modes[m]; + } + return NULL; +} + +static VkResult +wsi_display_register_mode(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + drmModeModeInfoPtr mode) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_mode *display_mode; + + display_mode = wsi_display_find_mode(wsi_device, connector, mode); + + if (display_mode) + return VK_SUCCESS; + + display_mode = vk_alloc(wsi->alloc, sizeof (struct wsi_display_mode), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + if (!display_mode) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + display_mode->connector = connector; + display_mode->mode = *mode; + + LIST_ADDTAIL(&display_mode->list, &wsi->display_modes); + return VK_SUCCESS; +} + + +/* + * Update our information about a specific connector + */ + +static struct wsi_display_connector * +wsi_display_find_connector(struct wsi_device *wsi_device, + uint32_t connector_id) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector; + + connector = NULL; + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { + if (connector->id == connector_id) + return connector; + } + + return NULL; +} + +static struct wsi_display_connector * +wsi_display_get_connector(struct wsi_device *wsi_device, + uint32_t connector_id) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector; + int master_fd = wsi->master_fd; + int p, m; + VkResult result; + + + connector = wsi_display_find_connector(wsi_device, connector_id); + + if (!connector) { + connector = vk_alloc(wsi->alloc, sizeof *connector, 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + if (!connector) + return NULL; + memset(connector, '\0', sizeof (*connector)); + connector->id = connector_id; + connector->wsi = wsi; + connector->active = false; + LIST_ADDTAIL(&connector->list, &wsi->connectors); + } + + if (connector->connector) + drmModeFreeConnector(connector->connector); + + connector->connector = drmModeGetConnector(master_fd, connector->id); + + if (!connector->connector) + return NULL; + + /* Look for an EDID property */ + for (p = 0; p < connector->connector->count_props; p++) { + drmModePropertyPtr prop = drmModeGetProperty(master_fd, connector->connector->props[p]); + if (prop && (prop->flags & DRM_MODE_PROP_BLOB)) { + if (!strcmp(prop->name, "EDID")) { + if (connector->edid_blob) + drmModeFreePropertyBlob(connector->edid_blob); + connector->edid_blob = drmModeGetPropertyBlob(master_fd, + connector->connector->prop_values[p]); + /* XXX dig name out of EDID data */ + } + } + } + + /* XXX use EDID name */ + connector->name = "monitor"; + + /* Register modes */ + for (m = 0; m < connector->connector->count_modes; m++) { + result = wsi_display_register_mode(wsi_device, + connector, + &connector->connector->modes[m]); + if (result != VK_SUCCESS) + return NULL; + } + + return connector; +} + +#define MM_PER_PIXEL (1.0/96.0 * 25.4) + +static void +wsi_display_fill_in_display_properties(struct wsi_device *wsi_device, + struct wsi_display_connector *connector, + VkDisplayPropertiesKHR *properties) +{ + int m; + drmModeModeInfoPtr preferred_mode; + + properties->display = wsi_display_connector_to_handle(connector); + properties->displayName = connector->name; + + /* Find the preferred mode and assume that's the physical resolution */ + if (connector->connector->count_modes > 0) { + preferred_mode = &connector->connector->modes[0]; + for (m = 0; m < connector->connector->count_modes; m++) + if (connector->connector->modes[m].type & DRM_MODE_TYPE_PREFERRED) { + preferred_mode = &connector->connector->modes[m]; + break; + } + + properties->physicalResolution.width = preferred_mode->hdisplay; + properties->physicalResolution.height = preferred_mode->vdisplay; + } else { + properties->physicalResolution.width = 1024; + properties->physicalResolution.height = 768; + } + + /* Make up physical size based on 96dpi */ + properties->physicalDimensions.width = floor(properties->physicalResolution.width * MM_PER_PIXEL + 0.5); + properties->physicalDimensions.height = floor(properties->physicalResolution.height * MM_PER_PIXEL + 0.5); + + properties->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + properties->persistentContent = 0; +} + +/* + * Implement vkGetPhysicalDeviceDisplayPropertiesKHR + */ +VkResult +wsi_display_get_physical_device_display_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPropertiesKHR *properties) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + int master_fd = wsi->master_fd; + struct wsi_display_connector *connector; + int c; + uint32_t connected = 0; + + if (wsi->mode_res) + drmModeFreeResources(wsi->mode_res); + + wsi->mode_res = drmModeGetResources(master_fd); + + if (!wsi->mode_res) + return VK_ERROR_INITIALIZATION_FAILED; + + connected = 0; + + /* Erase saved connector information */ + LIST_FOR_EACH_ENTRY(connector, &wsi->connectors, list) { + if (connector->connector) { + drmModeFreeConnector(connector->connector); + connector->connector = NULL; + } + } + + /* Get current information */ + for (c = 0; c < wsi->mode_res->count_connectors; c++) { + connector = wsi_display_get_connector(wsi_device, wsi->mode_res->connectors[c]); + + if (!connector) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + if (connector->connector->connection != DRM_MODE_DISCONNECTED) + connected++; + } + + /* Asking only for count, return that */ + if (properties == NULL) { + *property_count = connected; + return VK_SUCCESS; + } + + connected = 0; + + for (c = 0; c < wsi->mode_res->count_connectors; c++) { + connector = wsi_display_find_connector(wsi_device, wsi->mode_res->connectors[c]); + if (connector && connector->connector->connection != DRM_MODE_DISCONNECTED) { + if (connected < *property_count) { + + wsi_display_fill_in_display_properties(wsi_device, + connector, + &properties[connected]); + } + connected++; + } + } + + *property_count = connected; + + if (connected <= *property_count) + return VK_SUCCESS; + + return VK_INCOMPLETE; +} + +/* + * Implement vkGetPhysicalDeviceDisplayPlanePropertiesKHR + */ + +VkResult +wsi_display_get_physical_device_display_plane_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPlanePropertiesKHR *properties) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector; + int c; + + if (!properties) { + *property_count = wsi->mode_res->count_connectors; + return VK_SUCCESS; + } + + for (c = 0; c < wsi->mode_res->count_connectors; c++) { + connector = wsi_display_find_connector(wsi_device, wsi->mode_res->connectors[c]); + if (c < *property_count) { + if (connector && connector->active) { + properties[c].currentDisplay = wsi_display_connector_to_handle(connector); + properties[c].currentStackIndex = c; + } else { + properties[c].currentDisplay = NULL; + properties[c].currentStackIndex = 0; + } + } + } + + if (c <= *property_count) + return VK_SUCCESS; + + return VK_INCOMPLETE; +} + +/* + * Implement vkGetDisplayPlaneSupportedDisplaysKHR + */ + +VkResult +wsi_display_get_display_plane_supported_displays(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t plane_index, + uint32_t *display_count, + VkDisplayKHR *displays) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + struct wsi_display_connector *connector; + + if (displays == NULL) { + *display_count = 1; + return VK_SUCCESS; + } + + if (*display_count < 1) + return VK_INCOMPLETE; + + if (plane_index >= wsi->mode_res->count_connectors) + return VK_ERROR_OUT_OF_DATE_KHR; + + connector = wsi_display_find_connector(wsi_device, wsi->mode_res->connectors[plane_index]); + + if (connector) { + *displays = wsi_display_connector_to_handle(connector); + *display_count = 1; + } else { + *display_count = 0; + } + + return VK_SUCCESS; +} + +/* + * Implement vkGetDisplayModePropertiesKHR + */ + +VkResult +wsi_display_get_display_mode_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + uint32_t *property_count, + VkDisplayModePropertiesKHR *properties) +{ + struct wsi_display_connector *connector = wsi_display_connector_from_handle(display); + int m, i; + struct wsi_display_mode *display_mode; + + if (properties == NULL) { + *property_count = connector->connector->count_modes; + return VK_SUCCESS; + } + + i = 0; + for (m = 0; m < connector->connector->count_modes; m++) { + display_mode = wsi_display_find_mode(wsi_device, connector, &connector->connector->modes[m]); + if (display_mode) { + if (i < *property_count) { + properties[i].displayMode = wsi_display_mode_to_handle(display_mode); + properties[i].parameters.visibleRegion.width = display_mode->mode.hdisplay; + properties[i].parameters.visibleRegion.height = display_mode->mode.vdisplay; + properties[i].parameters.refreshRate = display_mode->mode.vrefresh; + } + i++; + } + } + + if (i <= *property_count) + return VK_SUCCESS; + + return VK_INCOMPLETE; +} + +VkResult +wsi_get_display_plane_capabilities(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayModeKHR mode_khr, + uint32_t plane_index, + VkDisplayPlaneCapabilitiesKHR *capabilities) +{ + struct wsi_display_mode *mode = wsi_display_mode_from_handle(mode_khr); + + /* XXX use actual values */ + capabilities->supportedAlpha = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; + capabilities->minSrcPosition.x = 0; + capabilities->minSrcPosition.y = 0; + capabilities->maxSrcPosition.x = 0; + capabilities->maxSrcPosition.y = 0; + capabilities->minSrcExtent.width = mode->mode.hdisplay; + capabilities->minSrcExtent.height = mode->mode.vdisplay; + capabilities->maxSrcExtent.width = mode->mode.hdisplay; + capabilities->maxSrcExtent.height = mode->mode.vdisplay; + capabilities->minDstPosition.x = 0; + capabilities->minDstPosition.y = 0; + capabilities->maxDstPosition.x = 0; + capabilities->maxDstPosition.y = 0; + capabilities->minDstExtent.width = mode->mode.hdisplay; + capabilities->minDstExtent.height = mode->mode.vdisplay; + capabilities->maxDstExtent.width = mode->mode.hdisplay; + capabilities->maxDstExtent.height = mode->mode.vdisplay; + return VK_SUCCESS; +} + +VkResult +wsi_create_display_surface(VkInstance instance, + const VkAllocationCallbacks *allocator, + const VkDisplaySurfaceCreateInfoKHR *create_info, + VkSurfaceKHR *surface_khr) +{ + VkIcdSurfaceDisplay *surface; + + surface = vk_alloc(allocator, sizeof *surface, 8, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + if (surface == NULL) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + surface->base.platform = VK_ICD_WSI_PLATFORM_DISPLAY; + + surface->displayMode = create_info->displayMode; + surface->planeIndex = create_info->planeIndex; + surface->planeStackIndex = create_info->planeStackIndex; + surface->transform = create_info->transform; + surface->globalAlpha = create_info->globalAlpha; + surface->alphaMode = create_info->alphaMode; + surface->imageExtent = create_info->imageExtent; + + *surface_khr = VkIcdSurfaceBase_to_handle(&surface->base); + return VK_SUCCESS; +} + + +static VkResult +wsi_display_surface_get_support(VkIcdSurfaceBase *surface, + struct wsi_device *wsi_device, + const VkAllocationCallbacks *allocator, + uint32_t queueFamilyIndex, + int local_fd, + bool can_handle_different_gpu, + VkBool32* pSupported) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + *pSupported = wsi->master_fd >= 0; + + return VK_SUCCESS; +} + +static VkResult +wsi_display_surface_get_capabilities(VkIcdSurfaceBase *surface_base, + VkSurfaceCapabilitiesKHR* caps) +{ + VkIcdSurfaceDisplay *surface = (VkIcdSurfaceDisplay *) surface_base; + wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode); + + caps->currentExtent.width = mode->mode.hdisplay; + caps->currentExtent.height = mode->mode.vdisplay; + + /* XXX Figure out extents based on driver capabilities */ + caps->maxImageExtent = caps->minImageExtent = caps->currentExtent; + + caps->supportedCompositeAlpha = (VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR | + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR); + + caps->minImageCount = 2; + caps->maxImageCount = 0; + + caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + caps->maxImageArrayLayers = 1; + caps->supportedUsageFlags = + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + return VK_SUCCESS; +} + +static const VkSurfaceFormatKHR available_surface_formats[] = { + { .format = VK_FORMAT_B8G8R8A8_SRGB, }, + { .format = VK_FORMAT_B8G8R8A8_UNORM, }, +}; + +static VkResult +wsi_display_surface_get_formats(VkIcdSurfaceBase *icd_surface, + struct wsi_device *wsi_device, + uint32_t *surface_format_count, + VkSurfaceFormatKHR *surface_formats) +{ + if (surface_formats == NULL) { + *surface_format_count = ARRAY_SIZE(available_surface_formats); + return VK_SUCCESS; + } + + *surface_format_count = MIN2(*surface_format_count, ARRAY_SIZE(available_surface_formats)); + typed_memcpy(surface_formats, available_surface_formats, *surface_format_count); + + return *surface_format_count < ARRAY_SIZE(available_surface_formats) ? + VK_INCOMPLETE : VK_SUCCESS; +} + +static const VkPresentModeKHR available_present_modes[] = { + VK_PRESENT_MODE_IMMEDIATE_KHR, + VK_PRESENT_MODE_MAILBOX_KHR, + VK_PRESENT_MODE_FIFO_KHR, +}; + +static VkResult +wsi_display_surface_get_present_modes(VkIcdSurfaceBase *surface, + uint32_t *present_mode_count, + VkPresentModeKHR *present_modes) +{ + if (present_modes == NULL) { + *present_mode_count = ARRAY_SIZE(available_present_modes); + return VK_SUCCESS; + } + + *present_mode_count = MIN2(*present_mode_count, ARRAY_SIZE(available_present_modes)); + typed_memcpy(present_modes, available_present_modes, *present_mode_count); + + if (*present_mode_count < ARRAY_SIZE(available_present_modes)) + return VK_INCOMPLETE; + return VK_SUCCESS; +} + +static VkResult +wsi_display_image_init(VkDevice device_h, + struct wsi_swapchain *drv_chain, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + struct wsi_display_image *image) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain; + VkResult result; + uint32_t row_pitch; + uint32_t offset; + uint32_t handle; + uint32_t size; + int ret; + + result = chain->base.image_fns->create_wsi_image(device_h, + create_info, + allocator, + chain->base.needs_linear_copy, + false, + &image->image, + &image->memory, + &size, + &offset, + &row_pitch, + NULL, + &handle); + + if (result != VK_SUCCESS) + return result; + + if (chain->base.needs_linear_copy) { + result = chain->base.image_fns->create_wsi_image(device_h, + create_info, + allocator, + chain->base.needs_linear_copy, + true, + &image->linear_image, + &image->linear_memory, + &size, + &offset, + &row_pitch, + NULL, + &handle); + + if (result != VK_SUCCESS) { + goto fail_linear; + } + } + + image->bo_handle = handle; + + /* XXX extract depth and bpp from image somehow */ + ret = drmModeAddFB(chain->fd, create_info->imageExtent.width, create_info->imageExtent.height, + 24, 32, row_pitch, handle, &image->fb_id); + + if (ret) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail_fb; + } + + return VK_SUCCESS; + +fail_fb: + + if (chain->base.needs_linear_copy) + chain->base.image_fns->free_wsi_image(device_h, allocator, + image->linear_image, image->linear_memory); + +fail_linear: + + chain->base.image_fns->free_wsi_image(device_h, allocator, + image->image, image->memory); + + return result; +} + +static void +wsi_display_image_finish(struct wsi_swapchain *drv_chain, + const VkAllocationCallbacks *allocator, + struct wsi_display_image *image) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain; + + if (chain->base.needs_linear_copy) { + chain->base.image_fns->free_wsi_image(chain->base.device, allocator, + image->linear_image, image->linear_memory); + } + chain->base.image_fns->free_wsi_image(chain->base.device, allocator, + image->image, image->memory); +} + +static VkResult +wsi_display_swapchain_destroy(struct wsi_swapchain *drv_chain, + const VkAllocationCallbacks *allocator) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain; + + for (uint32_t i = 0; i < chain->base.image_count; i++) + wsi_display_image_finish(drv_chain, allocator, &chain->images[i]); + vk_free(allocator, chain); + return VK_SUCCESS; +} + +static VkResult +wsi_display_get_images(struct wsi_swapchain *drv_chain, + uint32_t *count, + VkImage *images) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain; + uint32_t ret_count; + VkResult result; + + if (images == NULL) { + *count = chain->base.image_count; + return VK_SUCCESS; + } + + result = VK_SUCCESS; + ret_count = chain->base.image_count; + if (chain->base.image_count > *count) { + ret_count = *count; + result = VK_INCOMPLETE; + } + + for (uint32_t i = 0; i < ret_count; i++) + images[i] = chain->images[i].image; + + return result; +} + +static void +wsi_display_get_image_and_linear(struct wsi_swapchain *drv_chain, + int image_index, + VkImage *image, + VkImage *linear_image) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)drv_chain; + *image = chain->images[image_index].image; + *linear_image = chain->images[image_index].linear_image; +} + +static void +wsi_display_page_flip_handler2(int fd, + unsigned int frame, + unsigned int sec, + unsigned int usec, + uint32_t crtc_id, + void *data) +{ + struct wsi_display_image *image = data; + + /* XXX Deal with multiple CRTCs */ + image->busy = false; + image->sec = sec; + image->usec = usec; +} + +static void wsi_display_page_flip_handler(int fd, unsigned int frame, + unsigned int sec, unsigned int usec, void *data) +{ + wsi_display_page_flip_handler2(fd, frame, sec, usec, 0, data); +} + +static uint64_t wsi_get_current_time(void) +{ + uint64_t current_time; + struct timespec tv; + + clock_gettime(CLOCK_MONOTONIC, &tv); + current_time = tv.tv_nsec + tv.tv_sec*1000000000ull; + return current_time; +} + +static int +wsi_display_wait_for_event(struct wsi_swapchain *drv_chain, + uint64_t timeout_ns) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)drv_chain; + int ret_poll; + int timeout_ms; + struct pollfd pollfd; + drmEventContext event_context = { + .version = DRM_EVENT_CONTEXT_VERSION, + .page_flip_handler = wsi_display_page_flip_handler, +#if DRM_EVENT_CONTEXT_VERSION >= 3 + .page_flip_handler2 = wsi_display_page_flip_handler2, +#endif + }; + + if (timeout_ns == UINT64_MAX) + timeout_ms = -1; + else + timeout_ms = timeout_ns / (1000 * 1000); + + pollfd.fd = chain->fd; + pollfd.events = POLLIN; + + ret_poll = poll(&pollfd, 1, timeout_ms); + (void) drmHandleEvent(chain->fd, &event_context); + return ret_poll; +} + +static VkResult +wsi_display_acquire_next_image(struct wsi_swapchain *drv_chain, + uint64_t timeout, + VkSemaphore semaphore, + uint32_t *image_index) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *)drv_chain; + uint64_t abs_timeout; + int64_t timeout_remaining; + int ret = 1; + + if (timeout != 0 && timeout != UINT64_MAX) + abs_timeout = wsi_get_current_time() + timeout; + + for (;;) { + for (uint32_t i = 0; i < chain->base.image_count; i++) { + if (!chain->images[i].busy) { + *image_index = i; + chain->images[i].busy = true; + return VK_SUCCESS; + } + } + + if (ret == 0) + return VK_TIMEOUT; + if (ret < 0) + return VK_ERROR_OUT_OF_DATE_KHR; + + ret = wsi_display_wait_for_event(drv_chain, timeout); + + if (ret < 0) + return VK_ERROR_OUT_OF_DATE_KHR; + + if (timeout != 0 && timeout != UINT64_MAX) { + timeout_remaining = abs_timeout - wsi_get_current_time(); + if (timeout_remaining < 0) + timeout_remaining = 0; + timeout = timeout_remaining; + } + } +} + +static uint32_t +wsi_display_select_crtc(struct wsi_display_connector *connector) +{ + struct wsi_display *wsi = connector->wsi; + int c; + drmModeResPtr mode_res = wsi->mode_res; + drmModeCrtcPtr crtc; + uint32_t crtc_id; + + if (!mode_res) + return 0; + + crtc_id = 0; + for (c = 0; crtc_id == 0 && c < mode_res->count_crtcs; c++) { + crtc = drmModeGetCrtc(wsi->master_fd, mode_res->crtcs[c]); + if (crtc && crtc->buffer_id == 0) + crtc_id = crtc->crtc_id; + drmModeFreeCrtc(crtc); + } + return crtc_id; +} + +static VkResult +wsi_display_queue_present(struct wsi_swapchain *drv_chain, + uint32_t image_index, + const VkPresentRegionKHR *damage) +{ + struct wsi_display_swapchain *chain = (struct wsi_display_swapchain *) drv_chain; + struct wsi_display_image *image = &chain->images[image_index]; + VkIcdSurfaceDisplay *surface = chain->surface; + wsi_display_mode *mode = wsi_display_mode_from_handle(surface->displayMode); + wsi_display_connector *connector = mode->connector; + drmModeModeInfoPtr mode_info; + int ret; + + if (!connector->crtc_id) { + connector->crtc_id = wsi_display_select_crtc(connector); + if (!connector->crtc_id) + return VK_ERROR_OUT_OF_DATE_KHR; + } + + for (;;) { + ret = drmModePageFlip(chain->fd, connector->crtc_id, image->fb_id, + DRM_MODE_PAGE_FLIP_EVENT, image); + if (ret == 0) { + image->busy = true; + connector->active = true; + return VK_SUCCESS; + } + + if (ret) { + switch(-ret) { + case EINVAL: + + /* XXX allow setting of position */ + mode_info = wsi_display_find_mode_info(connector, mode); + if (!mode_info) + return VK_ERROR_OUT_OF_DATE_KHR; + + ret = drmModeSetCrtc(chain->fd, connector->crtc_id, image->fb_id, 0, 0, + &connector->id, 1, mode_info); + + if (ret == 0) { + image->busy = true; + connector->active = true; + return VK_SUCCESS; + } + break; + case EACCES: + usleep(1000 * 1000); + break; + default: + return VK_ERROR_OUT_OF_DATE_KHR; + } + } + } +} + +static VkResult +wsi_display_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, + VkDevice device, + struct wsi_device *wsi_device, + int local_fd, + const VkSwapchainCreateInfoKHR *create_info, + const VkAllocationCallbacks *allocator, + const struct wsi_image_fns *image_fns, + struct wsi_swapchain **swapchain_out) +{ + VkResult result; + assert(create_info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); + + struct wsi_display_swapchain *chain; + const unsigned num_images = create_info->minImageCount; + size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]); + + chain = vk_alloc(allocator, size, 0, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + + if (chain == NULL) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + chain->base.device = device; + chain->base.destroy = wsi_display_swapchain_destroy; + chain->base.get_images = wsi_display_get_images; + chain->base.get_image_and_linear = wsi_display_get_image_and_linear; + chain->base.acquire_next_image = wsi_display_acquire_next_image; + chain->base.queue_present = wsi_display_queue_present; + chain->base.image_fns = image_fns; + chain->base.present_mode = create_info->presentMode; + chain->base.image_count = num_images; + + chain->base.needs_linear_copy = false; + + chain->fd = local_fd; + + chain->surface = (VkIcdSurfaceDisplay *) icd_surface; + + for (uint32_t image = 0; image < chain->base.image_count; image++) { + result = wsi_display_image_init(device, &chain->base, create_info, allocator, + &chain->images[image]); + if (result != VK_SUCCESS) + goto fail_init_images; + } + + *swapchain_out = &chain->base; + + return VK_SUCCESS; +fail_init_images: + return result; +} + +VkResult +wsi_display_init_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc, + VkPhysicalDevice physical_device, + int master_fd, + const struct wsi_callbacks *cbs) +{ + struct wsi_display *wsi; + VkResult result; + + wsi = vk_alloc(alloc, sizeof(*wsi), 8, + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); + if (!wsi) { + result = VK_ERROR_OUT_OF_HOST_MEMORY; + goto fail; + } + memset(wsi, '\0', sizeof (*wsi)); + + wsi->master_fd = master_fd; + wsi->physical_device = physical_device; + wsi->alloc = alloc; + wsi->cbs = cbs; + + LIST_INITHEAD(&wsi->display_modes); + LIST_INITHEAD(&wsi->connectors); + + wsi->base.get_support = wsi_display_surface_get_support; + wsi->base.get_capabilities = wsi_display_surface_get_capabilities; + wsi->base.get_formats = wsi_display_surface_get_formats; + wsi->base.get_present_modes = wsi_display_surface_get_present_modes; + wsi->base.create_swapchain = wsi_display_surface_create_swapchain; + + wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY] = &wsi->base; + + return VK_SUCCESS; + +fail: + return result; +} + +void +wsi_display_finish_wsi(struct wsi_device *wsi_device, + const VkAllocationCallbacks *alloc) +{ + struct wsi_display *wsi = (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_PLATFORM_DISPLAY]; + + if (wsi) { + vk_free(alloc, wsi); + } +} diff --git a/src/vulkan/wsi/wsi_common_display.h b/src/vulkan/wsi/wsi_common_display.h new file mode 100644 index 0000000000..7c3db48758 --- /dev/null +++ b/src/vulkan/wsi/wsi_common_display.h @@ -0,0 +1,80 @@ +/* + * Copyright © 2017 Keith Packard + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef WSI_COMMON_DISPLAY_H +#define WSI_COMMON_DISPLAY_H + +#include "wsi_common.h" +#include +#include +#include + +#define typed_memcpy(dest, src, count) ({ \ + STATIC_ASSERT(sizeof(*src) == sizeof(*dest)); \ + memcpy((dest), (src), (count) * sizeof(*(src))); \ +}) + +VkBool32 +wsi_display_get_presentation_support(struct wsi_device *wsi_device); + +VkResult +wsi_display_set_master_fd(VkInstance instance, + struct wsi_device *wsi_device, + int master_fd); +VkResult +wsi_display_get_physical_device_display_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPropertiesKHR *properties); +VkResult +wsi_display_get_physical_device_display_plane_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t *property_count, + VkDisplayPlanePropertiesKHR *properties); + +VkResult +wsi_display_get_display_plane_supported_displays(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + uint32_t plane_index, + uint32_t *display_count, + VkDisplayKHR *displays); +VkResult +wsi_display_get_display_mode_properties(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayKHR display, + uint32_t *property_count, + VkDisplayModePropertiesKHR *properties); + +VkResult +wsi_get_display_plane_capabilities(VkPhysicalDevice physical_device, + struct wsi_device *wsi_device, + VkDisplayModeKHR mode_khr, + uint32_t plane_index, + VkDisplayPlaneCapabilitiesKHR *capabilities); + +VkResult +wsi_create_display_surface(VkInstance instance, + const VkAllocationCallbacks *pAllocator, + const VkDisplaySurfaceCreateInfoKHR *pCreateInfo, + VkSurfaceKHR *pSurface); + +#endif diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c index 5613283d9d..bba389faec 100644 --- a/src/vulkan/wsi/wsi_common_wayland.c +++ b/src/vulkan/wsi/wsi_common_wayland.c @@ -659,7 +659,7 @@ wsi_wl_image_init(struct wsi_wl_swapchain *chain, &size, &offset, &row_pitch, - &fd); + &fd, NULL); if (result != VK_SUCCESS) return result; diff --git a/src/vulkan/wsi/wsi_common_x11.c b/src/vulkan/wsi/wsi_common_x11.c index c399aae5af..c8659ee9c5 100644 --- a/src/vulkan/wsi/wsi_common_x11.c +++ b/src/vulkan/wsi/wsi_common_x11.c @@ -944,7 +944,7 @@ x11_image_init(VkDevice device_h, struct x11_swapchain *chain, &size, &offset, &row_pitch, - &fd); + &fd, NULL); if (result != VK_SUCCESS) return result; @@ -959,7 +959,7 @@ x11_image_init(VkDevice device_h, struct x11_swapchain *chain, &size, &offset, &row_pitch, - &fd); + &fd, NULL); if (result != VK_SUCCESS) { chain->base.image_fns->free_wsi_image(device_h, pAllocator, image->image, image->memory); -- 2.11.0