mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-04-17 06:46:39 -04:00
render/vulkan: add upload thread
TODO: - Update blocking stage submission as well - Allow multiple data ptr accesses - Stop abusing FDs for multi-thread queue
This commit is contained in:
parent
79cbbfb366
commit
fee8969955
6 changed files with 208 additions and 31 deletions
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef RENDER_VULKAN_H
|
||||
#define RENDER_VULKAN_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
|
@ -270,6 +271,12 @@ struct wlr_vk_renderer {
|
|||
// Pool of command buffers
|
||||
struct wlr_vk_command_buffer command_buffers[VULKAN_COMMAND_BUFFERS_CAP];
|
||||
|
||||
struct {
|
||||
pthread_t thread;
|
||||
int worker_fd, control_fd;
|
||||
struct wl_event_source *event_source;
|
||||
} upload;
|
||||
|
||||
struct {
|
||||
struct wlr_vk_command_buffer *cb;
|
||||
uint64_t last_timeline_point;
|
||||
|
|
@ -301,6 +308,17 @@ struct wlr_vk_texture_view {
|
|||
struct wlr_vk_descriptor_pool *ds_pool;
|
||||
};
|
||||
|
||||
struct wlr_vk_upload_task {
|
||||
struct wlr_buffer *buffer;
|
||||
VkDeviceMemory memory;
|
||||
uint64_t timeline_point;
|
||||
char *dst;
|
||||
const char *src;
|
||||
uint32_t src_stride, dst_size;
|
||||
pixman_region32_t region;
|
||||
const struct wlr_pixel_format_info *format_info;
|
||||
};
|
||||
|
||||
struct wlr_vk_pipeline *setup_get_or_create_pipeline(
|
||||
struct wlr_vk_render_format_setup *setup,
|
||||
const struct wlr_vk_pipeline_key *key);
|
||||
|
|
@ -312,7 +330,8 @@ struct wlr_vk_texture_view *vulkan_texture_get_or_create_view(
|
|||
const struct wlr_vk_pipeline_layout *layout);
|
||||
|
||||
// Creates a vulkan renderer for the given device.
|
||||
struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev);
|
||||
struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev,
|
||||
struct wl_event_loop *loop);
|
||||
|
||||
// stage utility - for uploading/retrieving data
|
||||
// Gets an command buffer in recording state which is guaranteed to be
|
||||
|
|
@ -382,6 +401,9 @@ bool vulkan_read_pixels(struct wlr_vk_renderer *vk_renderer,
|
|||
uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y,
|
||||
uint32_t dst_x, uint32_t dst_y, void *data);
|
||||
|
||||
bool vulkan_init_upload_worker(struct wlr_vk_renderer *renderer,
|
||||
struct wl_event_loop *loop);
|
||||
|
||||
// State (e.g. image texture) associated with a surface.
|
||||
struct wlr_vk_texture {
|
||||
struct wlr_texture wlr_texture;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ struct wlr_vk_image_attribs {
|
|||
VkFormat format;
|
||||
};
|
||||
|
||||
struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd);
|
||||
struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(
|
||||
struct wl_event_loop *loop, int drm_fd);
|
||||
|
||||
VkInstance wlr_vk_renderer_get_instance(struct wlr_renderer *renderer);
|
||||
VkPhysicalDevice wlr_vk_renderer_get_physical_device(struct wlr_renderer *renderer);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ wlr_files += files(
|
|||
)
|
||||
|
||||
wlr_deps += dep_vulkan
|
||||
wlr_deps += dependency('threads')
|
||||
features += { 'vulkan-renderer': true }
|
||||
|
||||
subdir('shaders')
|
||||
|
|
|
|||
|
|
@ -369,6 +369,7 @@ bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// TODO
|
||||
VkTimelineSemaphoreSubmitInfoKHR timeline_submit_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
|
||||
.signalSemaphoreValueCount = 1,
|
||||
|
|
@ -2153,7 +2154,8 @@ error:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev) {
|
||||
struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev,
|
||||
struct wl_event_loop *loop) {
|
||||
struct wlr_vk_renderer *renderer;
|
||||
VkResult res;
|
||||
if (!(renderer = calloc(1, sizeof(*renderer)))) {
|
||||
|
|
@ -2210,6 +2212,10 @@ struct wlr_renderer *vulkan_renderer_create_for_device(struct wlr_vk_device *dev
|
|||
goto error;
|
||||
}
|
||||
|
||||
if (!vulkan_init_upload_worker(renderer, loop)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return &renderer->wlr_renderer;
|
||||
|
||||
error:
|
||||
|
|
@ -2217,7 +2223,8 @@ error:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd) {
|
||||
struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(struct wl_event_loop *loop,
|
||||
int drm_fd) {
|
||||
wlr_log(WLR_INFO, "The vulkan renderer is only experimental and "
|
||||
"not expected to be ready for daily use");
|
||||
wlr_log(WLR_INFO, "Run with VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation "
|
||||
|
|
@ -2252,7 +2259,7 @@ struct wlr_renderer *wlr_vk_renderer_create_with_drm_fd(int drm_fd) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
return vulkan_renderer_create_for_device(dev);
|
||||
return vulkan_renderer_create_for_device(dev, loop);
|
||||
}
|
||||
|
||||
VkInstance wlr_vk_renderer_get_instance(struct wlr_renderer *renderer) {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@
|
|||
#include <assert.h>
|
||||
#include <drm_fourcc.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <wlr/render/wlr_texture.h>
|
||||
|
|
@ -70,13 +72,150 @@ static void copy_pixels(char *vmap, const char *vdata, uint32_t tex_width,
|
|||
assert((uint32_t)(map - vmap) == size);
|
||||
}
|
||||
|
||||
static bool read_upload_task(struct wlr_vk_upload_task *task, int fd) {
|
||||
while (true) {
|
||||
errno = 0;
|
||||
ssize_t n = read(fd, task, sizeof(*task));
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
if (n == sizeof(*task)) {
|
||||
return true;
|
||||
} else if (n < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "read() failed");
|
||||
} else if (n > 0) {
|
||||
wlr_log(WLR_ERROR, "Unexpected partial read");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool write_upload_task(const struct wlr_vk_upload_task *task, int fd) {
|
||||
while (true) {
|
||||
errno = 0;
|
||||
ssize_t n = write(fd, task, sizeof(*task));
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
if (n == sizeof(*task)) {
|
||||
return true;
|
||||
} else if (n < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "write() failed");
|
||||
} else if (n > 0) {
|
||||
wlr_log(WLR_ERROR, "Unexpected partial write");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void process_upload_task(struct wlr_vk_renderer *renderer,
|
||||
struct wlr_vk_upload_task *task) {
|
||||
copy_pixels(task->dst, task->src, task->buffer->width, task->src_stride,
|
||||
task->dst_size, &task->region, task->format_info);
|
||||
|
||||
VkSemaphoreSignalInfoKHR signal_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO_KHR,
|
||||
.semaphore = renderer->upload_timeline_semaphore,
|
||||
.value = task->timeline_point,
|
||||
};
|
||||
VkResult res = renderer->dev->api.vkSignalSemaphoreKHR(renderer->dev->dev, &signal_info);
|
||||
if (res != VK_SUCCESS) {
|
||||
wlr_vk_error("vkMapMemory", res);
|
||||
}
|
||||
}
|
||||
|
||||
static void *run_uploads(void *data) {
|
||||
struct wlr_vk_renderer *renderer = data;
|
||||
|
||||
while (true) {
|
||||
struct wlr_vk_upload_task task = {0};
|
||||
if (!read_upload_task(&task, renderer->upload.worker_fd)) {
|
||||
break;
|
||||
}
|
||||
process_upload_task(renderer, &task);
|
||||
if (!write_upload_task(&task, renderer->upload.worker_fd)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close(renderer->upload.worker_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void handle_upload_task_complete(struct wlr_vk_renderer *renderer,
|
||||
struct wlr_vk_upload_task *task) {
|
||||
wlr_buffer_end_data_ptr_access(task->buffer);
|
||||
wlr_buffer_unlock(task->buffer);
|
||||
pixman_region32_fini(&task->region);
|
||||
}
|
||||
|
||||
static int handle_upload_fd_event(int fd, uint32_t mask, void *data) {
|
||||
struct wlr_vk_renderer *renderer = data;
|
||||
|
||||
if (mask & WL_EVENT_ERROR) {
|
||||
wlr_log(WLR_ERROR, "Upload worker FD error");
|
||||
return 0;
|
||||
}
|
||||
if (mask & WL_EVENT_HANGUP) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mask & WL_EVENT_READABLE) {
|
||||
struct wlr_vk_upload_task task = {0};
|
||||
if (!read_upload_task(&task, fd)) {
|
||||
return 0;
|
||||
}
|
||||
handle_upload_task_complete(renderer, &task);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool vulkan_init_upload_worker(struct wlr_vk_renderer *renderer,
|
||||
struct wl_event_loop *loop) {
|
||||
int sockets[2];
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "pipe() failed");
|
||||
return false;
|
||||
}
|
||||
renderer->upload.worker_fd = sockets[0];
|
||||
renderer->upload.control_fd = sockets[1];
|
||||
|
||||
renderer->upload.event_source = wl_event_loop_add_fd(loop,
|
||||
renderer->upload.control_fd, WL_EVENT_READABLE,
|
||||
handle_upload_fd_event, renderer);
|
||||
if (renderer->upload.event_source == NULL) {
|
||||
wlr_log(WLR_ERROR, "wl_event_loop_add_fd() failed");
|
||||
goto error_fds;
|
||||
}
|
||||
|
||||
// Block all signals in the new thread: let the main thread handle these
|
||||
sigset_t saved_sigset, new_sigset;
|
||||
sigfillset(&new_sigset);
|
||||
pthread_sigmask(SIG_BLOCK, &new_sigset, &saved_sigset);
|
||||
int ret = pthread_create(&renderer->upload.thread, NULL, run_uploads, renderer);
|
||||
pthread_sigmask(SIG_SETMASK, &saved_sigset, NULL);
|
||||
if (ret != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "pthread_create() failed");
|
||||
goto error_event_source;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error_event_source:
|
||||
wl_event_source_remove(renderer->upload.event_source);
|
||||
error_fds:
|
||||
close(renderer->upload.worker_fd);
|
||||
close(renderer->upload.control_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Will transition the texture to shaderReadOnlyOptimal layout for reading
|
||||
// from fragment shader later on
|
||||
static bool write_pixels(struct wlr_vk_texture *texture,
|
||||
static bool start_upload(struct wlr_vk_texture *texture, struct wlr_buffer *buffer,
|
||||
uint32_t stride, const pixman_region32_t *region, const void *vdata,
|
||||
VkImageLayout old_layout, VkPipelineStageFlags src_stage,
|
||||
VkAccessFlags src_access) {
|
||||
VkResult res;
|
||||
struct wlr_vk_renderer *renderer = texture->renderer;
|
||||
|
||||
const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(texture->format->drm);
|
||||
|
|
@ -144,19 +283,20 @@ static bool write_pixels(struct wlr_vk_texture *texture,
|
|||
buf_off += height * packed_stride;
|
||||
}
|
||||
|
||||
char *dst = (char *)span.buffer->map + span.alloc.start;
|
||||
copy_pixels(dst, vdata, texture->wlr_texture.width,
|
||||
stride, bsize, region, format_info);
|
||||
|
||||
VkSemaphoreSignalInfoKHR signal_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO_KHR,
|
||||
.semaphore = renderer->upload_timeline_semaphore,
|
||||
.value = timeline_point,
|
||||
struct wlr_vk_upload_task task = {
|
||||
.buffer = wlr_buffer_lock(buffer),
|
||||
.memory = span.buffer->memory,
|
||||
.timeline_point = timeline_point,
|
||||
.dst = (char *)span.buffer->map + span.alloc.start,
|
||||
.src = vdata,
|
||||
.src_stride = stride,
|
||||
.dst_size = bsize,
|
||||
.format_info = format_info,
|
||||
};
|
||||
res = renderer->dev->api.vkSignalSemaphoreKHR(renderer->dev->dev, &signal_info);
|
||||
if (res != VK_SUCCESS) {
|
||||
pixman_region32_init(&task.region);
|
||||
pixman_region32_copy(&task.region, region);
|
||||
if (!write_upload_task(&task, renderer->upload.control_fd)) {
|
||||
free(copies);
|
||||
wlr_vk_error("vkMapMemory", res);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -199,19 +339,21 @@ static bool vulkan_texture_update_from_buffer(struct wlr_texture *wlr_texture,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
|
||||
if (format != texture->format->drm) {
|
||||
ok = false;
|
||||
goto out;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ok = write_pixels(texture, stride, damage, data, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||
if (!start_upload(texture, buffer, stride, damage, data,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
out:
|
||||
return true;
|
||||
|
||||
error:
|
||||
wlr_buffer_end_data_ptr_access(buffer);
|
||||
return ok;
|
||||
return false;
|
||||
}
|
||||
|
||||
void vulkan_texture_destroy(struct wlr_vk_texture *texture) {
|
||||
|
|
@ -407,7 +549,8 @@ static void texture_set_format(struct wlr_vk_texture *texture,
|
|||
}
|
||||
|
||||
static struct wlr_texture *vulkan_texture_from_pixels(
|
||||
struct wlr_vk_renderer *renderer, uint32_t drm_fmt, uint32_t stride,
|
||||
struct wlr_vk_renderer *renderer, struct wlr_buffer *buffer,
|
||||
uint32_t drm_fmt, uint32_t stride,
|
||||
uint32_t width, uint32_t height, const void *data) {
|
||||
VkResult res;
|
||||
VkDevice dev = renderer->dev->dev;
|
||||
|
|
@ -480,7 +623,8 @@ static struct wlr_texture *vulkan_texture_from_pixels(
|
|||
|
||||
pixman_region32_t region;
|
||||
pixman_region32_init_rect(®ion, 0, 0, width, height);
|
||||
if (!write_pixels(texture, stride, ®ion, data, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
if (!start_upload(texture, buffer, stride, ®ion, data,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0)) {
|
||||
goto error;
|
||||
}
|
||||
|
|
@ -816,8 +960,10 @@ struct wlr_texture *vulkan_texture_from_buffer(struct wlr_renderer *wlr_renderer
|
|||
} else if (wlr_buffer_begin_data_ptr_access(buffer,
|
||||
WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride)) {
|
||||
struct wlr_texture *tex = vulkan_texture_from_pixels(renderer,
|
||||
format, stride, buffer->width, buffer->height, data);
|
||||
wlr_buffer_end_data_ptr_access(buffer);
|
||||
buffer, format, stride, buffer->width, buffer->height, data);
|
||||
if (tex == NULL) {
|
||||
wlr_buffer_end_data_ptr_access(buffer);
|
||||
}
|
||||
return tex;
|
||||
} else {
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ static struct wlr_renderer *renderer_autocreate(struct wlr_backend *backend, int
|
|||
log_creation_failure(is_auto, "Cannot create Vulkan renderer: no DRM FD available");
|
||||
} else {
|
||||
#if WLR_HAS_VULKAN_RENDERER
|
||||
renderer = wlr_vk_renderer_create_with_drm_fd(drm_fd);
|
||||
renderer = wlr_vk_renderer_create_with_drm_fd(loop, drm_fd);
|
||||
#else
|
||||
wlr_log(WLR_ERROR, "Cannot create Vulkan renderer: disabled at compile-time");
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue