diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 82d3e40fd..021749c27 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -67,6 +67,9 @@ struct wlr_vk_device { struct wlr_drm_format_set dmabuf_render_formats; struct wlr_drm_format_set dmabuf_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. @@ -412,6 +415,12 @@ VkCommandBuffer vulkan_record_stage_cb(struct wlr_vk_renderer *renderer); // finished execution. 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_texture *texture; @@ -436,6 +445,8 @@ struct wlr_vk_render_pass { struct wlr_drm_syncobj_timeline *signal_timeline; uint64_t signal_point; + struct wlr_vk_render_timer *timer; + struct wl_array textures; // struct wlr_vk_render_pass_texture }; diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index becd060f4..01e8fbd7a 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -295,6 +295,11 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { 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 render_wait_cap = (1 + pass_textures_len) * WLR_DMABUF_MAX_PLANES; 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); } + 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 height = buffer->wlr_buffer->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_setup = render_setup; pass->command_buffer = cb; + pass->timer = timer; return pass; } diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index e04f95a0a..434ab4769 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -1556,6 +1556,80 @@ static struct wlr_render_pass *vulkan_begin_buffer_pass(struct wlr_renderer *wlr 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 = { .get_texture_formats = vulkan_get_texture_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, .texture_from_buffer = vulkan_texture_from_buffer, .begin_buffer_pass = vulkan_begin_buffer_pass, + .render_timer_create = vulkan_render_timer_create, }; // Initializes the VkDescriptorSetLayout and VkPipelineLayout needed diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index b4a5e0e11..3877ec2a7 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -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; if (graphics_found) { dev->queue_family = i; + dev->timestamp_valid_bits = queue_props[i].timestampValidBits; break; } } assert(graphics_found); + + VkPhysicalDeviceProperties phdev_props; + vkGetPhysicalDeviceProperties(phdev, &phdev_props); + dev->timestamp_period = phdev_props.limits.timestampPeriod; } bool exportable_semaphore = false, importable_semaphore = false;