diff --git a/render/vulkan/pass.c b/render/vulkan/pass.c index 04cb16cf9..2dca9d0f3 100644 --- a/render/vulkan/pass.c +++ b/render/vulkan/pass.c @@ -2,7 +2,9 @@ #include #include #include +#include #include +#include #include #include @@ -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); diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index e8e44b3f4..38e8ac9f4 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -222,7 +222,8 @@ static struct wlr_vk_stage_buffer *stage_buffer_create( .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = bsize, .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, }; res = vkCreateBuffer(r->dev->dev, &buf_info, NULL, &buf->buffer); @@ -1930,6 +1931,25 @@ static bool pipeline_key_equals(const struct wlr_vk_pipeline_key *a, return true; } +static const VkVertexInputBindingDescription instance_vert_binding = { + .binding = 0, + .stride = sizeof(float) * 4, + .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE, +}; +static const VkVertexInputAttributeDescription instance_vert_attr = { + .location = 0, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = 0, +}; +static const VkPipelineVertexInputStateCreateInfo instance_vert_input = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = &instance_vert_binding, + .vertexAttributeDescriptionCount = 1, + .pVertexAttributeDescriptions = &instance_vert_attr, +}; + // Initializes the pipeline for rendering textures and using the given // VkRenderPass and VkPipelineLayout. struct wlr_vk_pipeline *setup_get_or_create_pipeline( @@ -2061,10 +2081,6 @@ struct wlr_vk_pipeline *setup_get_or_create_pipeline( .dynamicStateCount = sizeof(dyn_states) / sizeof(dyn_states[0]), }; - VkPipelineVertexInputStateCreateInfo vertex = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - }; - VkGraphicsPipelineCreateInfo pinfo = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .layout = pipeline_layout->vk, @@ -2079,7 +2095,7 @@ struct wlr_vk_pipeline *setup_get_or_create_pipeline( .pMultisampleState = &multisample, .pViewportState = &viewport, .pDynamicState = &dynamic, - .pVertexInputState = &vertex, + .pVertexInputState = &instance_vert_input, }; VkPipelineCache cache = VK_NULL_HANDLE; @@ -2178,10 +2194,6 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, .dynamicStateCount = sizeof(dyn_states) / sizeof(dyn_states[0]), }; - VkPipelineVertexInputStateCreateInfo vertex = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - }; - VkGraphicsPipelineCreateInfo pinfo = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = NULL, @@ -2196,7 +2208,7 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, .pMultisampleState = &multisample, .pViewportState = &viewport, .pDynamicState = &dynamic, - .pVertexInputState = &vertex, + .pVertexInputState = &instance_vert_input, }; VkPipelineCache cache = VK_NULL_HANDLE; diff --git a/render/vulkan/shaders/common.vert b/render/vulkan/shaders/common.vert index f1579790d..82ea9658c 100644 --- a/render/vulkan/shaders/common.vert +++ b/render/vulkan/shaders/common.vert @@ -8,11 +8,14 @@ layout(push_constant, row_major) uniform UBO { vec2 uv_size; } data; +layout(location = 0) in vec4 inst_rect; + layout(location = 0) out vec2 uv; void main() { vec2 pos = vec2(float((gl_VertexIndex + 1) & 2) * 0.5f, float(gl_VertexIndex & 2) * 0.5f); + pos = inst_rect.xy + pos * inst_rect.zw; uv = data.uv_offset + pos * data.uv_size; gl_Position = data.proj * vec4(pos, 0.0, 1.0); }