render/vulkan: Defer to renderpass clears when possible

Signed-off-by: Joshua Ashton <joshua@froggi.es>
This commit is contained in:
Joshua Ashton 2021-10-16 15:00:21 +01:00
parent 3a685b10b6
commit f53aeeb4d4
2 changed files with 76 additions and 30 deletions

View file

@ -129,7 +129,8 @@ void vulkan_format_props_finish(struct wlr_vk_format_props *props);
struct wlr_vk_render_format_setup { struct wlr_vk_render_format_setup {
struct wl_list link; struct wl_list link;
VkFormat render_format; // used in renderpass VkFormat render_format; // used in renderpass
VkRenderPass render_pass; VkRenderPass render_pass_load;
VkRenderPass render_pass_clear;
VkPipeline tex_pipe; VkPipeline tex_pipe;
VkPipeline quad_pipe; VkPipeline quad_pipe;
@ -194,6 +195,10 @@ struct wlr_vk_renderer {
struct wl_list render_buffers; // wlr_vk_render_buffer struct wl_list render_buffers; // wlr_vk_render_buffer
bool in_render_pass;
bool pending_render_pass_clear;
VkClearValue render_pass_clear_color;
struct { struct {
VkCommandBuffer cb; VkCommandBuffer cb;
bool recording; bool recording;

View file

@ -155,7 +155,8 @@ static void destroy_render_format_setup(struct wlr_vk_renderer *renderer,
} }
VkDevice dev = renderer->dev->dev; VkDevice dev = renderer->dev->dev;
vkDestroyRenderPass(dev, setup->render_pass, NULL); vkDestroyRenderPass(dev, setup->render_pass_load, NULL);
vkDestroyRenderPass(dev, setup->render_pass_clear, NULL);
vkDestroyPipeline(dev, setup->tex_pipe, NULL); vkDestroyPipeline(dev, setup->tex_pipe, NULL);
vkDestroyPipeline(dev, setup->quad_pipe, NULL); vkDestroyPipeline(dev, setup->quad_pipe, NULL);
} }
@ -490,7 +491,8 @@ static struct wlr_vk_render_buffer *create_render_buffer(
fb_info.width = dmabuf.width; fb_info.width = dmabuf.width;
fb_info.height = dmabuf.height; fb_info.height = dmabuf.height;
fb_info.layers = 1u; fb_info.layers = 1u;
fb_info.renderPass = buffer->render_setup->render_pass; /* We can use either render pass here due to the compatibility rules. */
fb_info.renderPass = buffer->render_setup->render_pass_load;
res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->framebuffer); res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->framebuffer);
if (res != VK_SUCCESS) { if (res != VK_SUCCESS) {
@ -544,6 +546,28 @@ static bool vulkan_bind_buffer(struct wlr_renderer *wlr_renderer,
return true; return true;
} }
static void vulkan_begin_renderpass(struct wlr_vk_renderer *renderer, VkCommandBuffer cb) {
if (!renderer->in_render_pass) {
VkFramebuffer fb = renderer->current_render_buffer->framebuffer;
VkRect2D rect = {{0, 0}, {renderer->render_width, renderer->render_height}};
VkRenderPassBeginInfo rp_info = {0};
rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rp_info.renderArea = rect;
rp_info.renderPass = renderer->pending_render_pass_clear
? renderer->current_render_buffer->render_setup->render_pass_clear
: renderer->current_render_buffer->render_setup->render_pass_load;
rp_info.framebuffer = fb;
rp_info.clearValueCount = 1;
rp_info.pClearValues = &renderer->render_pass_clear_color;
vkCmdBeginRenderPass(cb, &rp_info, VK_SUBPASS_CONTENTS_INLINE);
renderer->in_render_pass = true;
}
}
static void vulkan_begin(struct wlr_renderer *wlr_renderer, static void vulkan_begin(struct wlr_renderer *wlr_renderer,
uint32_t width, uint32_t height) { uint32_t width, uint32_t height) {
struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer);
@ -554,20 +578,9 @@ static void vulkan_begin(struct wlr_renderer *wlr_renderer,
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
vkBeginCommandBuffer(cb, &begin_info); vkBeginCommandBuffer(cb, &begin_info);
// begin render pass
VkFramebuffer fb = renderer->current_render_buffer->framebuffer;
VkRect2D rect = {{0, 0}, {width, height}}; VkRect2D rect = {{0, 0}, {width, height}};
renderer->scissor = rect; renderer->scissor = rect;
VkRenderPassBeginInfo rp_info = {0};
rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rp_info.renderArea = rect;
rp_info.renderPass = renderer->current_render_buffer->render_setup->render_pass;
rp_info.framebuffer = fb;
rp_info.clearValueCount = 0;
vkCmdBeginRenderPass(cb, &rp_info, VK_SUBPASS_CONTENTS_INLINE);
VkViewport vp = {0.f, 0.f, (float) width, (float) height, 0.f, 1.f}; VkViewport vp = {0.f, 0.f, (float) width, (float) height, 0.f, 1.f};
vkCmdSetViewport(cb, 0, 1, &vp); vkCmdSetViewport(cb, 0, 1, &vp);
vkCmdSetScissor(cb, 0, 1, &rect); vkCmdSetScissor(cb, 0, 1, &rect);
@ -581,6 +594,8 @@ static void vulkan_begin(struct wlr_renderer *wlr_renderer,
renderer->render_width = width; renderer->render_width = width;
renderer->render_height = height; renderer->render_height = height;
renderer->bound_pipe = VK_NULL_HANDLE; renderer->bound_pipe = VK_NULL_HANDLE;
renderer->in_render_pass = false;
renderer->pending_render_pass_clear = false;
} }
static void vulkan_end(struct wlr_renderer *wlr_renderer) { static void vulkan_end(struct wlr_renderer *wlr_renderer) {
@ -594,7 +609,10 @@ static void vulkan_end(struct wlr_renderer *wlr_renderer) {
renderer->render_height = 0u; renderer->render_height = 0u;
renderer->bound_pipe = VK_NULL_HANDLE; renderer->bound_pipe = VK_NULL_HANDLE;
vkCmdEndRenderPass(render_cb); if (renderer->in_render_pass) {
vkCmdEndRenderPass(render_cb);
renderer->in_render_pass = false;
}
// insert acquire and release barriers for dmabuf-images // insert acquire and release barriers for dmabuf-images
unsigned barrier_count = wl_list_length(&renderer->foreign_textures) + 1; unsigned barrier_count = wl_list_length(&renderer->foreign_textures) + 1;
@ -753,6 +771,7 @@ static bool vulkan_render_subtexture_with_matrix(struct wlr_renderer *wlr_render
const float matrix[static 9], float alpha) { const float matrix[static 9], float alpha) {
struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer);
VkCommandBuffer cb = renderer->cb; VkCommandBuffer cb = renderer->cb;
vulkan_begin_renderpass(renderer, cb);
struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture); struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture);
assert(texture->renderer == renderer); assert(texture->renderer == renderer);
@ -809,10 +828,7 @@ static void vulkan_clear(struct wlr_renderer *wlr_renderer,
const float color[static 4]) { const float color[static 4]) {
struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer);
VkCommandBuffer cb = renderer->cb; VkCommandBuffer cb = renderer->cb;
VkClearValue clear_color;
VkClearAttachment att = {0};
att.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
att.colorAttachment = 0u;
// Input color values are given in srgb space, vulkan expects // Input color values are given in srgb space, vulkan expects
// them in linear space. We explicitly import argb8 render buffers // them in linear space. We explicitly import argb8 render buffers
@ -820,21 +836,36 @@ static void vulkan_clear(struct wlr_renderer *wlr_renderer,
// srgb first. // srgb first.
// But in other parts of wlroots we just always assume // But in other parts of wlroots we just always assume
// srgb so that's why we have to convert here. // srgb so that's why we have to convert here.
att.clearValue.color.float32[0] = color_to_linear(color[0]); clear_color.color.float32[0] = color_to_linear(color[0]);
att.clearValue.color.float32[1] = color_to_linear(color[1]); clear_color.color.float32[1] = color_to_linear(color[1]);
att.clearValue.color.float32[2] = color_to_linear(color[2]); clear_color.color.float32[2] = color_to_linear(color[2]);
att.clearValue.color.float32[3] = color[3]; // no conversion for alpha clear_color.color.float32[3] = color[3]; // no conversion for alpha
VkClearRect rect = {0}; if (!renderer->in_render_pass) {
rect.rect = renderer->scissor; /* Setting the scissor rect will start a renderpass
rect.layerCount = 1; * so we will fall into the path below for partial clears */
vkCmdClearAttachments(cb, 1, &att, 1, &rect); renderer->render_pass_clear_color = clear_color;
renderer->pending_render_pass_clear = true;
} else {
vulkan_begin_renderpass(renderer, cb);
VkClearAttachment att = {0};
att.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
att.colorAttachment = 0u;
att.clearValue = clear_color;
VkClearRect rect = {0};
rect.rect = renderer->scissor;
rect.layerCount = 1;
vkCmdClearAttachments(cb, 1, &att, 1, &rect);
}
} }
static void vulkan_scissor(struct wlr_renderer *wlr_renderer, static void vulkan_scissor(struct wlr_renderer *wlr_renderer,
struct wlr_box *box) { struct wlr_box *box) {
struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer);
VkCommandBuffer cb = renderer->cb; VkCommandBuffer cb = renderer->cb;
vulkan_begin_renderpass(renderer, cb);
uint32_t w = renderer->render_width; uint32_t w = renderer->render_width;
uint32_t h = renderer->render_height; uint32_t h = renderer->render_height;
@ -859,6 +890,7 @@ static void vulkan_render_quad_with_matrix(struct wlr_renderer *wlr_renderer,
const float color[static 4], const float matrix[static 9]) { const float color[static 4], const float matrix[static 9]) {
struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer);
VkCommandBuffer cb = renderer->cb; VkCommandBuffer cb = renderer->cb;
vulkan_begin_renderpass(renderer, cb);
VkPipeline pipe = renderer->current_render_buffer->render_setup->quad_pipe; VkPipeline pipe = renderer->current_render_buffer->render_setup->quad_pipe;
if (pipe != renderer->bound_pipe) { if (pipe != renderer->bound_pipe) {
@ -1299,14 +1331,23 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup(
rp_info.dependencyCount = 2u; rp_info.dependencyCount = 2u;
rp_info.pDependencies = deps; rp_info.pDependencies = deps;
res = vkCreateRenderPass(dev, &rp_info, NULL, &setup->render_pass); res = vkCreateRenderPass(dev, &rp_info, NULL, &setup->render_pass_load);
if (res != VK_SUCCESS) { if (res != VK_SUCCESS) {
wlr_vk_error("Failed to create render pass", res); wlr_vk_error("Failed to create render pass", res);
free(setup); free(setup);
return NULL; return NULL;
} }
if (!init_tex_pipeline(renderer, setup->render_pass, renderer->pipe_layout, attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
res = vkCreateRenderPass(dev, &rp_info, NULL, &setup->render_pass_clear);
if (res != VK_SUCCESS) {
wlr_vk_error("Failed to create render pass", res);
free(setup);
return NULL;
}
/* Only need one pipeline because of renderpass compatibility rules */
if (!init_tex_pipeline(renderer, setup->render_pass_load, renderer->pipe_layout,
&setup->tex_pipe)) { &setup->tex_pipe)) {
goto error; goto error;
} }
@ -1378,7 +1419,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup(
VkGraphicsPipelineCreateInfo pinfo = {0}; VkGraphicsPipelineCreateInfo pinfo = {0};
pinfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pinfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pinfo.layout = renderer->pipe_layout; pinfo.layout = renderer->pipe_layout;
pinfo.renderPass = setup->render_pass; pinfo.renderPass = setup->render_pass_load;
pinfo.subpass = 0; pinfo.subpass = 0;
pinfo.stageCount = 2; pinfo.stageCount = 2;
pinfo.pStages = quad_stages; pinfo.pStages = quad_stages;