mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-05 07:28:11 +02:00
vulkan/drm_syncobj: Add a vk_drm_syncobj_copy_payloads helper
This intentionally matches vk_device::copy_sync_payloads. Reviewed-by: Lars-Ivar Hesselberg Simonsen <lars-ivar.simonsen@arm.com> Reviewed-by: Yiwei Zhang <zzyiwei@chromium.org> Reviewed-by: Adam Jackson <ajax@redhat.com> Reviewed-by: Lionel Landwerlin <lionel.g.landwerlin@intel.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36827>
This commit is contained in:
parent
f4d00c1dbf
commit
4827ba625d
3 changed files with 205 additions and 5 deletions
|
|
@ -226,11 +226,7 @@ struct vk_device {
|
|||
* queue. Instead, the driver is expected to simply copy the sync payloads
|
||||
* from the wait set, merge them together into one, and apply that to the
|
||||
* signals. After this function returns, all of the signals are now
|
||||
* equivalent to all of the waits.
|
||||
*
|
||||
* This function MUST reset all binary syncs it waits on, unless that same
|
||||
* sync is also used as a signal. Otherwise, we risk breaking timeline
|
||||
* semaphore negotiation invariants.
|
||||
* equivalent to the union all of the waits.
|
||||
*/
|
||||
VkResult (*copy_sync_payloads)(struct vk_device *device,
|
||||
uint32_t wait_count,
|
||||
|
|
|
|||
|
|
@ -28,11 +28,13 @@
|
|||
|
||||
#include "drm-uapi/drm.h"
|
||||
|
||||
#include "util/libsync.h"
|
||||
#include "util/os_time.h"
|
||||
#include "util/u_sync_provider.h"
|
||||
|
||||
#include "vk_device.h"
|
||||
#include "vk_log.h"
|
||||
#include "vk_physical_device.h"
|
||||
#include "vk_util.h"
|
||||
|
||||
static struct vk_drm_syncobj *
|
||||
|
|
@ -462,6 +464,194 @@ vk_drm_syncobj_move(struct vk_device *device,
|
|||
}
|
||||
}
|
||||
|
||||
static VkResult
|
||||
vk_drm_copy_sync_file_payloads(struct vk_device *device,
|
||||
uint32_t wait_count,
|
||||
const struct vk_sync_wait *waits,
|
||||
uint32_t signal_count,
|
||||
const struct vk_sync_signal *signals)
|
||||
{
|
||||
VkResult result = VK_SUCCESS;
|
||||
int merged = -1;
|
||||
|
||||
for (uint32_t i = 0; i < wait_count; i++) {
|
||||
assert(!(waits[i].sync->flags & VK_SYNC_IS_TIMELINE));
|
||||
assert(waits[i].wait_value == 0);
|
||||
|
||||
int wait_fd = -1;
|
||||
result = vk_drm_syncobj_export_sync_file(device, waits[i].sync, &wait_fd);
|
||||
if (result != VK_SUCCESS)
|
||||
goto fail;
|
||||
|
||||
/* -1 means it's already signaled, so nothing to merge. */
|
||||
if (wait_fd == -1)
|
||||
continue;
|
||||
|
||||
if (merged == -1) {
|
||||
merged = wait_fd;
|
||||
} else {
|
||||
int ret = sync_merge("vk_drm_syncobj", merged, wait_fd);
|
||||
close(wait_fd);
|
||||
if (ret < 0) {
|
||||
result = vk_errorf(device, VK_ERROR_UNKNOWN,
|
||||
"SYNC_IOC_MERGE failed: %m");
|
||||
goto fail;
|
||||
}
|
||||
close(merged);
|
||||
merged = ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* merged == -1 could either mean that we had no waits or it could mean
|
||||
* that they were all already complete. In either case there's nothing to
|
||||
* wait on so we can just signal everything.
|
||||
*/
|
||||
if (merged == -1)
|
||||
return vk_drm_syncobj_signal_many(device, signal_count, signals);
|
||||
|
||||
for (uint32_t i = 0; i < signal_count; i++) {
|
||||
assert(!(signals[i].sync->flags & VK_SYNC_IS_TIMELINE));
|
||||
assert(signals[i].signal_value == 0);
|
||||
|
||||
result = vk_drm_syncobj_import_sync_file(device, signals[i].sync, merged);
|
||||
if (result != VK_SUCCESS)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (merged >= 0)
|
||||
close(merged);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static VkResult
|
||||
vk_drm_syncobj_transfer_payloads(struct vk_device *device,
|
||||
uint32_t wait_count,
|
||||
const struct vk_sync_wait *waits,
|
||||
uint32_t signal_count,
|
||||
const struct vk_sync_signal *signals)
|
||||
{
|
||||
if (wait_count == 1) {
|
||||
/* If we only have one wait, we can transfer directly into each of the
|
||||
* signal syncs.
|
||||
*/
|
||||
struct vk_drm_syncobj *wait_sobj = to_drm_syncobj(waits[0].sync);
|
||||
const uint64_t wait_value = waits[0].wait_value;
|
||||
|
||||
for (uint32_t i = 0; i < signal_count; i++) {
|
||||
struct vk_drm_syncobj *signal_sobj = to_drm_syncobj(signals[i].sync);
|
||||
const uint64_t signal_value = signals[i].signal_value;
|
||||
|
||||
/* It's possible that we're waiting and signaling the same syncobj */
|
||||
if (signal_sobj == wait_sobj) {
|
||||
if (wait_sobj->base.flags & VK_SYNC_IS_TIMELINE) {
|
||||
/* We have to be signaling a higher value */
|
||||
assert(signal_value > wait_value);
|
||||
} else {
|
||||
/* Don't copy into ourself */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int err = device->sync->transfer(device->sync,
|
||||
signal_sobj->syncobj, signal_value,
|
||||
wait_sobj->syncobj, wait_value, 0);
|
||||
if (err) {
|
||||
return vk_errorf(device, VK_ERROR_UNKNOWN,
|
||||
"DRM_IOCTL_SYNCOBJ_TRANSFER failed: %m");
|
||||
}
|
||||
}
|
||||
|
||||
return VK_SUCCESS;
|
||||
} else {
|
||||
/* This is the annoying case where we have to do an actual many-to-many
|
||||
* transfer. This requires us to go through an intermediary syncobj.
|
||||
*
|
||||
* We'll build up tmp_syncobj as a timeline and then transfer from it
|
||||
* as a binary. The behavior of dma_fence_chain in the kernel is that
|
||||
* waiting on a whole chain waits on everything.
|
||||
*/
|
||||
uint32_t tmp_syncobj;
|
||||
int err = device->sync->create(device->sync, 0, &tmp_syncobj);
|
||||
if (err) {
|
||||
return vk_errorf(device, VK_ERROR_UNKNOWN,
|
||||
"DRM_IOCTL_SYNCOBJ_CREATE failed: %m");
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < wait_count; i++) {
|
||||
struct vk_drm_syncobj *wait_sobj = to_drm_syncobj(waits[i].sync);
|
||||
const uint64_t wait_value = waits[i].wait_value;
|
||||
|
||||
err = device->sync->transfer(device->sync, tmp_syncobj, i + 1,
|
||||
wait_sobj->syncobj, wait_value, 0);
|
||||
if (err) {
|
||||
err = device->sync->destroy(device->sync, tmp_syncobj);
|
||||
assert(err == 0);
|
||||
return vk_errorf(device, VK_ERROR_UNKNOWN,
|
||||
"DRM_IOCTL_SYNCOBJ_TRANSFER failed: %m");
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < signal_count; i++) {
|
||||
struct vk_drm_syncobj *signal_sobj = to_drm_syncobj(signals[i].sync);
|
||||
const uint64_t signal_value = signals[i].signal_value;
|
||||
|
||||
int err = device->sync->transfer(device->sync,
|
||||
signal_sobj->syncobj, signal_value,
|
||||
tmp_syncobj, 0, 0);
|
||||
if (err) {
|
||||
err = device->sync->destroy(device->sync, tmp_syncobj);
|
||||
assert(err == 0);
|
||||
return vk_errorf(device, VK_ERROR_UNKNOWN,
|
||||
"DRM_IOCTL_SYNCOBJ_TRANSFER failed: %m");
|
||||
}
|
||||
}
|
||||
|
||||
err = device->sync->destroy(device->sync, tmp_syncobj);
|
||||
assert(err == 0);
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
vk_device_has_timeline_syncobj(struct vk_device *device)
|
||||
{
|
||||
/* This is annoyingly complex but nothing compared to calling an ioctl. */
|
||||
for (const struct vk_sync_type *const *t =
|
||||
device->physical->supported_sync_types; *t; t++) {
|
||||
if (vk_sync_type_is_drm_syncobj(*t) &&
|
||||
((*t)->features & VK_SYNC_FEATURE_TIMELINE))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
VkResult
|
||||
vk_drm_syncobj_copy_payloads(struct vk_device *device,
|
||||
uint32_t wait_count,
|
||||
const struct vk_sync_wait *waits,
|
||||
uint32_t signal_count,
|
||||
const struct vk_sync_signal *signals)
|
||||
{
|
||||
/* First check if there's even anything to signal */
|
||||
if (signal_count == 0)
|
||||
return VK_SUCCESS;
|
||||
|
||||
/* If there's nothing to wait on, just signal everything */
|
||||
if (wait_count == 0)
|
||||
return vk_drm_syncobj_signal_many(device, signal_count, signals);
|
||||
|
||||
if (vk_device_has_timeline_syncobj(device)) {
|
||||
return vk_drm_syncobj_transfer_payloads(device, wait_count, waits,
|
||||
signal_count, signals);
|
||||
} else {
|
||||
return vk_drm_copy_sync_file_payloads(device, wait_count, waits,
|
||||
signal_count, signals);
|
||||
}
|
||||
}
|
||||
|
||||
struct vk_sync_type
|
||||
vk_drm_syncobj_get_type_from_provider(struct util_sync_provider *sync)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -54,6 +54,20 @@ vk_sync_as_drm_syncobj(struct vk_sync *sync)
|
|||
return container_of(sync, struct vk_drm_syncobj, base);
|
||||
}
|
||||
|
||||
/** Signal the signal syncobjs with the union/merge of the wait syncobjs
|
||||
*
|
||||
* This is an implementation of vk_device::copy_sync_payloads for drivers
|
||||
* which use DRM syncobjs as their only core synchronization primitive.
|
||||
* Internally, it either uses DRM_SYNCOBJ_TRANSFER if the driver supports
|
||||
* timelines or sync file import/export if the driver only supports binary
|
||||
* sync objects.
|
||||
*/
|
||||
VkResult vk_drm_syncobj_copy_payloads(struct vk_device *device,
|
||||
uint32_t wait_count,
|
||||
const struct vk_sync_wait *waits,
|
||||
uint32_t signal_count,
|
||||
const struct vk_sync_signal *signals);
|
||||
|
||||
struct util_sync_provider;
|
||||
struct vk_sync_type vk_drm_syncobj_get_type_from_provider(struct util_sync_provider *sync);
|
||||
struct vk_sync_type vk_drm_syncobj_get_type(int drm_fd);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue