mirror of
https://github.com/elwrcl/intel-hard-graphics.git
synced 2026-11-01 03:18:02 +03:00
init
This commit is contained in:
12511 files changed
+6156641
No files matched your search
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright © 2022 Collabora Ltd.
|
||||
*
|
||||
* 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 "lvp_private.h"
|
||||
#include "util/timespec.h"
|
||||
#include "vk_common_entrypoints.h"
|
||||
|
||||
static void
|
||||
lvp_pipe_sync_validate(ASSERTED struct lvp_pipe_sync *sync)
|
||||
{
|
||||
if (sync->signaled)
|
||||
assert(sync->fence == NULL);
|
||||
}
|
||||
|
||||
static VkResult
|
||||
lvp_pipe_sync_init(UNUSED struct vk_device *vk_device,
|
||||
struct vk_sync *vk_sync,
|
||||
uint64_t initial_value)
|
||||
{
|
||||
struct lvp_pipe_sync *sync = vk_sync_as_lvp_pipe_sync(vk_sync);
|
||||
|
||||
mtx_init(&sync->lock, mtx_plain);
|
||||
cnd_init(&sync->changed);
|
||||
sync->signaled = (initial_value != 0);
|
||||
sync->fence = NULL;
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
lvp_pipe_sync_finish(struct vk_device *vk_device,
|
||||
struct vk_sync *vk_sync)
|
||||
{
|
||||
struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
|
||||
struct lvp_pipe_sync *sync = vk_sync_as_lvp_pipe_sync(vk_sync);
|
||||
|
||||
lvp_pipe_sync_validate(sync);
|
||||
if (sync->fence)
|
||||
device->pscreen->fence_reference(device->pscreen, &sync->fence, NULL);
|
||||
cnd_destroy(&sync->changed);
|
||||
mtx_destroy(&sync->lock);
|
||||
}
|
||||
|
||||
void
|
||||
lvp_pipe_sync_signal_with_fence(struct lvp_device *device,
|
||||
struct lvp_pipe_sync *sync,
|
||||
struct pipe_fence_handle *fence)
|
||||
{
|
||||
mtx_lock(&sync->lock);
|
||||
lvp_pipe_sync_validate(sync);
|
||||
sync->signaled = fence == NULL;
|
||||
device->pscreen->fence_reference(device->pscreen, &sync->fence, fence);
|
||||
cnd_broadcast(&sync->changed);
|
||||
mtx_unlock(&sync->lock);
|
||||
}
|
||||
|
||||
static VkResult
|
||||
lvp_pipe_sync_signal(struct vk_device *vk_device,
|
||||
struct vk_sync *vk_sync,
|
||||
uint64_t value)
|
||||
{
|
||||
struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
|
||||
struct lvp_pipe_sync *sync = vk_sync_as_lvp_pipe_sync(vk_sync);
|
||||
|
||||
mtx_lock(&sync->lock);
|
||||
lvp_pipe_sync_validate(sync);
|
||||
sync->signaled = true;
|
||||
if (sync->fence)
|
||||
device->pscreen->fence_reference(device->pscreen, &sync->fence, NULL);
|
||||
cnd_broadcast(&sync->changed);
|
||||
mtx_unlock(&sync->lock);
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static VkResult
|
||||
lvp_pipe_sync_reset(struct vk_device *vk_device,
|
||||
struct vk_sync *vk_sync)
|
||||
{
|
||||
struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
|
||||
struct lvp_pipe_sync *sync = vk_sync_as_lvp_pipe_sync(vk_sync);
|
||||
|
||||
mtx_lock(&sync->lock);
|
||||
lvp_pipe_sync_validate(sync);
|
||||
sync->signaled = false;
|
||||
if (sync->fence)
|
||||
device->pscreen->fence_reference(device->pscreen, &sync->fence, NULL);
|
||||
cnd_broadcast(&sync->changed);
|
||||
mtx_unlock(&sync->lock);
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static VkResult
|
||||
lvp_pipe_sync_move(struct vk_device *vk_device,
|
||||
struct vk_sync *vk_dst,
|
||||
struct vk_sync *vk_src)
|
||||
{
|
||||
struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
|
||||
struct lvp_pipe_sync *dst = vk_sync_as_lvp_pipe_sync(vk_dst);
|
||||
struct lvp_pipe_sync *src = vk_sync_as_lvp_pipe_sync(vk_src);
|
||||
|
||||
/* Pull the fence out of the source */
|
||||
mtx_lock(&src->lock);
|
||||
struct pipe_fence_handle *fence = src->fence;
|
||||
bool signaled = src->signaled;
|
||||
src->fence = NULL;
|
||||
src->signaled = false;
|
||||
cnd_broadcast(&src->changed);
|
||||
mtx_unlock(&src->lock);
|
||||
|
||||
mtx_lock(&dst->lock);
|
||||
if (dst->fence)
|
||||
device->pscreen->fence_reference(device->pscreen, &dst->fence, NULL);
|
||||
dst->fence = fence;
|
||||
dst->signaled = signaled;
|
||||
cnd_broadcast(&dst->changed);
|
||||
mtx_unlock(&dst->lock);
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static VkResult
|
||||
lvp_pipe_sync_wait_locked(struct lvp_device *device,
|
||||
struct lvp_pipe_sync *sync,
|
||||
uint64_t wait_value,
|
||||
enum vk_sync_wait_flags wait_flags,
|
||||
uint64_t abs_timeout_ns)
|
||||
{
|
||||
assert(!(wait_flags & VK_SYNC_WAIT_ANY));
|
||||
|
||||
lvp_pipe_sync_validate(sync);
|
||||
|
||||
uint64_t now_ns = os_time_get_nano();
|
||||
while (!sync->signaled && !sync->fence) {
|
||||
if (now_ns >= abs_timeout_ns)
|
||||
return VK_TIMEOUT;
|
||||
|
||||
int ret;
|
||||
if (abs_timeout_ns >= INT64_MAX) {
|
||||
/* Common infinite wait case */
|
||||
ret = cnd_wait(&sync->changed, &sync->lock);
|
||||
} else {
|
||||
/* This is really annoying. The C11 threads API uses CLOCK_REALTIME
|
||||
* while all our absolute timeouts are in CLOCK_MONOTONIC. Best
|
||||
* thing we can do is to convert and hope the system admin doesn't
|
||||
* change the time out from under us.
|
||||
*/
|
||||
uint64_t rel_timeout_ns = abs_timeout_ns - now_ns;
|
||||
|
||||
struct timespec now_ts, abs_timeout_ts;
|
||||
timespec_get(&now_ts, TIME_UTC);
|
||||
if (timespec_add_nsec(&abs_timeout_ts, &now_ts, rel_timeout_ns)) {
|
||||
/* Overflowed; may as well be infinite */
|
||||
ret = cnd_wait(&sync->changed, &sync->lock);
|
||||
} else {
|
||||
ret = cnd_timedwait(&sync->changed, &sync->lock, &abs_timeout_ts);
|
||||
}
|
||||
}
|
||||
if (ret == thrd_error)
|
||||
return vk_errorf(device, VK_ERROR_UNKNOWN, "cnd_timedwait failed");
|
||||
|
||||
lvp_pipe_sync_validate(sync);
|
||||
|
||||
/* We don't trust the timeout condition on cnd_timedwait() because of
|
||||
* the potential clock issues caused by using CLOCK_REALTIME. Instead,
|
||||
* update now_ns, go back to the top of the loop, and re-check.
|
||||
*/
|
||||
now_ns = os_time_get_nano();
|
||||
}
|
||||
|
||||
if (sync->signaled || (wait_flags & VK_SYNC_WAIT_PENDING))
|
||||
return VK_SUCCESS;
|
||||
|
||||
/* Grab a reference before we drop the lock */
|
||||
struct pipe_fence_handle *fence = NULL;
|
||||
device->pscreen->fence_reference(device->pscreen, &fence, sync->fence);
|
||||
|
||||
mtx_unlock(&sync->lock);
|
||||
|
||||
uint64_t rel_timeout_ns =
|
||||
now_ns >= abs_timeout_ns ? 0 : abs_timeout_ns - now_ns;
|
||||
bool complete = device->pscreen->fence_finish(device->pscreen, NULL,
|
||||
fence, rel_timeout_ns);
|
||||
|
||||
device->pscreen->fence_reference(device->pscreen, &fence, NULL);
|
||||
|
||||
mtx_lock(&sync->lock);
|
||||
|
||||
lvp_pipe_sync_validate(sync);
|
||||
|
||||
if (!complete)
|
||||
return VK_TIMEOUT;
|
||||
|
||||
if (sync->fence == fence) {
|
||||
device->pscreen->fence_reference(device->pscreen, &sync->fence, NULL);
|
||||
sync->signaled = true;
|
||||
}
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static VkResult
|
||||
lvp_pipe_sync_wait(struct vk_device *vk_device,
|
||||
struct vk_sync *vk_sync,
|
||||
uint64_t wait_value,
|
||||
enum vk_sync_wait_flags wait_flags,
|
||||
uint64_t abs_timeout_ns)
|
||||
{
|
||||
struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
|
||||
struct lvp_pipe_sync *sync = vk_sync_as_lvp_pipe_sync(vk_sync);
|
||||
|
||||
mtx_lock(&sync->lock);
|
||||
|
||||
VkResult result = lvp_pipe_sync_wait_locked(device, sync, wait_value,
|
||||
wait_flags, abs_timeout_ns);
|
||||
|
||||
mtx_unlock(&sync->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBDRM
|
||||
static VkResult
|
||||
lvp_pipe_import_sync_file(struct vk_device *vk_device,
|
||||
struct vk_sync *vk_sync,
|
||||
int sync_file)
|
||||
{
|
||||
struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
|
||||
struct lvp_pipe_sync *sync = vk_sync_as_lvp_pipe_sync(vk_sync);
|
||||
|
||||
struct pipe_fence_handle *fence;
|
||||
device->queue.ctx->create_fence_fd(
|
||||
device->queue.ctx, &fence, sync_file, PIPE_FD_TYPE_NATIVE_SYNC);
|
||||
|
||||
if (fence == NULL)
|
||||
return VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
|
||||
lvp_pipe_sync_signal_with_fence(device, sync, fence);
|
||||
device->pscreen->fence_reference(device->pscreen, &fence, NULL);
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
static VkResult
|
||||
lvp_pipe_export_sync_file(struct vk_device *vk_device,
|
||||
struct vk_sync *vk_sync,
|
||||
int *sync_file)
|
||||
{
|
||||
struct lvp_pipe_sync *sync = vk_sync_as_lvp_pipe_sync(vk_sync);
|
||||
|
||||
/* It's not ideal, but since we cannot properly support sync files
|
||||
* from userspace, what we will do instead is wait for lavapipe to
|
||||
* finish rendering, so that we can safely export a sync file that
|
||||
* has already been signalled.
|
||||
*/
|
||||
vk_common_DeviceWaitIdle(vk_device_to_handle(vk_device));
|
||||
struct lvp_device *device = container_of(vk_device, struct lvp_device, vk);
|
||||
|
||||
/* If native_fence_fd is supported we use the gallium context to perform the
|
||||
* export, otherwise we return -1 which is still a valid sync fd in vulkan
|
||||
*/
|
||||
if (device->pscreen->caps.native_fence_fd) {
|
||||
*sync_file = device->pscreen->fence_get_fd(device->pscreen, sync->fence);
|
||||
return *sync_file != -1 ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY;
|
||||
} else {
|
||||
struct pipe_fence_handle *handle = NULL;
|
||||
device->queue.ctx->flush(device->queue.ctx, &handle, 0);
|
||||
device->pscreen->fence_finish(device->pscreen, NULL,
|
||||
handle, OS_TIMEOUT_INFINITE);
|
||||
device->pscreen->fence_reference(device->pscreen, &handle, NULL);
|
||||
*sync_file = -1;
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct vk_sync_type lvp_pipe_sync_type = {
|
||||
.size = sizeof(struct lvp_pipe_sync),
|
||||
.features = VK_SYNC_FEATURE_BINARY |
|
||||
VK_SYNC_FEATURE_GPU_WAIT |
|
||||
VK_SYNC_FEATURE_GPU_MULTI_WAIT |
|
||||
VK_SYNC_FEATURE_CPU_WAIT |
|
||||
VK_SYNC_FEATURE_CPU_RESET |
|
||||
VK_SYNC_FEATURE_CPU_SIGNAL |
|
||||
VK_SYNC_FEATURE_WAIT_PENDING,
|
||||
.init = lvp_pipe_sync_init,
|
||||
.finish = lvp_pipe_sync_finish,
|
||||
.signal = lvp_pipe_sync_signal,
|
||||
.reset = lvp_pipe_sync_reset,
|
||||
.move = lvp_pipe_sync_move,
|
||||
.wait = lvp_pipe_sync_wait,
|
||||
#ifdef HAVE_LIBDRM
|
||||
.import_sync_file = lvp_pipe_import_sync_file,
|
||||
.export_sync_file = lvp_pipe_export_sync_file,
|
||||
#endif
|
||||
};
|
||||
Reference in new issue
Block a user