diff --git a/include/render/vulkan.h b/include/render/vulkan.h index 5157e9181..7133e294f 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -120,7 +120,8 @@ void vulkan_format_props_finish(struct wlr_vk_format_props *props); struct wlr_vk_render_format_setup { struct wl_list link; VkFormat render_format; // used in renderpass - VkRenderPass render_pass; + VkRenderPass render_pass_load; + VkRenderPass render_pass_clear; VkPipeline tex_pipe; VkPipeline quad_pipe; @@ -184,6 +185,10 @@ struct wlr_vk_renderer { struct wl_list render_buffers; // wlr_vk_render_buffer + bool in_render_pass; + bool pending_render_pass_clear; + VkClearValue render_pass_clear_color; + struct { VkCommandBuffer cb; bool recording; diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 2c78ada0c..c2c835368 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -156,7 +156,8 @@ static void destroy_render_format_setup(struct wlr_vk_renderer *renderer, } 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->quad_pipe, NULL); } @@ -481,7 +482,8 @@ static struct wlr_vk_render_buffer *create_render_buffer( fb_info.width = dmabuf.width; fb_info.height = dmabuf.height; 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); if (res != VK_SUCCESS) { @@ -547,6 +549,28 @@ static bool vulkan_bind_buffer(struct wlr_renderer *wlr_renderer, 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, uint32_t width, uint32_t height) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); @@ -557,20 +581,9 @@ static void vulkan_begin(struct wlr_renderer *wlr_renderer, begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; vkBeginCommandBuffer(cb, &begin_info); - // begin render pass - VkFramebuffer fb = renderer->current_render_buffer->framebuffer; - VkRect2D rect = {{0, 0}, {width, height}}; 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}; vkCmdSetViewport(cb, 0, 1, &vp); vkCmdSetScissor(cb, 0, 1, &rect); @@ -584,6 +597,8 @@ static void vulkan_begin(struct wlr_renderer *wlr_renderer, renderer->render_width = width; renderer->render_height = height; 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) { @@ -597,7 +612,10 @@ static void vulkan_end(struct wlr_renderer *wlr_renderer) { renderer->render_height = 0u; 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 unsigned barrier_count = wl_list_length(&renderer->foreign_textures) + 1; @@ -756,6 +774,7 @@ static bool vulkan_render_subtexture_with_matrix(struct wlr_renderer *wlr_render const float matrix[static 9], float alpha) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); VkCommandBuffer cb = renderer->cb; + vulkan_begin_renderpass(renderer, cb); struct wlr_vk_texture *texture = vulkan_get_texture(wlr_texture); assert(texture->renderer == renderer); @@ -807,10 +826,7 @@ static void vulkan_clear(struct wlr_renderer *wlr_renderer, const float color[static 4]) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); VkCommandBuffer cb = renderer->cb; - - VkClearAttachment att = {0}; - att.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - att.colorAttachment = 0u; + VkClearValue clear_color; // Input color values are given in srgb space, vulkan expects // them in linear space. We explicitly import argb8 render buffers @@ -818,21 +834,36 @@ static void vulkan_clear(struct wlr_renderer *wlr_renderer, // srgb first. // But in other parts of wlroots we just always assume // srgb so that's why we have to convert here. - att.clearValue.color.float32[0] = color_to_linear(color[0]); - att.clearValue.color.float32[1] = color_to_linear(color[1]); - att.clearValue.color.float32[2] = color_to_linear(color[2]); - att.clearValue.color.float32[3] = color[3]; // no conversion for alpha + clear_color.color.float32[0] = color_to_linear(color[0]); + clear_color.color.float32[1] = color_to_linear(color[1]); + clear_color.color.float32[2] = color_to_linear(color[2]); + clear_color.color.float32[3] = color[3]; // no conversion for alpha - VkClearRect rect = {0}; - rect.rect = renderer->scissor; - rect.layerCount = 1; - vkCmdClearAttachments(cb, 1, &att, 1, &rect); + if (!renderer->in_render_pass) { + /* Setting the scissor rect will start a renderpass + * so we will fall into the path below for partial clears */ + 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, struct wlr_box *box) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); VkCommandBuffer cb = renderer->cb; + vulkan_begin_renderpass(renderer, cb); uint32_t w = renderer->render_width; uint32_t h = renderer->render_height; @@ -857,6 +888,7 @@ static void vulkan_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, const float color[static 4], const float matrix[static 9]) { struct wlr_vk_renderer *renderer = vulkan_get_renderer(wlr_renderer); VkCommandBuffer cb = renderer->cb; + vulkan_begin_renderpass(renderer, cb); VkPipeline pipe = renderer->current_render_buffer->render_setup->quad_pipe; if (pipe != renderer->bound_pipe) { @@ -1496,14 +1528,23 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( rp_info.dependencyCount = 2u; 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) { wlr_vk_error("Failed to create render pass", res); free(setup); 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)) { goto error; } @@ -1575,7 +1616,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( VkGraphicsPipelineCreateInfo pinfo = {0}; pinfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pinfo.layout = renderer->pipe_layout; - pinfo.renderPass = setup->render_pass; + pinfo.renderPass = setup->render_pass_load; pinfo.subpass = 0; pinfo.stageCount = 2; pinfo.pStages = quad_stages;