[Mesa-dev] [PATCH 3/3] radv: Implement VK_ANDROID_native_buffer.
Dave Airlie
airlied at gmail.com
Mon Jan 8 04:30:42 UTC 2018
On 5 January 2018 at 03:38, Bas Nieuwenhuizen <basni at chromium.org> wrote:
> Passes
> dEQP-VK.api.smoke.*
> dEQP-VK.wsi.android.*
>
> with android-cts-7.1_r12 .
>
> Unlike the initial anv implementation this does
> use syncobjs instead of waiting on the CPU.
>
> This is missing meson build coverage for now.
>
> One possible todo is that linux 4.15 now has a
> sycall that allows us to export amdgpu fence to
> a sync_file, which allows us not to force all
> fences and semaphores to use syncobjs. However,
> I had trouble with my kernel crashing regularly
> with NULL pointers, and I'm not sure how beneficial
> it is in the first place given that intel uses
> syncobjs for all fences if available.
Happy to have these merged as-is, but I do wonder if we could refactor out most
of radv_android.c into a wsi_android or some such to share with anv.
The current WSI already has some magic flags to pass things like the
no space for cmask flag.
For all 3:
Reviewed-by: Dave Airlie <airlied at redhat.com>
Dave.
> ---
> src/amd/vulkan/Makefile.am | 7 +
> src/amd/vulkan/Makefile.sources | 3 +
> src/amd/vulkan/meson.build | 4 +-
> src/amd/vulkan/radv_android.c | 366 ++++++++++++++++++++++++++++++++++++++++
> src/amd/vulkan/radv_device.c | 7 +-
> src/amd/vulkan/radv_image.c | 12 ++
> src/amd/vulkan/radv_private.h | 12 ++
> 7 files changed, 407 insertions(+), 4 deletions(-)
> create mode 100644 src/amd/vulkan/radv_android.c
>
> diff --git a/src/amd/vulkan/Makefile.am b/src/amd/vulkan/Makefile.am
> index e1a04e8c7f..a4e23cd28e 100644
> --- a/src/amd/vulkan/Makefile.am
> +++ b/src/amd/vulkan/Makefile.am
> @@ -99,6 +99,13 @@ VULKAN_LIB_DEPS += \
> $(WAYLAND_CLIENT_LIBS)
> endif
>
> +if HAVE_PLATFORM_ANDROID
> +AM_CPPFLAGS += $(ANDROID_CPPFLAGS)
> +AM_CFLAGS += $(ANDROID_CFLAGS)
> +VULKAN_LIB_DEPS += $(ANDROID_LIBS)
> +VULKAN_SOURCES += $(VULKAN_ANDROID_FILES)
> +endif
> +
> noinst_LTLIBRARIES = libvulkan_common.la
> libvulkan_common_la_SOURCES = $(VULKAN_SOURCES)
>
> diff --git a/src/amd/vulkan/Makefile.sources b/src/amd/vulkan/Makefile.sources
> index c9d172c3b1..a510d88d96 100644
> --- a/src/amd/vulkan/Makefile.sources
> +++ b/src/amd/vulkan/Makefile.sources
> @@ -69,6 +69,9 @@ VULKAN_FILES := \
> vk_format.h \
> $(RADV_WS_AMDGPU_FILES)
>
> +VULKAN_ANDROID_FILES := \
> + radv_android.c
> +
> VULKAN_WSI_WAYLAND_FILES := \
> radv_wsi_wayland.c
>
> diff --git a/src/amd/vulkan/meson.build b/src/amd/vulkan/meson.build
> index 93997350a2..fa23d28fbb 100644
> --- a/src/amd/vulkan/meson.build
> +++ b/src/amd/vulkan/meson.build
> @@ -29,10 +29,10 @@ radv_entrypoints = custom_target(
>
> radv_extensions_c = custom_target(
> 'radv_extensions.c',
> - input : ['radv_extensions.py', vk_api_xml],
> + input : ['radv_extensions.py', vk_api_xml, vk_android_native_buffer_xml],
> output : ['radv_extensions.c'],
> command : [prog_python2, '@INPUT0@', '--xml', '@INPUT1@',
> - '--out', '@OUTPUT@'],
> + '--xml', '@INPUT2@', '--out', '@OUTPUT@'],
> )
>
> vk_format_table_c = custom_target(
> diff --git a/src/amd/vulkan/radv_android.c b/src/amd/vulkan/radv_android.c
> new file mode 100644
> index 0000000000..09da601dac
> --- /dev/null
> +++ b/src/amd/vulkan/radv_android.c
> @@ -0,0 +1,366 @@
> +/*
> + * Copyright © 2017, Google Inc.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +#include <hardware/gralloc.h>
> +#include <hardware/hardware.h>
> +#include <hardware/hwvulkan.h>
> +#include <vulkan/vk_android_native_buffer.h>
> +#include <vulkan/vk_icd.h>
> +#include <libsync.h>
> +
> +#include "radv_private.h"
> +
> +static int radv_hal_open(const struct hw_module_t* mod, const char* id, struct hw_device_t** dev);
> +static int radv_hal_close(struct hw_device_t *dev);
> +
> +static void UNUSED
> +static_asserts(void)
> +{
> + STATIC_ASSERT(HWVULKAN_DISPATCH_MAGIC == ICD_LOADER_MAGIC);
> +}
> +
> +PUBLIC struct hwvulkan_module_t HAL_MODULE_INFO_SYM = {
> + .common = {
> + .tag = HARDWARE_MODULE_TAG,
> + .module_api_version = HWVULKAN_MODULE_API_VERSION_0_1,
> + .hal_api_version = HARDWARE_MAKE_API_VERSION(1, 0),
> + .id = HWVULKAN_HARDWARE_MODULE_ID,
> + .name = "AMD Vulkan HAL",
> + .author = "Google",
> + .methods = &(hw_module_methods_t) {
> + .open = radv_hal_open,
> + },
> + },
> +};
> +
> +/* If any bits in test_mask are set, then unset them and return true. */
> +static inline bool
> +unmask32(uint32_t *inout_mask, uint32_t test_mask)
> +{
> + uint32_t orig_mask = *inout_mask;
> + *inout_mask &= ~test_mask;
> + return *inout_mask != orig_mask;
> +}
> +
> +static int
> +radv_hal_open(const struct hw_module_t* mod, const char* id,
> + struct hw_device_t** dev)
> +{
> + assert(mod == &HAL_MODULE_INFO_SYM.common);
> + assert(strcmp(id, HWVULKAN_DEVICE_0) == 0);
> +
> + hwvulkan_device_t *hal_dev = malloc(sizeof(*hal_dev));
> + if (!hal_dev)
> + return -1;
> +
> + *hal_dev = (hwvulkan_device_t) {
> + .common = {
> + .tag = HARDWARE_DEVICE_TAG,
> + .version = HWVULKAN_DEVICE_API_VERSION_0_1,
> + .module = &HAL_MODULE_INFO_SYM.common,
> + .close = radv_hal_close,
> + },
> + .EnumerateInstanceExtensionProperties = radv_EnumerateInstanceExtensionProperties,
> + .CreateInstance = radv_CreateInstance,
> + .GetInstanceProcAddr = radv_GetInstanceProcAddr,
> + };
> +
> + *dev = &hal_dev->common;
> + return 0;
> +}
> +
> +static int
> +radv_hal_close(struct hw_device_t *dev)
> +{
> + /* hwvulkan.h claims that hw_device_t::close() is never called. */
> + return -1;
> +}
> +
> +VkResult
> +radv_image_from_gralloc(VkDevice device_h,
> + const VkImageCreateInfo *base_info,
> + const VkNativeBufferANDROID *gralloc_info,
> + const VkAllocationCallbacks *alloc,
> + VkImage *out_image_h)
> +
> +{
> + RADV_FROM_HANDLE(radv_device, device, device_h);
> + VkImage image_h = VK_NULL_HANDLE;
> + struct radv_image *image = NULL;
> + struct radv_bo *bo = NULL;
> + VkResult result;
> +
> + result = radv_image_create(device_h,
> + &(struct radv_image_create_info) {
> + .vk_info = base_info,
> + .scanout = true,
> + .no_metadata_planes = true},
> + alloc,
> + &image_h);
> +
> + if (result != VK_SUCCESS)
> + return result;
> +
> + if (gralloc_info->handle->numFds != 1) {
> + return vk_errorf(VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR,
> + "VkNativeBufferANDROID::handle::numFds is %d, "
> + "expected 1", gralloc_info->handle->numFds);
> + }
> +
> + /* Do not close the gralloc handle's dma_buf. The lifetime of the dma_buf
> + * must exceed that of the gralloc handle, and we do not own the gralloc
> + * handle.
> + */
> + int dma_buf = gralloc_info->handle->data[0];
> +
> + image = radv_image_from_handle(image_h);
> +
> + VkDeviceMemory memory_h;
> +
> + const VkMemoryDedicatedAllocateInfoKHR ded_alloc = {
> + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR,
> + .pNext = NULL,
> + .buffer = VK_NULL_HANDLE,
> + .image = image_h
> + };
> +
> + const VkImportMemoryFdInfoKHR import_info = {
> + .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
> + .pNext = &ded_alloc,
> + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR,
> + .fd = dup(dma_buf),
> + };
> + /* Find the first VRAM memory type, or GART for PRIME images. */
> + int memory_type_index = -1;
> + for (int i = 0; i < device->physical_device->memory_properties.memoryTypeCount; ++i) {
> + bool is_local = !!(device->physical_device->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
> + if (is_local) {
> + memory_type_index = i;
> + break;
> + }
> + }
> +
> + /* fallback */
> + if (memory_type_index == -1)
> + memory_type_index = 0;
> +
> + result = radv_AllocateMemory(device_h,
> + &(VkMemoryAllocateInfo) {
> + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
> + .pNext = &import_info,
> + .allocationSize = image->size,
> + .memoryTypeIndex = memory_type_index,
> + },
> + alloc,
> + &memory_h);
> + if (result != VK_SUCCESS)
> + goto fail_create_image;
> +
> + radv_BindImageMemory(device_h, image_h, memory_h, 0);
> +
> + image->owned_memory = memory_h;
> + /* Don't clobber the out-parameter until success is certain. */
> + *out_image_h = image_h;
> +
> + return VK_SUCCESS;
> +
> +fail_create_image:
> +fail_size:
> + radv_DestroyImage(device_h, image_h, alloc);
> +
> + return result;
> +}
> +
> +VkResult radv_GetSwapchainGrallocUsageANDROID(
> + VkDevice device_h,
> + VkFormat format,
> + VkImageUsageFlags imageUsage,
> + int* grallocUsage)
> +{
> + RADV_FROM_HANDLE(radv_device, device, device_h);
> + struct radv_physical_device *phys_dev = device->physical_device;
> + VkPhysicalDevice phys_dev_h = radv_physical_device_to_handle(phys_dev);
> + VkResult result;
> +
> + *grallocUsage = 0;
> +
> + /* WARNING: Android Nougat's libvulkan.so hardcodes the VkImageUsageFlags
> + * returned to applications via VkSurfaceCapabilitiesKHR::supportedUsageFlags.
> + * The relevant code in libvulkan/swapchain.cpp contains this fun comment:
> + *
> + * TODO(jessehall): I think these are right, but haven't thought hard
> + * about it. Do we need to query the driver for support of any of
> + * these?
> + *
> + * Any disagreement between this function and the hardcoded
> + * VkSurfaceCapabilitiesKHR:supportedUsageFlags causes tests
> + * dEQP-VK.wsi.android.swapchain.*.image_usage to fail.
> + */
> +
> + const VkPhysicalDeviceImageFormatInfo2KHR image_format_info = {
> + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR,
> + .format = format,
> + .type = VK_IMAGE_TYPE_2D,
> + .tiling = VK_IMAGE_TILING_OPTIMAL,
> + .usage = imageUsage,
> + };
> +
> + VkImageFormatProperties2KHR image_format_props = {
> + .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR,
> + };
> +
> + /* Check that requested format and usage are supported. */
> + result = radv_GetPhysicalDeviceImageFormatProperties2KHR(phys_dev_h,
> + &image_format_info, &image_format_props);
> + if (result != VK_SUCCESS) {
> + return vk_errorf(result,
> + "radv_GetPhysicalDeviceImageFormatProperties2KHR failed "
> + "inside %s", __func__);
> + }
> +
> + if (unmask32(&imageUsage, VK_IMAGE_USAGE_TRANSFER_DST_BIT |
> + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT))
> + *grallocUsage |= GRALLOC_USAGE_HW_RENDER;
> +
> + if (unmask32(&imageUsage, VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
> + VK_IMAGE_USAGE_SAMPLED_BIT |
> + VK_IMAGE_USAGE_STORAGE_BIT |
> + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))
> + *grallocUsage |= GRALLOC_USAGE_HW_TEXTURE;
> +
> + /* All VkImageUsageFlags not explicitly checked here are unsupported for
> + * gralloc swapchains.
> + */
> + if (imageUsage != 0) {
> + return vk_errorf(VK_ERROR_FORMAT_NOT_SUPPORTED,
> + "unsupported VkImageUsageFlags(0x%x) for gralloc "
> + "swapchain", imageUsage);
> + }
> +
> + /*
> + * FINISHME: Advertise all display-supported formats. Mostly
> + * DRM_FORMAT_ARGB2101010 and DRM_FORMAT_ABGR2101010, but need to check
> + * what we need for 30-bit colors.
> + */
> + if (format == VK_FORMAT_B8G8R8A8_UNORM ||
> + format == VK_FORMAT_B5G6R5_UNORM_PACK16) {
> + *grallocUsage |= GRALLOC_USAGE_HW_FB |
> + GRALLOC_USAGE_HW_COMPOSER |
> + GRALLOC_USAGE_EXTERNAL_DISP;
> + }
> +
> + if (*grallocUsage == 0)
> + return VK_ERROR_FORMAT_NOT_SUPPORTED;
> +
> + return VK_SUCCESS;
> +}
> +
> +VkResult
> +radv_AcquireImageANDROID(
> + VkDevice device,
> + VkImage image_h,
> + int nativeFenceFd,
> + VkSemaphore semaphore,
> + VkFence fence)
> +{
> + VkResult semaphore_result = VK_SUCCESS, fence_result = VK_SUCCESS;
> +
> + if (semaphore != VK_NULL_HANDLE) {
> + int semaphore_fd = nativeFenceFd >= 0 ? dup(nativeFenceFd) : nativeFenceFd;
> + semaphore_result = radv_ImportSemaphoreFdKHR(device,
> + &(VkImportSemaphoreFdInfoKHR) {
> + .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR,
> + .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR,
> + .fd = semaphore_fd,
> + .semaphore = semaphore,
> + });
> + }
> +
> + if (fence != VK_NULL_HANDLE) {
> + int fence_fd = nativeFenceFd >= 0 ? dup(nativeFenceFd) : nativeFenceFd;
> + fence_result = radv_ImportFenceFdKHR(device,
> + &(VkImportFenceFdInfoKHR) {
> + .sType = VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR,
> + .flags = VK_FENCE_IMPORT_TEMPORARY_BIT_KHR,
> + .fd = fence_fd,
> + .fence = fence,
> + });
> + }
> +
> + close(nativeFenceFd);
> +
> + if (semaphore_result != VK_SUCCESS)
> + return semaphore_result;
> + return fence_result;
> +}
> +
> +VkResult
> +radv_QueueSignalReleaseImageANDROID(
> + VkQueue _queue,
> + uint32_t waitSemaphoreCount,
> + const VkSemaphore* pWaitSemaphores,
> + VkImage image,
> + int* pNativeFenceFd)
> +{
> + RADV_FROM_HANDLE(radv_queue, queue, _queue);
> + VkResult result = VK_SUCCESS;
> +
> + if (waitSemaphoreCount == 0) {
> + if (pNativeFenceFd)
> + *pNativeFenceFd = -1;
> + return VK_SUCCESS;
> + }
> +
> + int fd = -1;
> +
> + for (uint32_t i = 0; i < waitSemaphoreCount; ++i) {
> + int tmp_fd;
> + result = radv_GetSemaphoreFdKHR(radv_device_to_handle(queue->device),
> + &(VkSemaphoreGetFdInfoKHR) {
> + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
> + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR,
> + .semaphore = pWaitSemaphores[i],
> + }, &tmp_fd);
> + if (result != VK_SUCCESS) {
> + if (fd >= 0)
> + close (fd);
> + return result;
> + }
> +
> + if (fd < 0)
> + fd = tmp_fd;
> + else if (tmp_fd >= 0) {
> + sync_accumulate("radv", &fd, tmp_fd);
> + close(tmp_fd);
> + }
> + }
> +
> + if (pNativeFenceFd) {
> + *pNativeFenceFd = fd;
> + } else if (fd >= 0) {
> + close(fd);
> + /* We still need to do the exports, to reset the semaphores, but
> + * otherwise we don't wait on them. */
> + }
> + return VK_SUCCESS;
> +}
> diff --git a/src/amd/vulkan/radv_device.c b/src/amd/vulkan/radv_device.c
> index cbf8f5cf49..4751b7ab6c 100644
> --- a/src/amd/vulkan/radv_device.c
> +++ b/src/amd/vulkan/radv_device.c
> @@ -1087,6 +1087,9 @@ VkResult radv_CreateDevice(
> /* Disabled and not implemented for now. */
> device->dfsm_allowed = device->pbb_allowed && false;
>
> +#ifdef ANDROID
> + device->always_use_syncobj = device->physical_device->rad_info.has_syncobj_wait_for_submit;
> +#endif
>
> #if HAVE_LLVM < 0x0400
> device->llvm_supports_spill = false;
> @@ -2656,7 +2659,7 @@ VkResult radv_CreateFence(
> fence->submitted = false;
> fence->signalled = !!(pCreateInfo->flags & VK_FENCE_CREATE_SIGNALED_BIT);
> fence->temp_syncobj = 0;
> - if (handleTypes) {
> + if (device->always_use_syncobj || handleTypes) {
> int ret = device->ws->create_syncobj(device->ws, &fence->syncobj);
> if (ret) {
> vk_free2(&device->alloc, pAllocator, fence);
> @@ -2832,7 +2835,7 @@ VkResult radv_CreateSemaphore(
>
> sem->temp_syncobj = 0;
> /* create a syncobject if we are going to export this semaphore */
> - if (handleTypes) {
> + if (device->always_use_syncobj || handleTypes) {
> assert (device->physical_device->rad_info.has_syncobj);
> int ret = device->ws->create_syncobj(device->ws, &sem->syncobj);
> if (ret) {
> diff --git a/src/amd/vulkan/radv_image.c b/src/amd/vulkan/radv_image.c
> index d69ae8af48..54b2b5247d 100644
> --- a/src/amd/vulkan/radv_image.c
> +++ b/src/amd/vulkan/radv_image.c
> @@ -1147,6 +1147,15 @@ radv_CreateImage(VkDevice device,
> const VkAllocationCallbacks *pAllocator,
> VkImage *pImage)
> {
> +#ifdef ANDROID
> + const VkNativeBufferANDROID *gralloc_info =
> + vk_find_struct_const(pCreateInfo->pNext, NATIVE_BUFFER_ANDROID);
> +
> + if (gralloc_info)
> + return radv_image_from_gralloc(device, pCreateInfo, gralloc_info,
> + pAllocator, pImage);
> +#endif
> +
> const struct wsi_image_create_info *wsi_info =
> vk_find_struct_const(pCreateInfo->pNext, WSI_IMAGE_CREATE_INFO_MESA);
> bool scanout = wsi_info && wsi_info->scanout;
> @@ -1173,6 +1182,9 @@ radv_DestroyImage(VkDevice _device, VkImage _image,
> if (image->flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT)
> device->ws->buffer_destroy(image->bo);
>
> + if (image->owned_memory != VK_NULL_HANDLE)
> + radv_FreeMemory(_device, image->owned_memory, pAllocator);
> +
> vk_free2(&device->alloc, pAllocator, image);
> }
>
> diff --git a/src/amd/vulkan/radv_private.h b/src/amd/vulkan/radv_private.h
> index c15b044d00..e8cd7fe9b7 100644
> --- a/src/amd/vulkan/radv_private.h
> +++ b/src/amd/vulkan/radv_private.h
> @@ -69,6 +69,7 @@ typedef uint32_t xcb_window_t;
> #include <vulkan/vulkan.h>
> #include <vulkan/vulkan_intel.h>
> #include <vulkan/vk_icd.h>
> +#include <vulkan/vk_android_native_buffer.h>
>
> #include "radv_entrypoints.h"
>
> @@ -581,6 +582,7 @@ struct radv_device {
> int queue_count[RADV_MAX_QUEUE_FAMILIES];
> struct radeon_winsys_cs *empty_cs[RADV_MAX_QUEUE_FAMILIES];
>
> + bool always_use_syncobj;
> bool llvm_supports_spill;
> bool has_distributed_tess;
> bool pbb_allowed;
> @@ -1340,6 +1342,9 @@ struct radv_image {
> struct radv_cmask_info cmask;
> uint64_t clear_value_offset;
> uint64_t dcc_pred_offset;
> +
> + /* For VK_ANDROID_native_buffer, the WSI image owns the memory, */
> + VkDeviceMemory owned_memory;
> };
>
> /* Whether the image has a htile that is known consistent with the contents of
> @@ -1434,6 +1439,13 @@ VkResult radv_image_create(VkDevice _device,
> const VkAllocationCallbacks* alloc,
> VkImage *pImage);
>
> +VkResult
> +radv_image_from_gralloc(VkDevice device_h,
> + const VkImageCreateInfo *base_info,
> + const VkNativeBufferANDROID *gralloc_info,
> + const VkAllocationCallbacks *alloc,
> + VkImage *out_image_h);
> +
> void radv_image_view_init(struct radv_image_view *view,
> struct radv_device *device,
> const VkImageViewCreateInfo* pCreateInfo);
> --
> 2.16.0.rc0.223.g4a4ac83678-goog
>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/mesa-dev
More information about the mesa-dev
mailing list