mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2026-04-11 08:21:34 -04:00
vulkan: Add support for render timer using timestamp queries
This commit is contained in:
parent
8a9e3a84b5
commit
35c35530a3
4 changed files with 105 additions and 0 deletions
|
|
@ -67,6 +67,9 @@ struct wlr_vk_device {
|
||||||
struct wlr_drm_format_set dmabuf_render_formats;
|
struct wlr_drm_format_set dmabuf_render_formats;
|
||||||
struct wlr_drm_format_set dmabuf_texture_formats;
|
struct wlr_drm_format_set dmabuf_texture_formats;
|
||||||
struct wlr_drm_format_set shm_texture_formats;
|
struct wlr_drm_format_set shm_texture_formats;
|
||||||
|
|
||||||
|
float timestamp_period;
|
||||||
|
uint32_t timestamp_valid_bits;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tries to find the VkPhysicalDevice for the given drm fd.
|
// Tries to find the VkPhysicalDevice for the given drm fd.
|
||||||
|
|
@ -412,6 +415,12 @@ VkCommandBuffer vulkan_record_stage_cb(struct wlr_vk_renderer *renderer);
|
||||||
// finished execution.
|
// finished execution.
|
||||||
bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer, int wait_sync_file_fd);
|
bool vulkan_submit_stage_wait(struct wlr_vk_renderer *renderer, int wait_sync_file_fd);
|
||||||
|
|
||||||
|
struct wlr_vk_render_timer {
|
||||||
|
struct wlr_render_timer base;
|
||||||
|
struct wlr_vk_renderer *renderer;
|
||||||
|
VkQueryPool query_pool;
|
||||||
|
};
|
||||||
|
|
||||||
struct wlr_vk_render_pass_texture {
|
struct wlr_vk_render_pass_texture {
|
||||||
struct wlr_vk_texture *texture;
|
struct wlr_vk_texture *texture;
|
||||||
|
|
||||||
|
|
@ -436,6 +445,8 @@ struct wlr_vk_render_pass {
|
||||||
struct wlr_drm_syncobj_timeline *signal_timeline;
|
struct wlr_drm_syncobj_timeline *signal_timeline;
|
||||||
uint64_t signal_point;
|
uint64_t signal_point;
|
||||||
|
|
||||||
|
struct wlr_vk_render_timer *timer;
|
||||||
|
|
||||||
struct wl_array textures; // struct wlr_vk_render_pass_texture
|
struct wl_array textures; // struct wlr_vk_render_pass_texture
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,11 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
|
||||||
|
|
||||||
vkCmdEndRenderPass(render_cb->vk);
|
vkCmdEndRenderPass(render_cb->vk);
|
||||||
|
|
||||||
|
if (pass->timer != NULL) {
|
||||||
|
vkCmdWriteTimestamp(render_cb->vk, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
|
||||||
|
pass->timer->query_pool, 1);
|
||||||
|
}
|
||||||
|
|
||||||
size_t pass_textures_len = pass->textures.size / sizeof(struct wlr_vk_render_pass_texture);
|
size_t pass_textures_len = pass->textures.size / sizeof(struct wlr_vk_render_pass_texture);
|
||||||
size_t render_wait_cap = (1 + pass_textures_len) * WLR_DMABUF_MAX_PLANES;
|
size_t render_wait_cap = (1 + pass_textures_len) * WLR_DMABUF_MAX_PLANES;
|
||||||
render_wait = calloc(render_wait_cap, sizeof(*render_wait));
|
render_wait = calloc(render_wait_cap, sizeof(*render_wait));
|
||||||
|
|
@ -1280,6 +1285,14 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend
|
||||||
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_ACCESS_SHADER_READ_BIT);
|
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_ACCESS_SHADER_READ_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct wlr_vk_render_timer *timer = NULL;
|
||||||
|
if (options != NULL && options->timer != NULL) {
|
||||||
|
timer = wl_container_of(options->timer, timer, base);
|
||||||
|
vkCmdResetQueryPool(cb->vk, timer->query_pool, 0, 2);
|
||||||
|
vkCmdWriteTimestamp(cb->vk, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||||
|
timer->query_pool, 0);
|
||||||
|
}
|
||||||
|
|
||||||
int width = buffer->wlr_buffer->width;
|
int width = buffer->wlr_buffer->width;
|
||||||
int height = buffer->wlr_buffer->height;
|
int height = buffer->wlr_buffer->height;
|
||||||
VkRect2D rect = { .extent = { width, height } };
|
VkRect2D rect = { .extent = { width, height } };
|
||||||
|
|
@ -1308,5 +1321,6 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend
|
||||||
pass->render_buffer_out = buffer_out;
|
pass->render_buffer_out = buffer_out;
|
||||||
pass->render_setup = render_setup;
|
pass->render_setup = render_setup;
|
||||||
pass->command_buffer = cb;
|
pass->command_buffer = cb;
|
||||||
|
pass->timer = timer;
|
||||||
return pass;
|
return pass;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1556,6 +1556,80 @@ static struct wlr_render_pass *vulkan_begin_buffer_pass(struct wlr_renderer *wlr
|
||||||
return &render_pass->base;
|
return &render_pass->base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct wlr_render_timer_impl render_timer_impl;
|
||||||
|
|
||||||
|
static struct wlr_render_timer *vulkan_render_timer_create(
|
||||||
|
struct wlr_renderer *wlr_renderer) {
|
||||||
|
struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer);
|
||||||
|
if (renderer->dev->timestamp_valid_bits == 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to create render timer: "
|
||||||
|
"timestamp queries not supported by queue family");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_vk_render_timer *timer = calloc(1, sizeof(*timer));
|
||||||
|
if (!timer) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkQueryPoolCreateInfo pool_info = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
|
||||||
|
.queryType = VK_QUERY_TYPE_TIMESTAMP,
|
||||||
|
.queryCount = 2,
|
||||||
|
};
|
||||||
|
VkResult res = vkCreateQueryPool(renderer->dev->dev, &pool_info,
|
||||||
|
NULL, &timer->query_pool);
|
||||||
|
if (res != VK_SUCCESS) {
|
||||||
|
wlr_vk_error("vkCreateQueryPool", res);
|
||||||
|
free(timer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer->base.impl = &render_timer_impl;
|
||||||
|
timer->renderer = renderer;
|
||||||
|
return &timer->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vulkan_render_timer_get_duration_ns(
|
||||||
|
struct wlr_render_timer *wlr_timer) {
|
||||||
|
struct wlr_vk_render_timer *timer =
|
||||||
|
wl_container_of(wlr_timer, timer, base);
|
||||||
|
struct wlr_vk_renderer *renderer = timer->renderer;
|
||||||
|
|
||||||
|
// Layout: [ timestamp1, avail1, timestamp2, avail2 ]
|
||||||
|
uint64_t data[4] = {0};
|
||||||
|
VkResult res = vkGetQueryPoolResults(renderer->dev->dev,
|
||||||
|
timer->query_pool, 0, 2, sizeof(data), data,
|
||||||
|
2 * sizeof(uint64_t),
|
||||||
|
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
|
||||||
|
if (res == VK_NOT_READY || data[1] == 0 || data[3] == 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to get render duration: "
|
||||||
|
"timestamp query results not yet ready");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (res != VK_SUCCESS) {
|
||||||
|
wlr_vk_error("vkGetQueryPoolResults", res);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t ticks = data[2] - data[0];
|
||||||
|
return (int)(ticks * renderer->dev->timestamp_period);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vulkan_render_timer_destroy(
|
||||||
|
struct wlr_render_timer *wlr_timer) {
|
||||||
|
struct wlr_vk_render_timer *timer =
|
||||||
|
wl_container_of(wlr_timer, timer, base);
|
||||||
|
vkDestroyQueryPool(timer->renderer->dev->dev, timer->query_pool, NULL);
|
||||||
|
free(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wlr_render_timer_impl render_timer_impl = {
|
||||||
|
.get_duration_ns = vulkan_render_timer_get_duration_ns,
|
||||||
|
.destroy = vulkan_render_timer_destroy,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct wlr_renderer_impl renderer_impl = {
|
static const struct wlr_renderer_impl renderer_impl = {
|
||||||
.get_texture_formats = vulkan_get_texture_formats,
|
.get_texture_formats = vulkan_get_texture_formats,
|
||||||
.get_render_formats = vulkan_get_render_formats,
|
.get_render_formats = vulkan_get_render_formats,
|
||||||
|
|
@ -1563,6 +1637,7 @@ static const struct wlr_renderer_impl renderer_impl = {
|
||||||
.get_drm_fd = vulkan_get_drm_fd,
|
.get_drm_fd = vulkan_get_drm_fd,
|
||||||
.texture_from_buffer = vulkan_texture_from_buffer,
|
.texture_from_buffer = vulkan_texture_from_buffer,
|
||||||
.begin_buffer_pass = vulkan_begin_buffer_pass,
|
.begin_buffer_pass = vulkan_begin_buffer_pass,
|
||||||
|
.render_timer_create = vulkan_render_timer_create,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initializes the VkDescriptorSetLayout and VkPipelineLayout needed
|
// Initializes the VkDescriptorSetLayout and VkPipelineLayout needed
|
||||||
|
|
|
||||||
|
|
@ -488,10 +488,15 @@ struct wlr_vk_device *vulkan_device_create(struct wlr_vk_instance *ini,
|
||||||
graphics_found = queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT;
|
graphics_found = queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT;
|
||||||
if (graphics_found) {
|
if (graphics_found) {
|
||||||
dev->queue_family = i;
|
dev->queue_family = i;
|
||||||
|
dev->timestamp_valid_bits = queue_props[i].timestampValidBits;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(graphics_found);
|
assert(graphics_found);
|
||||||
|
|
||||||
|
VkPhysicalDeviceProperties phdev_props;
|
||||||
|
vkGetPhysicalDeviceProperties(phdev, &phdev_props);
|
||||||
|
dev->timestamp_period = phdev_props.limits.timestampPeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool exportable_semaphore = false, importable_semaphore = false;
|
bool exportable_semaphore = false, importable_semaphore = false;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue