Merge branch 'new-renderpass-clears' into 'master'

render/vulkan: Defer to renderpass clears when possible

See merge request wlroots/wlroots!3340
This commit is contained in:
Joshua Ashton 2022-10-30 16:52:57 +00:00
commit 9494eda286
2 changed files with 76 additions and 30 deletions

View file

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

View file

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