<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Tue, May 2, 2017 at 5:15 PM, Chad Versace <span dir="ltr"><<a href="mailto:chadversary@chromium.org" target="_blank">chadversary@chromium.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Fri 14 Apr 2017, Jason Ekstrand wrote:<br>
> This implementation allocates a 4k BO for each semaphore that can be<br>
> exported using OPAQUE_FD and uses the kernel's already-existing<br>
> synchronization mechanism on BOs.<br>
> ---<br>
> src/intel/vulkan/anv_batch_<wbr>chain.c | 53 ++++++++++--<br>
> src/intel/vulkan/anv_device.c | 4 +<br>
> src/intel/vulkan/anv_<wbr>entrypoints_gen.py | 1 +<br>
> src/intel/vulkan/anv_private.h | 16 +++-<br>
> src/intel/vulkan/anv_queue.c | 141 ++++++++++++++++++++++++++++++<wbr>--<br>
> 5 files changed, 199 insertions(+), 16 deletions(-)<br>
<br>
</span>[snip]<br>
<div><div class="h5"><br>
> @@ -513,14 +533,33 @@ VkResult anv_CreateSemaphore(<br>
> VkExternalSemaphoreHandleTypeF<wbr>lagsKHX handleTypes =<br>
> export ? export->handleTypes : 0;<br>
><br>
> - /* External semaphores are not yet supported */<br>
> - assert(handleTypes == 0);<br>
> + if (handleTypes == 0) {<br>
> + /* The DRM execbuffer ioctl always execute in-oder so long as you stay<br>
> + * on the same ring. Since we don't expose the blit engine as a DMA<br>
> + * queue, a dummy no-op semaphore is a perfectly valid implementation.<br>
> + */<br>
> + semaphore->permanent.type = ANV_SEMAPHORE_TYPE_DUMMY;<br>
> + } else if (handleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_<wbr>TYPE_OPAQUE_FD_BIT_KHX) {<br>
> + assert(handleTypes == VK_EXTERNAL_SEMAPHORE_HANDLE_<wbr>TYPE_OPAQUE_FD_BIT_KHX);<br>
> +<br>
> + semaphore->permanent.type = ANV_SEMAPHORE_TYPE_BO;<br>
> + VkResult result = anv_bo_cache_alloc(device, &device->bo_cache,<br>
> + 4096, &semaphore-><a href="http://permanent.bo" rel="noreferrer" target="_blank">permanent.bo</a>);<br>
> + if (result != VK_SUCCESS) {<br>
> + vk_free2(&device->alloc, pAllocator, semaphore);<br>
> + return result;<br>
> + }<br>
> +<br>
> + /* If we're going to use this as a fence, we need to *not* have the<br>
> + * EXEC_OBJECT_ASYNC bit set.<br>
> + */<br>
> + semaphore->permanent.bo->flags &= ~EXEC_OBJECT_ASYNC;<br>
> + } else {<br>
> + assert(!"Unknown handle type");<br>
> + vk_free2(&device->alloc, pAllocator, semaphore);<br>
> + return vk_error(VK_ERROR_INVALID_<wbr>EXTERNAL_HANDLE_KHX);<br>
<br>
</div></div>The Vulkan 1.0.49 spec does not list<br>
VK_ERROR_INVALID_EXTERNAL_<wbr>HANDLE_KHX as a possible error code for<br>
vkCreateSemaphore. The reason is that<br>
VK_ERROR_INVALID_EXTERNAL_<wbr>HANDLE_KHX indicates that an external handle<br>
does not match the expected handle type. Since vkCreateSemaphore has no<br>
parameter that's an external handle, it can't return the error.<br>
VK_ERROR_INVALID_EXTERNAL_<wbr>HANDLE_KHX only makes sense for functions that<br>
import a handle or query the properties of a handle.<br>
<br>
This 'else' branch is capturing invalid application usage the API. Since<br>
invalid usages leads to undefined behavior, we could claim that Mesa's<br>
undefined behavior is to return a helpful error code. But...<br>
<br>
I much prefer disastrous undefined behavior here instead of helpful<br>
undefined behavior. If we return VK_ERROR_INVALID_EXTERNAL_<wbr>HANDLE_KHX,<br>
then appsy may accidentally rely on our out-of-spec behavior. It would<br>
be better to just abort() or, like anvil does in most cases, silently<br>
ignore the invalid case and continue blindly ahead.<br></blockquote><div><br></div><div>They do get disastrous behavior in debug builds because of the assert. In release builds, however, I'm not sure what I think. I don't know that I like the idea of silently continuing because it would most likely give them a perfectly "valid" dummy semaphore and they would get an error much later when they try to export from it. We could abort(). I don't know. I really don't think apps will rely on that error.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
[snip]<br>
<span class=""><br>
> +static void<br>
> +anv_semaphore_impl_cleanup(<wbr>struct anv_device *device,<br>
> + struct anv_semaphore_impl *impl)<br>
> +{<br>
> + switch (impl->type) {<br>
> + case ANV_SEMAPHORE_TYPE_NONE:<br>
> + case ANV_SEMAPHORE_TYPE_DUMMY:<br>
> + /* Dummy. Nothing to do */<br>
> + break;<br>
> +<br>
> + case ANV_SEMAPHORE_TYPE_BO:<br>
> + anv_bo_cache_release(device, &device->bo_cache, impl->bo);<br>
> + break;<br>
> +<br>
> + default:<br>
> + unreachable("Invalid semaphore type");<br>
> + }<br>
<br>
</span>This switch statement is a good candidate for exhaustive switch warnings<br>
(-Wswitch). But we don't get the warnings unless the default is dropped<br>
and the unreachable is moved to after the switch statement.<br></blockquote><div><br></div><div>Sure, I can do that.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
> +}<br>
<br>
[snip]<br>
<span class=""><br>
><br>
> @@ -548,9 +609,73 @@ void anv_<wbr>GetPhysicalDeviceExternalSemap<wbr>horePropertiesKHX(<br>
> VkExternalSemaphorePropertiesK<wbr>HX* pExternalSemaphoreProperties)<br>
> {<br>
> switch (pExternalSemaphoreInfo-><wbr>handleType) {<br>
> + case VK_EXTERNAL_SEMAPHORE_HANDLE_<wbr>TYPE_OPAQUE_FD_BIT_KHX:<br>
> + pExternalSemaphoreProperties-><wbr>exportFromImportedHandleTypes = 0;<br>
<br>
</span>I expected exportFromImportedHandleTypes to be<br>
VK_EXTERNAL_SEMAPHORE_HANDLE_<wbr>TYPE_OPAQUE_FD_BIT_KHX. Why is it 0?<br>
I admit I don't fully understand the VK_KHX_external_semaphore spec. (To<br>
be fair, I doubt anyone fully understands it).<br><div><div class="h5"></div></div></blockquote><div><br></div><div>I didn't figure there was any reason to allow re-export but there's no harm in it for OPAQUE_FD.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="h5">
> + pExternalSemaphoreProperties-><wbr>compatibleHandleTypes =<br>
> + VK_EXTERNAL_SEMAPHORE_HANDLE_<wbr>TYPE_OPAQUE_FD_BIT_KHX;<br>
> + pExternalSemaphoreProperties-><wbr>externalSemaphoreFeatures =<br>
> + VK_EXTERNAL_SEMAPHORE_FEATURE_<wbr>EXPORTABLE_BIT_KHX |<br>
> + VK_EXTERNAL_SEMAPHORE_FEATURE_<wbr>IMPORTABLE_BIT_KHX;<br>
> + break;<br>
> +<br>
> default:<br>
> pExternalSemaphoreProperties-><wbr>exportFromImportedHandleTypes = 0;<br>
> pExternalSemaphoreProperties-><wbr>compatibleHandleTypes = 0;<br>
> pExternalSemaphoreProperties-><wbr>externalSemaphoreFeatures = 0;<br>
> }<br>
> }<br>
> +<br>
> +VkResult anv_ImportSemaphoreFdKHX(<br>
> + VkDevice _device,<br>
> + const VkImportSemaphoreFdInfoKHX* pImportSemaphoreFdInfo)<br>
> +{<br>
> + ANV_FROM_HANDLE(anv_device, device, _device);<br>
> + ANV_FROM_HANDLE(anv_semaphore, semaphore, pImportSemaphoreFdInfo-><wbr>semaphore);<br>
> +<br>
> + switch (pImportSemaphoreFdInfo-><wbr>handleType) {<br>
> + case VK_EXTERNAL_SEMAPHORE_HANDLE_<wbr>TYPE_OPAQUE_FD_BIT_KHX: {<br>
> + struct anv_bo *bo;<br>
> + VkResult result = anv_bo_cache_import(device, &device->bo_cache,<br>
> + pImportSemaphoreFdInfo->fd, 4096,<br>
> + &bo);<br>
> + if (result != VK_SUCCESS)<br>
> + return result;<br>
> +<br>
> + /* If we're going to use this as a fence, we need to *not* have the<br>
> + * EXEC_OBJECT_ASYNC bit set.<br>
> + */<br>
> + bo->flags &= ~EXEC_OBJECT_ASYNC;<br>
> +<br>
> + anv_semaphore_impl_cleanup(<wbr>device, &semaphore->permanent);<br>
> +<br>
> + semaphore->permanent.type = ANV_SEMAPHORE_TYPE_BO;<br>
> + semaphore-><a href="http://permanent.bo" rel="noreferrer" target="_blank">permanent.bo</a> = bo;<br>
> +<br>
> + return VK_SUCCESS;<br>
> + }<br>
> +<br>
> + default:<br>
> + return vk_error(VK_ERROR_INVALID_<wbr>EXTERNAL_HANDLE_KHX);<br>
<br>
</div></div>Yep. VK_ERROR_INVALID_EXTERNAL_<wbr>HANDLE_KHX is required here.<br>
<span class=""><br>
> + }<br>
> +}<br>
<br>
> +<br>
> +VkResult anv_GetSemaphoreFdKHX(<br>
> + VkDevice _device,<br>
> + VkSemaphore _semaphore,<br>
> + VkExternalSemaphoreHandleTypeF<wbr>lagBitsKHX handleType,<br>
> + int* pFd)<br>
> +{<br>
> + ANV_FROM_HANDLE(anv_device, device, _device);<br>
> + ANV_FROM_HANDLE(anv_semaphore, semaphore, _semaphore);<br>
> +<br>
> + switch (semaphore->permanent.type) {<br>
> + case ANV_SEMAPHORE_TYPE_BO:<br>
> + return anv_bo_cache_export(device, &device->bo_cache,<br>
> + semaphore-><a href="http://permanent.bo" rel="noreferrer" target="_blank">permanent.bo</a>, pFd);<br>
> +<br>
> + default:<br>
> + return vk_error(VK_ERROR_INVALID_<wbr>EXTERNAL_HANDLE_KHX);<br>
<br>
</span>Like vkCreateSemaphore, for the same reasons,<br>
VK_ERROR_INVALID_EXTERNAL_<wbr>HANDLE_KHX is not a valid error code for<br>
vkGetSemaphoreFdKHX. And like vkCreateSemaphore, I think disastrous<br>
undefined behavior here is better than helpful undefined behavior.<br></blockquote><div><br></div><div>Hrm... Maybe? We could abort(). What I don't want to do is just get rid of the case and silently return a random return value (which may be VK_SUCCES!)<br><br></div><div>As a side-note, I'm beginning to come to the conclusion that we may want to make the undefined behavior in the driver a bit more contained. While our driver shouldn't be robust against API misuse, I would like it to be deterministic and try to crash early rather than just corrupting something and maybe crashing later in a weird way.<br></div><div><br></div><div>--Jason <br></div></div></div></div>