render/vulkan: Use instanced draws instead of scissors

Similar to what we have already done for gles2. To simplify things we
use the staging ring buffer for the vertex buffers by extending the
usage bits, rather than introducing a separate pool.
This commit is contained in:
Kenny Levinsen 2026-04-12 17:47:40 +02:00
parent 3c9f1e35b1
commit b16dd0178b
3 changed files with 156 additions and 36 deletions

View file

@ -2,7 +2,9 @@
#include <drm_fourcc.h>
#include <stdlib.h>
#include <unistd.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include <wlr/util/transform.h>
#include <wlr/render/color.h>
#include <wlr/render/drm_syncobj.h>
@ -285,6 +287,20 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) {
int clip_rects_len;
const pixman_box32_t *clip_rects = pixman_region32_rectangles(
clip, &clip_rects_len);
float identity[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
struct wlr_vk_buffer_span span = vulkan_get_stage_span(renderer,
sizeof(identity), sizeof(identity));
if (!span.buffer) {
pass->failed = true;
goto error;
}
memcpy((char *)span.buffer->cpu_mapping + span.offset, identity, sizeof(identity));
VkDeviceSize vb_offset = span.offset;
vkCmdBindVertexBuffers(render_cb->vk, 0, 1, &span.buffer->buffer, &vb_offset);
for (int i = 0; i < clip_rects_len; i++) {
VkRect2D rect;
convert_pixman_box_to_vk_rect(&clip_rects[i], &rect);
@ -656,20 +672,6 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass,
int clip_rects_len;
const pixman_box32_t *clip_rects = pixman_region32_rectangles(&clip, &clip_rects_len);
// Record regions possibly updated for use in second subpass
for (int i = 0; i < clip_rects_len; i++) {
struct wlr_box clip_box = {
.x = clip_rects[i].x1,
.y = clip_rects[i].y1,
.width = clip_rects[i].x2 - clip_rects[i].x1,
.height = clip_rects[i].y2 - clip_rects[i].y1,
};
struct wlr_box intersection;
if (!wlr_box_intersection(&intersection, &options->box, &clip_box)) {
continue;
}
render_pass_mark_box_updated(pass, &intersection);
}
struct wlr_box box;
wlr_render_rect_options_get_box(options, pass->render_buffer->wlr_buffer, &box);
@ -692,6 +694,45 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass,
break;
}
if (clip_rects_len == 0) {
break;
}
const VkDeviceSize instance_size = 4 * sizeof(float);
struct wlr_vk_buffer_span span = vulkan_get_stage_span(pass->renderer,
clip_rects_len * instance_size, 16);
if (!span.buffer) {
pass->failed = true;
break;
}
float *instance_data = (float *)((char *)span.buffer->cpu_mapping + span.offset);
int instance_count = 0;
for (int i = 0; i < clip_rects_len; i++) {
struct wlr_box clip_box = {
.x = clip_rects[i].x1,
.y = clip_rects[i].y1,
.width = clip_rects[i].x2 - clip_rects[i].x1,
.height = clip_rects[i].y2 - clip_rects[i].y1,
};
struct wlr_box intersection;
if (!wlr_box_intersection(&intersection, &box, &clip_box)) {
continue;
}
render_pass_mark_box_updated(pass, &intersection);
instance_data[instance_count * 4 + 0] = (float)(intersection.x - box.x) / box.width;
instance_data[instance_count * 4 + 1] = (float)(intersection.y - box.y) / box.height;
instance_data[instance_count * 4 + 2] = (float)intersection.width / box.width;
instance_data[instance_count * 4 + 3] = (float)intersection.height / box.height;
instance_count++;
}
if (instance_count < clip_rects_len) {
vulkan_return_stage_span(&span,
(clip_rects_len - instance_count) * instance_size);
if (instance_count == 0) {
break;
}
}
struct wlr_vk_vert_pcr_data vert_pcr_data = {
.uv_off = { 0, 0 },
.uv_size = { 1, 1 },
@ -705,12 +746,17 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass,
VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vert_pcr_data), sizeof(float) * 4,
linear_color);
for (int i = 0; i < clip_rects_len; i++) {
VkRect2D rect;
convert_pixman_box_to_vk_rect(&clip_rects[i], &rect);
vkCmdSetScissor(cb, 0, 1, &rect);
vkCmdDraw(cb, 4, 1, 0, 0);
}
VkDeviceSize vb_offset = span.offset;
vkCmdBindVertexBuffers(cb, 0, 1, &span.buffer->buffer, &vb_offset);
VkRect2D full_scissor = {
.extent = {
.width = pass->render_buffer->wlr_buffer->width,
.height = pass->render_buffer->wlr_buffer->height,
},
};
vkCmdSetScissor(cb, 0, 1, &full_scissor);
vkCmdDraw(cb, 4, instance_count, 0, 0);
break;
case WLR_RENDER_BLEND_MODE_NONE:;
VkClearAttachment clear_att = {
@ -727,6 +773,18 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass,
.layerCount = 1,
};
for (int i = 0; i < clip_rects_len; i++) {
struct wlr_box clip_box = {
.x = clip_rects[i].x1,
.y = clip_rects[i].y1,
.width = clip_rects[i].x2 - clip_rects[i].x1,
.height = clip_rects[i].y2 - clip_rects[i].y1,
};
struct wlr_box intersection;
if (!wlr_box_intersection(&intersection, &options->box, &clip_box)) {
continue;
}
render_pass_mark_box_updated(pass, &intersection);
convert_pixman_box_to_vk_rect(&clip_rects[i], &clear_rect.rect);
vkCmdClearAttachments(cb, 1, &clear_att, 1, &clear_rect);
}
@ -888,12 +946,23 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
int clip_rects_len;
const pixman_box32_t *clip_rects = pixman_region32_rectangles(&clip, &clip_rects_len);
for (int i = 0; i < clip_rects_len; i++) {
VkRect2D rect;
convert_pixman_box_to_vk_rect(&clip_rects[i], &rect);
vkCmdSetScissor(cb, 0, 1, &rect);
vkCmdDraw(cb, 4, 1, 0, 0);
if (clip_rects_len == 0) {
goto out;
}
const VkDeviceSize instance_size = 4 * sizeof(float);
struct wlr_vk_buffer_span span = vulkan_get_stage_span(renderer,
clip_rects_len * instance_size, 16);
if (!span.buffer) {
pass->failed = true;
goto out;
}
float *instance_data = (float *)((char *)span.buffer->cpu_mapping + span.offset);
int instance_count = 0;
enum wl_output_transform inv_transform =
wlr_output_transform_invert(options->transform);
for (int i = 0; i < clip_rects_len; i++) {
struct wlr_box clip_box = {
.x = clip_rects[i].x1,
.y = clip_rects[i].y1,
@ -905,8 +974,44 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass,
continue;
}
render_pass_mark_box_updated(pass, &intersection);
struct wlr_fbox norm = {
.x = (double)(intersection.x - dst_box.x) / dst_box.width,
.y = (double)(intersection.y - dst_box.y) / dst_box.height,
.width = (double)intersection.width / dst_box.width,
.height = (double)intersection.height / dst_box.height,
};
if (options->transform != WL_OUTPUT_TRANSFORM_NORMAL) {
wlr_fbox_transform(&norm, &norm, inv_transform, 1.0, 1.0);
}
instance_data[instance_count * 4 + 0] = (float)norm.x;
instance_data[instance_count * 4 + 1] = (float)norm.y;
instance_data[instance_count * 4 + 2] = (float)norm.width;
instance_data[instance_count * 4 + 3] = (float)norm.height;
instance_count++;
}
if (instance_count < clip_rects_len) {
vulkan_return_stage_span(&span,
(clip_rects_len - instance_count) * instance_size);
}
if (instance_count > 0) {
VkDeviceSize vb_offset = span.offset;
vkCmdBindVertexBuffers(cb, 0, 1, &span.buffer->buffer, &vb_offset);
VkRect2D full_scissor = {
.extent = {
.width = pass->render_buffer->wlr_buffer->width,
.height = pass->render_buffer->wlr_buffer->height,
},
};
vkCmdSetScissor(cb, 0, 1, &full_scissor);
vkCmdDraw(cb, 4, instance_count, 0, 0);
}
out:
texture->last_used_cb = pass->command_buffer;
pixman_region32_fini(&clip);