<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">Sorry for the flood of comments. Most of it's pretty trivial. The one that has me the most concerned is the random appearence of 0.1s in some strange places. Other than that, I'm reasonably happy with it.<br></div><div class="gmail_quote"><br></div><div class="gmail_quote">On Thu, Jun 14, 2018 at 7:52 PM, Keith Packard <span dir="ltr"><<a href="mailto:keithp@keithp.com" target="_blank">keithp@keithp.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">This extension provides fences and frame count information to direct<br>
display contexts. It uses new kernel ioctls to provide 64-bits of<br>
vblank sequence and nanosecond resolution.<br>
<br>
v2: Remove DRM_CRTC_SEQUENCE_FIRST_PIXEL_<wbr>OUT flag. This has<br>
been removed from the proposed kernel API.<br>
<br>
Add NULL parameter to drmCrtcQueueSequence ioctl as we<br>
don't care what sequence the event was actually queued to.<br>
<br>
v3: Adapt to pthread clock switch to MONOTONIC<br>
<br>
v4: Fix scope for wsi_display_mode andwsi_display_connector allocs<br>
<br>
Suggested-by: Jason Ekstrand <<a href="mailto:jason@jlekstrand.net">jason@jlekstrand.net</a>><br>
<br>
v5: Adopt Jason Ekstrand's coding conventions<br>
<br>
Declare variables at first use, eliminate extra whitespace between<br>
types and names. Wrap lines to 80 columns.<br>
<br>
Use wsi_rel_to_abs_time helper function to convert relative<br>
timeouts to absolute timeouts without causing overflow.<br>
<br>
Suggested-by: Jason Ekstrand <<a href="mailto:jason.ekstrand@intel.com">jason.ekstrand@intel.com</a>><br>
<br>
Signed-off-by: Keith Packard <<a href="mailto:keithp@keithp.com">keithp@keithp.com</a>><br>
---<br>
src/vulkan/wsi/wsi_common.h | 10 +<br>
src/vulkan/wsi/wsi_common_<wbr>display.c | 307 +++++++++++++++++++++++++++-<br>
src/vulkan/wsi/wsi_common_<wbr>display.h | 29 +++<br>
3 files changed, 345 insertions(+), 1 deletion(-)<br>
<br>
diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h<br>
index 054aad23c1c..081fe1dcf8d 100644<br>
--- a/src/vulkan/wsi/wsi_common.h<br>
+++ b/src/vulkan/wsi/wsi_common.h<br>
@@ -66,6 +66,16 @@ struct wsi_format_modifier_<wbr>properties_list {<br>
struct wsi_format_modifier_properties *modifier_properties;<br>
};<br>
<br>
+struct wsi_fence {<br>
+ VkDevice device;<br>
+ const struct wsi_device *wsi_device;<br>
+ VkDisplayKHR display;<br>
+ const VkAllocationCallbacks *alloc;<br>
+ bool (*wait)(struct wsi_fence *fence,<br>
+ bool absolute, uint64_t timeout);<br>
+ void (*destroy)(struct wsi_fence *fence);<br>
+};<br>
+<br>
struct wsi_interface;<br>
<br>
#define VK_ICD_WSI_PLATFORM_MAX (VK_ICD_WSI_PLATFORM_DISPLAY + 1)<br>
diff --git a/src/vulkan/wsi/wsi_common_<wbr>display.c b/src/vulkan/wsi/wsi_common_<wbr>display.c<br>
index 504f7741d73..40eaa6a322e 100644<br>
--- a/src/vulkan/wsi/wsi_common_<wbr>display.c<br>
+++ b/src/vulkan/wsi/wsi_common_<wbr>display.c<br>
@@ -79,6 +79,7 @@ typedef struct wsi_display_connector {<br>
struct list_head display_modes;<br>
wsi_display_mode *current_mode;<br>
drmModeModeInfo current_drm_mode;<br>
+ uint32_t dpms_property;<br>
#ifdef VK_USE_PLATFORM_XLIB_XRANDR_<wbr>EXT<br>
xcb_randr_output_t output;<br>
#endif<br>
@@ -132,6 +133,15 @@ struct wsi_display_swapchain {<br>
struct wsi_display_image images[0];<br>
};<br>
<br>
+struct wsi_display_fence {<br>
+ struct wsi_fence base;<br>
+ bool event_received;<br>
+ bool destroyed;<br>
+ uint64_t sequence;<br>
+};<br>
+<br>
+static uint64_t fence_sequence;<br>
+<br>
ICD_DEFINE_NONDISP_HANDLE_<wbr>CASTS(wsi_display_mode, VkDisplayModeKHR)<br>
ICD_DEFINE_NONDISP_HANDLE_<wbr>CASTS(wsi_display_connector, VkDisplayKHR)<br>
<br>
@@ -307,6 +317,19 @@ wsi_display_get_connector(<wbr>struct wsi_device *wsi_device,<br>
<br>
connector->connected = drm_connector->connection != DRM_MODE_DISCONNECTED;<br>
<br>
+ /* Look for a DPMS property */<br>
+ for (int p = 0; p < drm_connector->count_props; p++) {<br>
+ drmModePropertyPtr prop = drmModeGetProperty(wsi->fd,<br>
+ drm_connector->props[p]);<br>
+ if (!prop)<br>
+ continue;<br>
+ if (prop->flags & DRM_MODE_PROP_ENUM) {<br>
+ if (!strcmp(prop->name, "DPMS"))<br>
+ connector->dpms_property = drm_connector->props[p];<br></blockquote><div><br></div><div>break?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ }<br>
+ drmModeFreeProperty(prop);<br>
+ }<br>
+<br>
/* Mark all connector modes as invalid */<br>
wsi_display_invalidate_<wbr>connector_modes(wsi_device, connector);<br>
<br>
@@ -663,7 +686,7 @@ wsi_display_surface_get_<wbr>capabilities2ext(<wbr>VkIcdSurfaceBase *icd_surface,<br>
caps->currentTransform = khr_caps.currentTransform;<br>
caps->supportedCompositeAlpha = khr_caps.<wbr>supportedCompositeAlpha;<br>
caps->supportedUsageFlags = khr_caps.supportedUsageFlags;<br>
- caps->supportedSurfaceCounters = 0;<br>
+ caps->supportedSurfaceCounters = VK_SURFACE_COUNTER_VBLANK_EXT;<br>
return ret;<br>
}<br>
<br>
@@ -896,12 +919,21 @@ static void wsi_display_page_flip_handler(<wbr>int fd,<br>
wsi_display_page_flip_<wbr>handler2(fd, frame, sec, usec, 0, data);<br>
}<br>
<br>
+static void wsi_display_vblank_handler(int fd, unsigned int frame,<br>
+ unsigned int sec, unsigned int usec,<br>
+ void *data);<br>
+<br>
+static void wsi_display_sequence_handler(<wbr>int fd, uint64_t frame,<br>
+ uint64_t ns, uint64_t user_data);<br>
+<br>
static drmEventContext event_context = {<br>
.version = DRM_EVENT_CONTEXT_VERSION,<br>
.page_flip_handler = wsi_display_page_flip_handler,<br>
#if DRM_EVENT_CONTEXT_VERSION >= 3<br>
.page_flip_handler2 = wsi_display_page_flip_<wbr>handler2,<br>
#endif<br>
+ .vblank_handler = wsi_display_vblank_handler,<br>
+ .sequence_handler = wsi_display_sequence_handler,<br>
};<br>
<br>
static void *<br>
@@ -1168,6 +1200,147 @@ bail:<br>
<br>
}<br>
<br>
+static bool<br>
+wsi_display_fence_wait(struct wsi_fence *fence_wsi,<br>
+ bool absolute,<br>
+ uint64_t timeout)<br></blockquote><div><br></div><div>Would it make more sense for this function to return a VkResult? Then you could tell the difference between success, timeout, and some other failure. I guess the only other thing to return would be VK_ERROR_DEVICE_LOST which seems pretty harsh but, then again, pthread_timed_wait just failed so that's also really bad.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+{<br>
+ const struct wsi_device *wsi_device = fence_wsi->wsi_device;<br>
+ struct wsi_display *wsi =<br>
+ (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_<wbr>PLATFORM_DISPLAY];<br>
+ struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi;<br>
+<br>
+ if (!absolute)<br>
+ timeout = wsi_rel_to_abs_time(timeout);<br></blockquote><div><br></div><div>Are relative times really useful? I suspect it doesn't save you more than a couple of lines and it makes the interface weird.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+ wsi_display_debug("%9lu wait fence %lu %ld\n",<br>
+ pthread_self(), fence->sequence,<br>
+ (int64_t) (timeout - wsi_get_current_monotonic()));<br>
+ wsi_display_debug_code(uint64_<wbr>t start_ns = wsi_get_current_monotonic());<br>
+ pthread_mutex_lock(&wsi->wait_<wbr>mutex);<br>
+<br>
+ bool value = false;<br>
+ int ret = 0;<br>
+ for (;;) {<br>
+ if (fence->event_received) {<br>
+ wsi_display_debug("%9lu fence %lu passed\n",<br>
+ pthread_self(), fence->sequence);<br>
+ value = true;<br>
+ break;<br>
+ }<br>
+<br>
+ if (ret == ETIMEDOUT) {<br>
+ wsi_display_debug("%9lu fence %lu timeout\n",<br>
+ pthread_self(), fence->sequence);<br>
+ value = false;<br>
+ break;<br>
+ }<br>
+<br>
+ ret = wsi_display_wait_for_event(<wbr>wsi, timeout);<br>
+<br>
+ if (ret && ret != ETIMEDOUT) {<br>
+ wsi_display_debug("%9lu fence %lu error\n",<br>
+ pthread_self(), fence->sequence);<br>
+ value = false;<br>
+ break;<br>
+ }<br>
+ }<br>
+ pthread_mutex_unlock(&wsi-><wbr>wait_mutex);<br>
+ wsi_display_debug("%9lu fence wait %f ms\n",<br>
+ pthread_self(),<br>
+ ((int64_t) (wsi_get_current_monotonic() - start_ns)) /<br>
+ 1.0e6);<br>
+ return value;<br>
+}<br>
+<br>
+static void<br>
+wsi_display_fence_check_free(<wbr>struct wsi_display_fence *fence)<br>
+{<br>
+ if (fence->event_received && fence->destroyed)<br>
+ vk_free(fence->base.alloc, fence);<br>
+}<br>
+<br>
+static void<br>
+wsi_display_fence_destroy(<wbr>struct wsi_fence *fence_wsi)<br>
+{<br>
+ struct wsi_display_fence *fence = (struct wsi_display_fence *) fence_wsi;<br>
+<br></blockquote><div><br></div><div>An assert(!fence->destroyed) in here might be useful to guard against double-frees.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ fence->destroyed = true;<br>
+ wsi_display_fence_check_free(<wbr>fence);<br>
+}<br>
+<br>
+static struct wsi_display_fence *<br>
+wsi_display_fence_alloc(<wbr>VkDevice device,<br>
+ const struct wsi_device *wsi_device,<br>
+ VkDisplayKHR display,<br>
+ const VkAllocationCallbacks *allocator)<br>
+{<br>
+ struct wsi_display_fence *fence =<br>
+ vk_zalloc(allocator, sizeof (*fence),<br>
+ 8, VK_SYSTEM_ALLOCATION_SCOPE_<wbr>OBJECT);<br>
+<br>
+ if (!fence)<br>
+ return NULL;<br>
+<br>
+ fence->base.device = device;<br>
+ fence->base.display = display;<br>
+ fence->base.wsi_device = wsi_device;<br>
+ fence->base.alloc = allocator;<br>
+ fence->base.wait = wsi_display_fence_wait;<br>
+ fence->base.destroy = wsi_display_fence_destroy;<br>
+ fence->event_received = false;<br>
+ fence->destroyed = false;<br>
+ fence->sequence = ++fence_sequence;<br>
+ return fence;<br>
+}<br>
+<br>
+static VkResult<br>
+wsi_register_vblank_event(<wbr>struct wsi_display_fence *fence,<br>
+ const struct wsi_device *wsi_device,<br>
+ VkDisplayKHR display,<br>
+ uint32_t flags,<br>
+ uint64_t frame_requested,<br>
+ uint64_t *frame_queued)<br>
+{<br>
+ struct wsi_display *wsi =<br>
+ (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_<wbr>PLATFORM_DISPLAY];<br>
+ struct wsi_display_connector *connector =<br>
+ wsi_display_connector_from_<wbr>handle(display);<br>
+<br>
+ if (wsi->fd < 0)<br>
+ return VK_ERROR_INITIALIZATION_<wbr>FAILED;<br>
+<br>
+ for (;;) {<br>
+ int ret = drmCrtcQueueSequence(wsi->fd, connector->crtc_id,<br>
+ flags,<br>
+ frame_requested,<br>
+ frame_queued,<br>
+ (uint64_t) fence);<br>
+<br>
+ if (!ret)<br>
+ return VK_SUCCESS;<br>
+<br>
+ if (errno != ENOMEM) {<br>
+ wsi_display_debug("queue vblank event %lu failed\n", fence->sequence);<br>
+ struct timespec delay = {<br>
+ .tv_sec = 0,<br>
+ .tv_nsec = 100000000ull,<br>
+ };<br>
+ nanosleep(&delay, NULL);<br>
+ return VK_ERROR_OUT_OF_HOST_MEMORY;<br></blockquote><div><br></div><div>Why are we sleeping for 0.1s before we return? That seems fishy.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ }<br>
+<br>
+ pthread_mutex_lock(&wsi->wait_<wbr>mutex);<br>
+ ret = wsi_display_wait_for_event(<wbr>wsi, wsi_rel_to_abs_time(<wbr>100000000ull));<br></blockquote><div><br></div><div>What's with the magic number?<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ pthread_mutex_unlock(&wsi-><wbr>wait_mutex);<br>
+<br>
+ if (ret) {<br>
+ wsi_display_debug("vblank queue full, event wait failed\n");<br>
+ return VK_ERROR_OUT_OF_HOST_MEMORY;<br>
+ }<br>
+ }<br>
+}<br>
+<br>
/*<br>
* Check to see if the kernel has no flip queued and if there's an image<br>
* waiting to be displayed.<br>
@@ -1963,3 +2136,135 @@ wsi_get_randr_output_display(<wbr>VkPhysicalDevice physical_device,<br>
}<br>
<br>
#endif<br>
+<br>
+/* VK_EXT_display_control */<br>
+VkResult<br>
+wsi_display_power_control(<wbr>VkDevice device,<br>
+ struct wsi_device *wsi_device,<br>
+ VkDisplayKHR display,<br>
+ const VkDisplayPowerInfoEXT *display_power_info)<br>
+{<br>
+ struct wsi_display *wsi =<br>
+ (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_<wbr>PLATFORM_DISPLAY];<br>
+ struct wsi_display_connector *connector =<br>
+ wsi_display_connector_from_<wbr>handle(display);<br>
+ int mode;<br>
+<br>
+ if (wsi->fd < 0)<br>
+ return VK_ERROR_INITIALIZATION_<wbr>FAILED;<br>
+<br>
+ switch (display_power_info-><wbr>powerState) {<br>
+ case VK_DISPLAY_POWER_STATE_OFF_<wbr>EXT:<br>
+ mode = DRM_MODE_DPMS_OFF;<br>
+ break;<br>
+ case VK_DISPLAY_POWER_STATE_<wbr>SUSPEND_EXT:<br>
+ mode = DRM_MODE_DPMS_SUSPEND;<br>
+ break;<br>
+ default:<br>
+ mode = DRM_MODE_DPMS_ON;<br>
+ break;<br>
+ }<br>
+ drmModeConnectorSetProperty(<wbr>wsi->fd,<br>
+ connector->id,<br>
+ connector->dpms_property,<br>
+ mode);<br>
+ return VK_SUCCESS;<br>
+}<br>
+<br>
+VkResult<br>
+wsi_register_device_event(<wbr>VkDevice device,<br>
+ struct wsi_device *wsi_device,<br>
+ const VkDeviceEventInfoEXT *device_event_info,<br>
+ const VkAllocationCallbacks *allocator,<br>
+ struct wsi_fence **fence_p)<br>
+{<br>
+ return VK_ERROR_FEATURE_NOT_PRESENT;<br></blockquote><div><br></div><div>I don't think we're allowed to just not implemnet this. At the very least, we should accept the event and never trigger it. Better would be to actually wire up hotplug detection. I have no idea how insane that would be to do. :-P<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+}<br>
+<br>
+VkResult<br>
+wsi_register_display_event(<wbr>VkDevice device,<br>
+ struct wsi_device *wsi_device,<br>
+ VkDisplayKHR display,<br>
+ const VkDisplayEventInfoEXT *display_event_info,<br>
+ const VkAllocationCallbacks *allocator,<br>
+ struct wsi_fence **fence_p)<br>
+{<br>
+ struct wsi_display_fence *fence;<br>
+ VkResult ret = VK_ERROR_FEATURE_NOT_PRESENT;<br>
+<br>
+ switch (display_event_info-><wbr>displayEvent) {<br>
+ case VK_DISPLAY_EVENT_TYPE_FIRST_<wbr>PIXEL_OUT_EXT:<br>
+<br>
+ fence = wsi_display_fence_alloc(<wbr>device, wsi_device, display, allocator);<br>
+<br>
+ if (!fence)<br>
+ return VK_ERROR_OUT_OF_HOST_MEMORY;<br></blockquote><div><br></div><div>Both RegisterDeviceEvent and RegisterDisplayEvent say they can only return VK_SUCCESS. We should submit a MR against the extensions to also allow OUT_OF_HOST_MEMORY at the very least.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+ ret = wsi_register_vblank_event(<wbr>fence, wsi_device, display,<br>
+ DRM_CRTC_SEQUENCE_RELATIVE, 1, NULL);<br>
+<br>
+ if (ret == VK_SUCCESS)<br>
+ *fence_p = &fence->base;<br>
+ else if (fence != NULL)<br>
+ vk_free(allocator, fence);<br>
+<br>
+ break;<br>
+ }<br>
+<br>
+ return ret;<br>
+}<br>
+<br>
+<br>
+VkResult<br>
+wsi_get_swapchain_counter(<wbr>VkDevice device,<br>
+ struct wsi_device *wsi_device,<br>
+ VkSwapchainKHR _swapchain,<br>
+ VkSurfaceCounterFlagBitsEXT flag_bits,<br>
+ uint64_t *value)<br>
+{<br>
+ struct wsi_display *wsi =<br>
+ (struct wsi_display *) wsi_device->wsi[VK_ICD_WSI_<wbr>PLATFORM_DISPLAY];<br>
+ struct wsi_display_swapchain *swapchain =<br>
+ (struct wsi_display_swapchain *) wsi_swapchain_from_handle(_<wbr>swapchain);<br>
+ struct wsi_display_connector *connector =<br>
+ wsi_display_mode_from_handle(<wbr>swapchain->surface-><wbr>displayMode)->connector;<br>
+<br>
+ if (wsi->fd < 0)<br>
+ return VK_ERROR_INITIALIZATION_<wbr>FAILED;<br>
+<br>
+ if (!connector->active) {<br>
+ *value = 0;<br>
+ return VK_SUCCESS;<br>
+ }<br>
+<br>
+ int ret = drmCrtcGetSequence(wsi->fd, connector->crtc_id, value, NULL);<br>
+ if (ret)<br>
+ *value = 0;<br>
+<br>
+ return VK_SUCCESS;<br>
+}<br>
+<br>
+static void wsi_display_fence_event_<wbr>handler(struct wsi_display_fence *fence)<br>
+{<br>
+ fence->event_received = true;<br>
+ wsi_display_fence_check_free(<wbr>fence);<br></blockquote><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+}<br>
+<br>
+static void wsi_display_vblank_handler(int fd, unsigned int frame,<br>
+ unsigned int sec, unsigned int usec,<br>
+ void *data)<br>
+{<br>
+ struct wsi_display_fence *fence = data;<br>
+<br>
+ wsi_display_fence_event_<wbr>handler(fence);<br>
+}<br>
+<br>
+static void wsi_display_sequence_handler(<wbr>int fd, uint64_t frame,<br>
+ uint64_t nsec, uint64_t user_data)<br>
+{<br>
+ struct wsi_display_fence *fence =<br>
+ (struct wsi_display_fence *) (uintptr_t) user_data;<br>
+<br>
+ wsi_display_fence_event_<wbr>handler(fence);<br></blockquote><div><br></div><div>Any particular reason to put these all the way down here? I think my preference would be to move wsi_display_fence_event_handler to right after wsi_display_fence_check_free and give it a predeclaration (instead of these two) and then move the sequence and vblank handlers to right above event_context since they're just little wrappers around wsi_display_fence_check_free. Sorry if that's a bit petty but it was hard to find wsi_display_fence_check_free all the way down here and it's really needed in order to understand the pseudo reference counting you're doing with fences.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+}<br>
+<br>
diff --git a/src/vulkan/wsi/wsi_common_<wbr>display.h b/src/vulkan/wsi/wsi_common_<wbr>display.h<br>
index 58447d2c6eb..2f760f825fb 100644<br>
--- a/src/vulkan/wsi/wsi_common_<wbr>display.h<br>
+++ b/src/vulkan/wsi/wsi_common_<wbr>display.h<br>
@@ -96,4 +96,33 @@ wsi_get_randr_output_display(<wbr>VkPhysicalDevice physical_device,<br>
<br>
#endif /* VK_USE_PLATFORM_XLIB_XRANDR_<wbr>EXT */<br>
<br>
+/* VK_EXT_display_control */<br>
+VkResult<br>
+wsi_display_power_control(<wbr>VkDevice device,<br>
+ struct wsi_device *wsi_device,<br>
+ VkDisplayKHR display,<br>
+ const VkDisplayPowerInfoEXT *display_power_info);<br>
+<br>
+VkResult<br>
+wsi_register_device_event(<wbr>VkDevice device,<br>
+ struct wsi_device *wsi_device,<br>
+ const VkDeviceEventInfoEXT *device_event_info,<br>
+ const VkAllocationCallbacks *allocator,<br>
+ struct wsi_fence **fence);<br>
+<br>
+VkResult<br>
+wsi_register_display_event(<wbr>VkDevice device,<br>
+ struct wsi_device *wsi_device,<br>
+ VkDisplayKHR display,<br>
+ const VkDisplayEventInfoEXT *display_event_info,<br>
+ const VkAllocationCallbacks *allocator,<br>
+ struct wsi_fence **fence);<br>
+<br>
+VkResult<br>
+wsi_get_swapchain_counter(<wbr>VkDevice device,<br>
+ struct wsi_device *wsi_device,<br>
+ VkSwapchainKHR swapchain,<br>
+ VkSurfaceCounterFlagBitsEXT flag_bits,<br>
+ uint64_t *value);<br>
+<br>
#endif<br>
<span class="HOEnZb"><font color="#888888">-- <br>
2.17.1<br>
<br>
______________________________<wbr>_________________<br>
dri-devel mailing list<br>
<a href="mailto:dri-devel@lists.freedesktop.org">dri-devel@lists.freedesktop.<wbr>org</a><br>
<a href="https://lists.freedesktop.org/mailman/listinfo/dri-devel" rel="noreferrer" target="_blank">https://lists.freedesktop.org/<wbr>mailman/listinfo/dri-devel</a><br>
</font></span></blockquote></div><br></div></div>