Skip to content
This repository has been archived by the owner on Nov 1, 2021. It is now read-only.

render/vulkan: Defer to renderpass clears when possible #3269

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion include/render/vulkan.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,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;
Expand Down Expand Up @@ -194,6 +195,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;
Expand Down
100 changes: 70 additions & 30 deletions render/vulkan/renderer.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,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);
}
Expand Down Expand Up @@ -490,7 +491,7 @@ 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;
fb_info.renderPass = buffer->render_setup->render_pass_load;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add a comment here stating that we can use either VkRenderPass since they are compatible due to only loadOp being different.


res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->framebuffer);
if (res != VK_SUCCESS) {
Expand Down Expand Up @@ -544,6 +545,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);
Expand All @@ -554,20 +577,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);
Expand All @@ -581,6 +593,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) {
Expand All @@ -594,7 +608,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;
Expand Down Expand Up @@ -753,6 +770,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);
Expand Down Expand Up @@ -820,32 +838,44 @@ 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
// as srgb, vulkan will convert the input values we give here to
// 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

VkClearRect rect = {0};
rect.rect = renderer->scissor;
rect.layerCount = 1;
vkCmdClearAttachments(cb, 1, &att, 1, &rect);
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

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this really needed? we only land here if renderer->in_render_pass == true in which case this call is a no-op right?


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;
Expand All @@ -870,6 +900,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) {
Expand Down Expand Up @@ -1310,14 +1341,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;
}

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;
}

if (!init_tex_pipeline(renderer, setup->render_pass, renderer->pipe_layout,
/* 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;
}
Expand Down Expand Up @@ -1389,7 +1429,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;
Expand Down