mesa/src/vulkan/runtime/vk_meta_draw_rects.c
Erik Faye-Lund 18db05d3e6 vulkan/runtime: implementaiton -> implementation
Acked-By: Mike Blumenkrantz <michael.blumenkrantz@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/29890>
2024-06-26 12:31:00 +00:00

337 lines
11 KiB
C

/*
* Copyright © 2022 Collabora Ltd
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "vk_meta_private.h"
#include "vk_command_buffer.h"
#include "vk_command_pool.h"
#include "vk_device.h"
#include "nir_builder.h"
const VkPipelineVertexInputStateCreateInfo vk_meta_draw_rects_vi_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &(const VkVertexInputBindingDescription) {
.binding = 0,
.stride = 4 * sizeof(uint32_t),
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
},
.vertexAttributeDescriptionCount = 1,
.pVertexAttributeDescriptions = &(const VkVertexInputAttributeDescription) {
.location = 0,
.binding = 0,
.format = VK_FORMAT_R32G32B32A32_UINT,
.offset = 0,
},
};
const VkPipelineInputAssemblyStateCreateInfo vk_meta_draw_rects_ia_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_META_RECT_LIST_MESA,
.primitiveRestartEnable = VK_FALSE,
};
const VkPipelineViewportStateCreateInfo vk_meta_draw_rects_vs_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1,
};
nir_shader *
vk_meta_draw_rects_vs_nir(struct vk_meta_device *device, bool use_gs)
{
nir_builder build = nir_builder_init_simple_shader(MESA_SHADER_VERTEX, NULL,
"vk-meta-draw-rects-vs");
nir_builder *b = &build;
nir_variable *in = nir_variable_create(b->shader, nir_var_shader_in,
glsl_uvec4_type(), "vtx_in");
in->data.location = VERT_ATTRIB_GENERIC0;
nir_variable *pos =
nir_variable_create(b->shader, nir_var_shader_out, glsl_vec4_type(),
use_gs ? "pos_out" : "gl_Position");
pos->data.location = use_gs ? VARYING_SLOT_VAR0 : VARYING_SLOT_POS;
nir_variable *layer =
nir_variable_create(b->shader, nir_var_shader_out, glsl_int_type(),
use_gs ? "layer_out" : "gl_Layer");
layer->data.location = use_gs ? VARYING_SLOT_VAR1 : VARYING_SLOT_LAYER;
nir_def *vtx = nir_load_var(b, in);
nir_store_var(b, pos, nir_vec4(b, nir_channel(b, vtx, 0),
nir_channel(b, vtx, 1),
nir_channel(b, vtx, 2),
nir_imm_float(b, 1)),
0xf);
nir_store_var(b, layer, nir_iadd(b, nir_load_instance_id(b),
nir_channel(b, vtx, 3)),
0x1);
return b->shader;
}
nir_shader *
vk_meta_draw_rects_gs_nir(struct vk_meta_device *device)
{
nir_builder build =
nir_builder_init_simple_shader(MESA_SHADER_GEOMETRY, NULL,
"vk-meta-draw-rects-gs");
nir_builder *b = &build;
nir_variable *pos_in =
nir_variable_create(b->shader, nir_var_shader_in,
glsl_array_type(glsl_vec4_type(), 3, 0), "pos_in");
pos_in->data.location = VARYING_SLOT_VAR0;
nir_variable *layer_in =
nir_variable_create(b->shader, nir_var_shader_in,
glsl_array_type(glsl_int_type(), 3, 0), "layer_in");
layer_in->data.location = VARYING_SLOT_VAR1;
nir_variable *pos_out =
nir_variable_create(b->shader, nir_var_shader_out,
glsl_vec4_type(), "gl_Position");
pos_out->data.location = VARYING_SLOT_POS;
nir_variable *layer_out =
nir_variable_create(b->shader, nir_var_shader_out,
glsl_int_type(), "gl_Layer");
layer_out->data.location = VARYING_SLOT_LAYER;
for (unsigned i = 0; i < 3; i++) {
nir_deref_instr *pos_in_deref =
nir_build_deref_array_imm(b, nir_build_deref_var(b, pos_in), i);
nir_deref_instr *layer_in_deref =
nir_build_deref_array_imm(b, nir_build_deref_var(b, layer_in), i);
nir_store_var(b, pos_out, nir_load_deref(b, pos_in_deref), 0xf);
nir_store_var(b, layer_out, nir_load_deref(b, layer_in_deref), 1);
nir_emit_vertex(b);
}
nir_end_primitive(b);
struct shader_info *info = &build.shader->info;
info->gs.input_primitive = MESA_PRIM_TRIANGLES;
info->gs.output_primitive = MESA_PRIM_TRIANGLE_STRIP;
info->gs.vertices_in = 3;
info->gs.vertices_out = 3;
info->gs.invocations = 1;
info->gs.active_stream_mask = 1;
return b->shader;
}
struct vertex {
float x, y, z;
uint32_t layer;
};
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)
{
const struct vk_device_dispatch_table *disp =
&cmd->base.device->dispatch_table;
VkCommandBuffer _cmd = vk_command_buffer_to_handle(cmd);
assert(rects[0].x0 < rects[0].x1 && rects[0].y0 < rects[0].y1);
uint32_t xbits = rects[0].x1 - 1, ybits = rects[0].y1 - 1;
float zmin = rects[0].z, zmax = rects[0].z;
for (uint32_t r = 1; r < rect_count; r++) {
assert(rects[r].x0 < rects[r].x1 && rects[r].y0 < rects[r].y1);
xbits |= rects[r].x1 - 1;
ybits |= rects[r].y1 - 1;
zmin = fminf(zmin, rects[r].z);
zmax = fminf(zmax, rects[r].z);
}
/* Annoyingly, we don't actually know the render area. We assume that all
* our rects are inside the render area. We further assume the maximum
* image and/or viewport size is a power of two. This means we can round
* up to a power of two without going outside any maximums. Using a power
* of two will ensure we don't lose precision when scaling coordinates.
*/
int xmax_log2 = 1 + util_logbase2(xbits);
int ymax_log2 = 1 + util_logbase2(ybits);
assert(xmax_log2 >= 0 && xmax_log2 <= 31);
assert(ymax_log2 >= 0 && ymax_log2 <= 31);
/* We don't care about precise bounds on Z, only that it's inside [0, 1] if
* the implementation only supports [0, 1].
*/
if (zmin >= 0.0f && zmax <= 1.0f) {
zmin = 0.0f;
zmax = 1.0f;
}
VkViewport viewport = {
.x = 0,
.y = 0,
.width = ldexpf(1.0, xmax_log2),
.height = ldexpf(1.0, ymax_log2),
.minDepth = zmin,
.maxDepth = zmax,
};
disp->CmdSetViewport(_cmd, 0, 1, &viewport);
VkRect2D scissor = {
.offset = { 0, 0 },
.extent = { 1u << xmax_log2, 1u << ymax_log2 },
};
disp->CmdSetScissor(_cmd, 0, 1, &scissor);
/* Scaling factors */
*x_scale = ldexpf(2.0, -xmax_log2);
*y_scale = ldexpf(2.0, -ymax_log2);
}
static const uint32_t rect_vb_size_B = 6 * 4 * sizeof(float);
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)
{
VkResult result;
const VkBufferCreateInfo vtx_buffer_info = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = rect_count * rect_vb_size_B,
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
.queueFamilyIndexCount = 1,
.pQueueFamilyIndices = &cmd->pool->queue_family_index,
};
result = vk_meta_create_buffer(cmd, meta, &vtx_buffer_info, buffer_out);
if (unlikely(result != VK_SUCCESS))
return result;
void *map;
result = meta->cmd_bind_map_buffer(cmd, meta, *buffer_out, &map);
if (unlikely(result != VK_SUCCESS))
return result;
for (uint32_t r = 0; r < rect_count; r++) {
float x0 = rects[r].x0 * x_scale - 1.0f;
float y0 = rects[r].y0 * y_scale - 1.0f;
float x1 = rects[r].x1 * x_scale - 1.0f;
float y1 = rects[r].y1 * y_scale - 1.0f;
float z = rects[r].z;
uint32_t w = rects[r].layer;
struct vertex rect_vb_data[6] = {
{ x0, y1, z, w },
{ x0, y0, z, w },
{ x1, y1, z, w },
{ x1, y0, z, w },
{ x1, y1, z, w },
{ x0, y0, z, w },
};
assert(sizeof(rect_vb_data) == rect_vb_size_B);
memcpy((char *)map + r * rect_vb_size_B, rect_vb_data, rect_vb_size_B);
}
return VK_SUCCESS;
}
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)
{
const struct vk_device_dispatch_table *disp =
&cmd->base.device->dispatch_table;
VkCommandBuffer _cmd = vk_command_buffer_to_handle(cmd);
float x_scale, y_scale;
setup_viewport_scissor(cmd, 1, rect, &x_scale, &y_scale);
VkBuffer vtx_buffer;
VkResult result = create_vertex_buffer(cmd, meta, x_scale, y_scale,
1, rect, &vtx_buffer);
if (unlikely(result != VK_SUCCESS)) {
/* TODO: Report error */
return;
}
const VkDeviceSize zero = 0;
disp->CmdBindVertexBuffers(_cmd, 0, 1, &vtx_buffer, &zero);
disp->CmdDraw(_cmd, 6, layer_count, 0, 0);
}
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)
{
const struct vk_device_dispatch_table *disp =
&cmd->base.device->dispatch_table;
VkCommandBuffer _cmd = vk_command_buffer_to_handle(cmd);
/* Two triangles with VK_FORMAT_R16G16_UINT */
const uint32_t rect_vb_size_B = 6 * 3 * sizeof(float);
const uint32_t rects_per_draw =
meta->max_bind_map_buffer_size_B / rect_vb_size_B;
if (rect_count == 0)
return;
float x_scale, y_scale;
setup_viewport_scissor(cmd, rect_count, rects, &x_scale, &y_scale);
uint32_t next_rect = 0;
while (next_rect < rect_count) {
const uint32_t count = MIN2(rects_per_draw, rect_count - next_rect);
VkBuffer vtx_buffer;
VkResult result = create_vertex_buffer(cmd, meta, x_scale, y_scale,
count, &rects[next_rect],
&vtx_buffer);
if (unlikely(result != VK_SUCCESS)) {
/* TODO: Report error */
return;
}
const VkDeviceSize zero = 0;
disp->CmdBindVertexBuffers(_cmd, 0, 1, &vtx_buffer, &zero);
disp->CmdDraw(_cmd, 6 * count, 1, 0, 0);
next_rect += count;
}
assert(next_rect == rect_count);
}