1 /*
2 * Copyright © 2022 Collabora Ltd
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "vk_meta_private.h"
25
26 #include "vk_command_buffer.h"
27 #include "vk_command_pool.h"
28 #include "vk_device.h"
29
30 #include "nir_builder.h"
31
32 const VkPipelineVertexInputStateCreateInfo vk_meta_draw_rects_vi_state = {
33 .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
34 .vertexBindingDescriptionCount = 1,
35 .pVertexBindingDescriptions = &(const VkVertexInputBindingDescription) {
36 .binding = 0,
37 .stride = 4 * sizeof(uint32_t),
38 .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
39 },
40 .vertexAttributeDescriptionCount = 1,
41 .pVertexAttributeDescriptions = &(const VkVertexInputAttributeDescription) {
42 .location = 0,
43 .binding = 0,
44 .format = VK_FORMAT_R32G32B32A32_UINT,
45 .offset = 0,
46 },
47 };
48
49 const VkPipelineInputAssemblyStateCreateInfo vk_meta_draw_rects_ia_state = {
50 .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
51 .topology = VK_PRIMITIVE_TOPOLOGY_META_RECT_LIST_MESA,
52 .primitiveRestartEnable = VK_FALSE,
53 };
54
55 const VkPipelineViewportStateCreateInfo vk_meta_draw_rects_vs_state = {
56 .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
57 .viewportCount = 1,
58 .scissorCount = 1,
59 };
60
61 nir_shader *
vk_meta_draw_rects_vs_nir(struct vk_meta_device * device,bool use_gs)62 vk_meta_draw_rects_vs_nir(struct vk_meta_device *device, bool use_gs)
63 {
64 nir_builder build = nir_builder_init_simple_shader(MESA_SHADER_VERTEX, NULL,
65 "vk-meta-draw-rects-vs");
66 nir_builder *b = &build;
67
68 nir_variable *in = nir_variable_create(b->shader, nir_var_shader_in,
69 glsl_uvec4_type(), "vtx_in");
70 in->data.location = VERT_ATTRIB_GENERIC0;
71
72 nir_variable *pos =
73 nir_variable_create(b->shader, nir_var_shader_out, glsl_vec4_type(),
74 use_gs ? "pos_out" : "gl_Position");
75 pos->data.location = use_gs ? VARYING_SLOT_VAR0 : VARYING_SLOT_POS;
76
77 nir_variable *layer =
78 nir_variable_create(b->shader, nir_var_shader_out, glsl_int_type(),
79 use_gs ? "layer_out" : "gl_Layer");
80 layer->data.location = use_gs ? VARYING_SLOT_VAR1 : VARYING_SLOT_LAYER;
81
82 nir_def *vtx = nir_load_var(b, in);
83 nir_store_var(b, pos, nir_vec4(b, nir_channel(b, vtx, 0),
84 nir_channel(b, vtx, 1),
85 nir_channel(b, vtx, 2),
86 nir_imm_float(b, 1)),
87 0xf);
88
89 nir_store_var(b, layer, nir_iadd(b, nir_load_instance_id(b),
90 nir_channel(b, vtx, 3)),
91 0x1);
92
93 return b->shader;
94 }
95
96 nir_shader *
vk_meta_draw_rects_gs_nir(struct vk_meta_device * device)97 vk_meta_draw_rects_gs_nir(struct vk_meta_device *device)
98 {
99 nir_builder build =
100 nir_builder_init_simple_shader(MESA_SHADER_GEOMETRY, NULL,
101 "vk-meta-draw-rects-gs");
102 nir_builder *b = &build;
103
104 nir_variable *pos_in =
105 nir_variable_create(b->shader, nir_var_shader_in,
106 glsl_array_type(glsl_vec4_type(), 3, 0), "pos_in");
107 pos_in->data.location = VARYING_SLOT_VAR0;
108
109 nir_variable *layer_in =
110 nir_variable_create(b->shader, nir_var_shader_in,
111 glsl_array_type(glsl_int_type(), 3, 0), "layer_in");
112 layer_in->data.location = VARYING_SLOT_VAR1;
113
114 nir_variable *pos_out =
115 nir_variable_create(b->shader, nir_var_shader_out,
116 glsl_vec4_type(), "gl_Position");
117 pos_out->data.location = VARYING_SLOT_POS;
118
119 nir_variable *layer_out =
120 nir_variable_create(b->shader, nir_var_shader_out,
121 glsl_int_type(), "gl_Layer");
122 layer_out->data.location = VARYING_SLOT_LAYER;
123
124 for (unsigned i = 0; i < 3; i++) {
125 nir_deref_instr *pos_in_deref =
126 nir_build_deref_array_imm(b, nir_build_deref_var(b, pos_in), i);
127 nir_deref_instr *layer_in_deref =
128 nir_build_deref_array_imm(b, nir_build_deref_var(b, layer_in), i);
129
130 nir_store_var(b, pos_out, nir_load_deref(b, pos_in_deref), 0xf);
131 nir_store_var(b, layer_out, nir_load_deref(b, layer_in_deref), 1);
132 nir_emit_vertex(b);
133 }
134
135 nir_end_primitive(b);
136
137 struct shader_info *info = &build.shader->info;
138 info->gs.input_primitive = MESA_PRIM_TRIANGLES;
139 info->gs.output_primitive = MESA_PRIM_TRIANGLE_STRIP;
140 info->gs.vertices_in = 3;
141 info->gs.vertices_out = 3;
142 info->gs.invocations = 1;
143 info->gs.active_stream_mask = 1;
144
145 return b->shader;
146 }
147
148 struct vertex {
149 float x, y, z;
150 uint32_t layer;
151 };
152
153 static void
setup_viewport_scissor(struct vk_command_buffer * cmd,uint32_t rect_count,const struct vk_meta_rect * rects,float * x_scale,float * y_scale)154 setup_viewport_scissor(struct vk_command_buffer *cmd,
155 uint32_t rect_count,
156 const struct vk_meta_rect *rects,
157 float *x_scale, float *y_scale)
158 {
159 const struct vk_device_dispatch_table *disp =
160 &cmd->base.device->dispatch_table;
161 VkCommandBuffer _cmd = vk_command_buffer_to_handle(cmd);
162
163 assert(rects[0].x0 < rects[0].x1 && rects[0].y0 < rects[0].y1);
164 uint32_t xbits = rects[0].x1 - 1, ybits = rects[0].y1 - 1;
165 float zmin = rects[0].z, zmax = rects[0].z;
166 for (uint32_t r = 1; r < rect_count; r++) {
167 assert(rects[r].x0 < rects[r].x1 && rects[r].y0 < rects[r].y1);
168 xbits |= rects[r].x1 - 1;
169 ybits |= rects[r].y1 - 1;
170 zmin = fminf(zmin, rects[r].z);
171 zmax = fminf(zmax, rects[r].z);
172 }
173
174 /* Annoyingly, we don't actually know the render area. We assume that all
175 * our rects are inside the render area. We further assume the maximum
176 * image and/or viewport size is a power of two. This means we can round
177 * up to a power of two without going outside any maximums. Using a power
178 * of two will ensure we don't lose precision when scaling coordinates.
179 */
180 int xmax_log2 = 1 + util_logbase2(xbits);
181 int ymax_log2 = 1 + util_logbase2(ybits);
182
183 assert(xmax_log2 >= 0 && xmax_log2 <= 31);
184 assert(ymax_log2 >= 0 && ymax_log2 <= 31);
185
186 /* We don't care about precise bounds on Z, only that it's inside [0, 1] if
187 * the implementaiton only supports [0, 1].
188 */
189 if (zmin >= 0.0f && zmax <= 1.0f) {
190 zmin = 0.0f;
191 zmax = 1.0f;
192 }
193
194 VkViewport viewport = {
195 .x = 0,
196 .y = 0,
197 .width = ldexpf(1.0, xmax_log2),
198 .height = ldexpf(1.0, ymax_log2),
199 .minDepth = zmin,
200 .maxDepth = zmax,
201 };
202 disp->CmdSetViewport(_cmd, 0, 1, &viewport);
203
204 VkRect2D scissor = {
205 .offset = { 0, 0 },
206 .extent = { 1u << xmax_log2, 1u << ymax_log2 },
207 };
208 disp->CmdSetScissor(_cmd, 0, 1, &scissor);
209
210 /* Scaling factors */
211 *x_scale = ldexpf(2.0, -xmax_log2);
212 *y_scale = ldexpf(2.0, -ymax_log2);
213 }
214
215 static const uint32_t rect_vb_size_B = 6 * 4 * sizeof(float);
216
217 static VkResult
create_vertex_buffer(struct vk_command_buffer * cmd,struct vk_meta_device * meta,float x_scale,float y_scale,uint32_t rect_count,const struct vk_meta_rect * rects,VkBuffer * buffer_out)218 create_vertex_buffer(struct vk_command_buffer *cmd,
219 struct vk_meta_device *meta,
220 float x_scale, float y_scale,
221 uint32_t rect_count,
222 const struct vk_meta_rect *rects,
223 VkBuffer *buffer_out)
224 {
225 VkResult result;
226
227 const VkBufferCreateInfo vtx_buffer_info = {
228 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
229 .size = rect_count * rect_vb_size_B,
230 .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
231 .queueFamilyIndexCount = 1,
232 .pQueueFamilyIndices = &cmd->pool->queue_family_index,
233 };
234
235 result = vk_meta_create_buffer(cmd, meta, &vtx_buffer_info, buffer_out);
236 if (unlikely(result != VK_SUCCESS))
237 return result;
238
239 void *map;
240 result = meta->cmd_bind_map_buffer(cmd, meta, *buffer_out, &map);
241 if (unlikely(result != VK_SUCCESS))
242 return result;
243
244 for (uint32_t r = 0; r < rect_count; r++) {
245 float x0 = rects[r].x0 * x_scale - 1.0f;
246 float y0 = rects[r].y0 * y_scale - 1.0f;
247 float x1 = rects[r].x1 * x_scale - 1.0f;
248 float y1 = rects[r].y1 * y_scale - 1.0f;
249 float z = rects[r].z;
250 uint32_t w = rects[r].layer;
251
252 struct vertex rect_vb_data[6] = {
253 { x0, y1, z, w },
254 { x0, y0, z, w },
255 { x1, y1, z, w },
256
257 { x1, y0, z, w },
258 { x1, y1, z, w },
259 { x0, y0, z, w },
260 };
261 assert(sizeof(rect_vb_data) == rect_vb_size_B);
262 memcpy((char *)map + r * rect_vb_size_B, rect_vb_data, rect_vb_size_B);
263 }
264
265 return VK_SUCCESS;
266 }
267
268 void
vk_meta_draw_volume(struct vk_command_buffer * cmd,struct vk_meta_device * meta,const struct vk_meta_rect * rect,uint32_t layer_count)269 vk_meta_draw_volume(struct vk_command_buffer *cmd,
270 struct vk_meta_device *meta,
271 const struct vk_meta_rect *rect,
272 uint32_t layer_count)
273 {
274 const struct vk_device_dispatch_table *disp =
275 &cmd->base.device->dispatch_table;
276 VkCommandBuffer _cmd = vk_command_buffer_to_handle(cmd);
277
278 float x_scale, y_scale;
279 setup_viewport_scissor(cmd, 1, rect, &x_scale, &y_scale);
280
281 VkBuffer vtx_buffer;
282 VkResult result = create_vertex_buffer(cmd, meta, x_scale, y_scale,
283 1, rect, &vtx_buffer);
284 if (unlikely(result != VK_SUCCESS)) {
285 /* TODO: Report error */
286 return;
287 }
288
289 const VkDeviceSize zero = 0;
290 disp->CmdBindVertexBuffers(_cmd, 0, 1, &vtx_buffer, &zero);
291
292 disp->CmdDraw(_cmd, 6, layer_count, 0, 0);
293 }
294
295 void
vk_meta_draw_rects(struct vk_command_buffer * cmd,struct vk_meta_device * meta,uint32_t rect_count,const struct vk_meta_rect * rects)296 vk_meta_draw_rects(struct vk_command_buffer *cmd,
297 struct vk_meta_device *meta,
298 uint32_t rect_count,
299 const struct vk_meta_rect *rects)
300 {
301 const struct vk_device_dispatch_table *disp =
302 &cmd->base.device->dispatch_table;
303 VkCommandBuffer _cmd = vk_command_buffer_to_handle(cmd);
304
305 /* Two triangles with VK_FORMAT_R16G16_UINT */
306 const uint32_t rect_vb_size_B = 6 * 3 * sizeof(float);
307 const uint32_t rects_per_draw =
308 meta->max_bind_map_buffer_size_B / rect_vb_size_B;
309
310 if (rect_count == 0)
311 return;
312
313 float x_scale, y_scale;
314 setup_viewport_scissor(cmd, rect_count, rects, &x_scale, &y_scale);
315
316 uint32_t next_rect = 0;
317 while (next_rect < rect_count) {
318 const uint32_t count = MIN2(rects_per_draw, rect_count - next_rect);
319
320 VkBuffer vtx_buffer;
321 VkResult result = create_vertex_buffer(cmd, meta, x_scale, y_scale,
322 count, &rects[next_rect],
323 &vtx_buffer);
324 if (unlikely(result != VK_SUCCESS)) {
325 /* TODO: Report error */
326 return;
327 }
328
329 const VkDeviceSize zero = 0;
330 disp->CmdBindVertexBuffers(_cmd, 0, 1, &vtx_buffer, &zero);
331
332 disp->CmdDraw(_cmd, 6 * count, 1, 0, 0);
333
334 next_rect += count;
335 }
336 assert(next_rect == rect_count);
337 }
338