v3dv: implement shader draw fallback for vkCmdBlitImage

For now this is limited to blits of 2D color images.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6766>
This commit is contained in:
Iago Toral Quiroga
2020-04-21 14:09:23 +02:00
committed by Marge Bot
parent 5fc55e3a04
commit 831aa5d438
3 changed files with 881 additions and 15 deletions
+64 -7
View File
@@ -1053,26 +1053,60 @@ init_device_dispatch(struct v3dv_device *device)
}
static uint32_t
meta_color_clear_cache_hash(const void *key)
u64_hash(const void *key)
{
return _mesa_hash_data(key, sizeof(uint64_t));
}
static bool
meta_color_clear_cache_compare(const void *key1, const void *key2)
u64_compare(const void *key1, const void *key2)
{
return memcmp(key1, key2, sizeof(uint64_t)) == 0;
}
static void
init_meta_color_clear_resources(struct v3dv_device *device)
{
device->meta.color_clear.cache =
_mesa_hash_table_create(NULL, u64_hash, u64_compare);
}
static void
create_meta_blit_descriptor_pool(struct v3dv_device *device)
{
VkDescriptorPoolSize pool_size = {
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 256,
};
VkDescriptorPoolCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.maxSets = 256,
.poolSizeCount = 1,
.pPoolSizes = &pool_size,
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
};
v3dv_CreateDescriptorPool(v3dv_device_to_handle(device),
&info, &device->alloc,
&device->meta.blit.dspool);
}
static void
init_meta_blit_resources(struct v3dv_device *device)
{
device->meta.blit.cache =
_mesa_hash_table_create(NULL, u64_hash, u64_compare);
create_meta_blit_descriptor_pool(device);
}
static void
init_device_meta(struct v3dv_device *device)
{
mtx_init(&device->meta.mtx, mtx_plain);
device->meta.color_clear.cache =
_mesa_hash_table_create(NULL,
meta_color_clear_cache_hash,
meta_color_clear_cache_compare);
init_meta_color_clear_resources(device);
init_meta_blit_resources(device);
}
static void
@@ -1094,6 +1128,29 @@ destroy_device_meta(struct v3dv_device *device)
v3dv_DestroyPipelineLayout(_device, device->meta.color_clear.playout,
&device->alloc);
}
hash_table_foreach(device->meta.blit.cache, entry) {
struct v3dv_meta_blit_pipeline *item = entry->data;
v3dv_DestroyPipeline(_device, item->pipeline, &device->alloc);
v3dv_DestroyRenderPass(_device, item->pass, &device->alloc);
vk_free(&device->alloc, item);
}
_mesa_hash_table_destroy(device->meta.blit.cache, NULL);
if (device->meta.blit.playout) {
v3dv_DestroyPipelineLayout(_device, device->meta.blit.playout,
&device->alloc);
}
if (device->meta.blit.dslayout) {
v3dv_DestroyDescriptorSetLayout(_device, device->meta.blit.dslayout,
&device->alloc);
}
if (device->meta.blit.dspool) {
v3dv_DestroyDescriptorPool(_device, device->meta.blit.dspool,
&device->alloc);
}
}
VkResult
+806 -8
View File
@@ -23,6 +23,7 @@
#include "v3dv_private.h"
#include "compiler/nir/nir_builder.h"
#include "broadcom/cle/v3dx_pack.h"
#include "vk_format_info.h"
#include "util/u_pack_color.h"
@@ -1716,10 +1717,6 @@ emit_tfu_job(struct v3dv_cmd_buffer *cmd_buffer,
uint32_t width,
uint32_t height)
{
/* Blit jobs can only happen outside a render pass */
assert(cmd_buffer->state.pass == NULL);
assert(cmd_buffer->state.job == NULL);
const struct v3d_resource_slice *src_slice = &src->slices[src_mip_level];
const struct v3d_resource_slice *dst_slice = &dst->slices[src_mip_level];
@@ -1877,6 +1874,800 @@ blit_tfu(struct v3dv_cmd_buffer *cmd_buffer,
return true;
}
static inline uint64_t
get_blit_pipeline_cache_key(VkFormat dst_format)
{
uint64_t key = 0;
uint32_t bit_offset = 0;
key |= dst_format;
bit_offset += 32;
return key;
}
static bool
create_blit_pipeline_layout(struct v3dv_device *device,
VkDescriptorSetLayout *descriptor_set_layout,
VkPipelineLayout *pipeline_layout)
{
VkResult result;
if (*descriptor_set_layout == 0) {
VkDescriptorSetLayoutBinding descriptor_set_layout_binding = {
.binding = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
};
VkDescriptorSetLayoutCreateInfo descriptor_set_layout_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &descriptor_set_layout_binding,
};
result =
v3dv_CreateDescriptorSetLayout(v3dv_device_to_handle(device),
&descriptor_set_layout_info,
&device->alloc,
descriptor_set_layout);
if (result != VK_SUCCESS)
return false;
}
assert(*pipeline_layout == 0);
VkPipelineLayoutCreateInfo pipeline_layout_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1,
.pSetLayouts = descriptor_set_layout,
.pushConstantRangeCount = 1,
.pPushConstantRanges =
&(VkPushConstantRange) { VK_SHADER_STAGE_VERTEX_BIT, 0, 16 },
};
result =
v3dv_CreatePipelineLayout(v3dv_device_to_handle(device),
&pipeline_layout_info,
&device->alloc,
pipeline_layout);
return result == VK_SUCCESS;
}
static bool
create_blit_render_pass(struct v3dv_device *device,
VkFormat format,
VkRenderPass *pass)
{
/* FIXME: if blitting to tile boundaries or to the whole image, we could
* use LOAD_DONT_CARE, but then we would have to include that in the
* pipeline hash key. Or maybe we should just create both render passes and
* use one or the other at draw time since they would both be compatible
* with the pipeline anyway
*/
VkAttachmentDescription att = {
.format = format,
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
.initialLayout = VK_IMAGE_LAYOUT_GENERAL,
.finalLayout = VK_IMAGE_LAYOUT_GENERAL,
};
VkAttachmentReference att_ref = {
.attachment = 0,
.layout = VK_IMAGE_LAYOUT_GENERAL,
};
VkSubpassDescription subpass = {
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
.inputAttachmentCount = 0,
.colorAttachmentCount = 1,
.pColorAttachments = &att_ref,
.pResolveAttachments = NULL,
.pDepthStencilAttachment = NULL,
.preserveAttachmentCount = 0,
.pPreserveAttachments = NULL,
};
VkRenderPassCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &att,
.subpassCount = 1,
.pSubpasses = &subpass,
.dependencyCount = 0,
.pDependencies = NULL,
};
VkResult result = v3dv_CreateRenderPass(v3dv_device_to_handle(device),
&info, &device->alloc, pass);
return result == VK_SUCCESS;
}
static nir_ssa_def *
gen_rect_vertices(nir_builder *b)
{
nir_intrinsic_instr *vertex_id =
nir_intrinsic_instr_create(b->shader,
nir_intrinsic_load_vertex_id);
nir_ssa_dest_init(&vertex_id->instr, &vertex_id->dest, 1, 32, "vertexid");
nir_builder_instr_insert(b, &vertex_id->instr);
/* vertex 0: -1.0, -1.0
* vertex 1: -1.0, 1.0
* vertex 2: 1.0, -1.0
* vertex 3: 1.0, 1.0
*
* so:
*
* channel 0 is vertex_id < 2 ? -1.0 : 1.0
* channel 1 is vertex id & 1 ? 1.0 : -1.0
*/
nir_ssa_def *one = nir_imm_int(b, 1);
nir_ssa_def *c0cmp = nir_ilt(b, &vertex_id->dest.ssa, nir_imm_int(b, 2));
nir_ssa_def *c1cmp = nir_ieq(b, nir_iand(b, &vertex_id->dest.ssa, one), one);
nir_ssa_def *comp[4];
comp[0] = nir_bcsel(b, c0cmp,
nir_imm_float(b, -1.0f),
nir_imm_float(b, 1.0f));
comp[1] = nir_bcsel(b, c1cmp,
nir_imm_float(b, 1.0f),
nir_imm_float(b, -1.0f));
comp[2] = nir_imm_float(b, 0.0f);
comp[3] = nir_imm_float(b, 1.0f);
return nir_vec(b, comp, 4);
}
static nir_ssa_def *
gen_tex_coords(nir_builder *b)
{
nir_intrinsic_instr *tex_box =
nir_intrinsic_instr_create(b->shader, nir_intrinsic_load_push_constant);
tex_box->src[0] = nir_src_for_ssa(nir_imm_int(b, 0));
nir_intrinsic_set_base(tex_box, 0);
nir_intrinsic_set_range(tex_box, 16);
tex_box->num_components = 4;
nir_ssa_dest_init(&tex_box->instr, &tex_box->dest, 4, 32, "tex_box");
nir_builder_instr_insert(b, &tex_box->instr);
nir_intrinsic_instr *vertex_id =
nir_intrinsic_instr_create(b->shader, nir_intrinsic_load_vertex_id);
nir_ssa_dest_init(&vertex_id->instr, &vertex_id->dest, 1, 32, "vertexid");
nir_builder_instr_insert(b, &vertex_id->instr);
/* vertex 0: src0_x, src0_y
* vertex 1: src0_x, src1_y
* vertex 2: src1_x, src0_y
* vertex 3: src1_x, src1_y
*
* So:
*
* channel 0 is vertex_id < 2 ? src0_x : src1_x
* channel 1 is vertex id & 1 ? src1_y : src0_y
*/
nir_ssa_def *one = nir_imm_int(b, 1);
nir_ssa_def *c0cmp = nir_ilt(b, &vertex_id->dest.ssa, nir_imm_int(b, 2));
nir_ssa_def *c1cmp = nir_ieq(b, nir_iand(b, &vertex_id->dest.ssa, one), one);
nir_ssa_def *comp[4];
comp[0] = nir_bcsel(b, c0cmp,
nir_channel(b, &tex_box->dest.ssa, 0),
nir_channel(b, &tex_box->dest.ssa, 2));
comp[1] = nir_bcsel(b, c1cmp,
nir_channel(b, &tex_box->dest.ssa, 3),
nir_channel(b, &tex_box->dest.ssa, 1));
comp[2] = nir_imm_float(b, 0.0f);
comp[3] = nir_imm_float(b, 1.0f);
return nir_vec(b, comp, 4);
}
static nir_ssa_def *
build_nir_tex_op(struct nir_builder *b,
struct v3dv_device *device,
nir_ssa_def *tex_pos,
enum glsl_base_type tex_type)
{
const enum glsl_sampler_dim dim = GLSL_SAMPLER_DIM_2D;
const struct glsl_type *sampler_type =
glsl_sampler_type(dim, false, false, tex_type);
nir_variable *sampler =
nir_variable_create(b->shader, nir_var_uniform, sampler_type, "s_tex");
sampler->data.descriptor_set = 0;
sampler->data.binding = 0;
nir_ssa_def *tex_deref = &nir_build_deref_var(b, sampler)->dest.ssa;
nir_tex_instr *tex = nir_tex_instr_create(b->shader, 3);
tex->sampler_dim = dim;
tex->op = nir_texop_tex;
tex->src[0].src_type = nir_tex_src_coord;
tex->src[0].src = nir_src_for_ssa(tex_pos);
tex->src[1].src_type = nir_tex_src_texture_deref;
tex->src[1].src = nir_src_for_ssa(tex_deref);
tex->src[2].src_type = nir_tex_src_sampler_deref;
tex->src[2].src = nir_src_for_ssa(tex_deref);
tex->dest_type =
nir_alu_type_get_base_type(nir_get_nir_type_for_glsl_base_type(tex_type));
tex->is_array = glsl_sampler_type_is_array(sampler_type);
tex->coord_components = tex_pos->num_components;
nir_ssa_dest_init(&tex->instr, &tex->dest, 4, 32, "tex");
nir_builder_instr_insert(b, &tex->instr);
return &tex->dest.ssa;
}
static nir_shader *
get_blit_vs()
{
nir_builder b;
const nir_shader_compiler_options *options = v3dv_pipeline_get_nir_options();
nir_builder_init_simple_shader(&b, NULL, MESA_SHADER_VERTEX, options);
b.shader->info.name = ralloc_strdup(b.shader, "meta blit vs");
const struct glsl_type *vec4 = glsl_vec4_type();
nir_variable *vs_out_pos =
nir_variable_create(b.shader, nir_var_shader_out, vec4, "gl_Position");
vs_out_pos->data.location = VARYING_SLOT_POS;
nir_variable *vs_out_tex_coord =
nir_variable_create(b.shader, nir_var_shader_out, vec4, "out_tex_coord");
vs_out_tex_coord->data.location = VARYING_SLOT_VAR0;
vs_out_tex_coord->data.interpolation = INTERP_MODE_SMOOTH;
nir_ssa_def *pos = gen_rect_vertices(&b);
nir_store_var(&b, vs_out_pos, pos, 0xf);
nir_ssa_def *tex_coord = gen_tex_coords(&b);
nir_store_var(&b, vs_out_tex_coord, tex_coord, 0xf);
return b.shader;
}
static nir_shader *
get_blit_fs(struct v3dv_device *device,
struct v3dv_render_pass *pass)
{
nir_builder b;
const nir_shader_compiler_options *options = v3dv_pipeline_get_nir_options();
nir_builder_init_simple_shader(&b, NULL, MESA_SHADER_FRAGMENT, options);
b.shader->info.name = ralloc_strdup(b.shader, "meta blit fs");
const struct glsl_type *vec4 = glsl_vec4_type();
nir_variable *fs_in_tex_coord =
nir_variable_create(b.shader, nir_var_shader_in, vec4, "in_tex_coord");
fs_in_tex_coord->data.location = VARYING_SLOT_VAR0;
assert(pass->attachment_count == 1);
VkFormat rt_format = pass->attachments[0].desc.format;
const struct glsl_type *fs_out_type =
vk_format_is_int(rt_format) ? glsl_uvec4_type() : glsl_vec4_type();
nir_variable *fs_out_color =
nir_variable_create(b.shader, nir_var_shader_out, fs_out_type, "out_color");
fs_out_color->data.location = FRAG_RESULT_DATA0;
nir_ssa_def *tex_coord = nir_load_var(&b, fs_in_tex_coord);
nir_ssa_def *tex_coord_xy = nir_channels(&b, tex_coord, 0x3);
nir_ssa_def *color = build_nir_tex_op(&b, device, tex_coord_xy,
glsl_get_base_type(fs_out_type));
nir_store_var(&b, fs_out_color, color, 0xf);
return b.shader;
}
static bool
create_pipeline(struct v3dv_device *device,
struct v3dv_render_pass *pass,
struct nir_shader *vs_nir,
struct nir_shader *fs_nir,
const VkPipelineVertexInputStateCreateInfo *vi_state,
const VkPipelineDepthStencilStateCreateInfo *ds_state,
const VkPipelineColorBlendStateCreateInfo *cb_state,
const VkPipelineLayout layout,
VkPipeline *pipeline)
{
struct v3dv_shader_module vs_m = { .nir = vs_nir };
struct v3dv_shader_module fs_m = { .nir = fs_nir };
VkPipelineShaderStageCreateInfo stages[2] = {
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = v3dv_shader_module_to_handle(&vs_m),
.pName = "main",
},
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = v3dv_shader_module_to_handle(&fs_m),
.pName = "main",
},
};
VkGraphicsPipelineCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.stageCount = 2,
.pStages = stages,
.pVertexInputState = vi_state,
.pInputAssemblyState = &(VkPipelineInputAssemblyStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
.primitiveRestartEnable = false,
},
.pViewportState = &(VkPipelineViewportStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1,
},
.pRasterizationState = &(VkPipelineRasterizationStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.rasterizerDiscardEnable = false,
.polygonMode = VK_POLYGON_MODE_FILL,
.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.depthBiasEnable = false,
},
.pMultisampleState = &(VkPipelineMultisampleStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
.sampleShadingEnable = false,
.pSampleMask = NULL,
.alphaToCoverageEnable = false,
.alphaToOneEnable = false,
},
.pDepthStencilState = ds_state,
.pColorBlendState = cb_state,
/* The meta clear pipeline declares all state as dynamic.
* As a consequence, vkCmdBindPipeline writes no dynamic state
* to the cmd buffer. Therefore, at the end of the meta clear,
* we need only restore dynamic state that was vkCmdSet.
*
* FIXME: Update this when we support more dynamic states (adding
* them now will assert because they are not supported).
*/
.pDynamicState = &(VkPipelineDynamicStateCreateInfo) {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = 6,
.pDynamicStates = (VkDynamicState[]) {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK,
VK_DYNAMIC_STATE_STENCIL_REFERENCE,
VK_DYNAMIC_STATE_BLEND_CONSTANTS,
#if 0
VK_DYNAMIC_STATE_LINE_WIDTH,
VK_DYNAMIC_STATE_DEPTH_BIAS,
VK_DYNAMIC_STATE_DEPTH_BOUNDS,
#endif
},
},
.flags = 0,
.layout = layout,
.renderPass = v3dv_render_pass_to_handle(pass),
.subpass = 0,
};
VkResult result =
v3dv_CreateGraphicsPipelines(v3dv_device_to_handle(device),
VK_NULL_HANDLE,
1, &info,
&device->alloc,
pipeline);
ralloc_free(vs_nir);
ralloc_free(fs_nir);
return result == VK_SUCCESS;
}
static bool
create_blit_pipeline(struct v3dv_device *device,
VkRenderPass _pass,
VkPipelineLayout pipeline_layout,
VkPipeline *pipeline)
{
struct v3dv_render_pass *pass = v3dv_render_pass_from_handle(_pass);
nir_shader *vs_nir = get_blit_vs();
nir_shader *fs_nir = get_blit_fs(device, pass);
const VkPipelineVertexInputStateCreateInfo vi_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 0,
.vertexAttributeDescriptionCount = 0,
};
const VkPipelineDepthStencilStateCreateInfo ds_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = false,
.depthWriteEnable = false,
.depthBoundsTestEnable = false,
.stencilTestEnable = false,
};
VkPipelineColorBlendAttachmentState blend_att_state[1] = { 0 };
blend_att_state[0] = (VkPipelineColorBlendAttachmentState) {
.blendEnable = false,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT,
};
const VkPipelineColorBlendStateCreateInfo cb_state = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = false,
.attachmentCount = 1,
.pAttachments = blend_att_state
};
return create_pipeline(device,
pass,
vs_nir, fs_nir,
&vi_state,
&ds_state,
&cb_state,
pipeline_layout,
pipeline);
}
static bool
get_blit_pipeline(struct v3dv_device *device,
VkFormat dst_format,
struct v3dv_meta_blit_pipeline **pipeline)
{
bool ok = true;
mtx_lock(&device->meta.mtx);
if (!device->meta.blit.playout) {
ok = create_blit_pipeline_layout(device,
&device->meta.blit.dslayout,
&device->meta.blit.playout);
}
mtx_unlock(&device->meta.mtx);
if (!ok)
return false;
const uint64_t key = get_blit_pipeline_cache_key(dst_format);
mtx_lock(&device->meta.mtx);
struct hash_entry *entry =
_mesa_hash_table_search(device->meta.blit.cache, &key);
if (entry) {
mtx_unlock(&device->meta.mtx);
*pipeline = entry->data;
return true;
}
*pipeline = vk_zalloc2(&device->alloc, NULL, sizeof(**pipeline), 8,
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
if (*pipeline == NULL)
goto fail;
ok = create_blit_render_pass(device, dst_format, &(*pipeline)->pass);
if (!ok)
goto fail;
ok = create_blit_pipeline(device,
(*pipeline)->pass,
device->meta.blit.playout,
&(*pipeline)->pipeline);
if (!ok)
goto fail;
_mesa_hash_table_insert(device->meta.blit.cache, &key, *pipeline);
mtx_unlock(&device->meta.mtx);
return true;
fail:
mtx_unlock(&device->meta.mtx);
VkDevice _device = v3dv_device_to_handle(device);
if (*pipeline) {
if ((*pipeline)->pass)
v3dv_DestroyRenderPass(_device, (*pipeline)->pass, &device->alloc);
if ((*pipeline)->pipeline)
v3dv_DestroyPipeline(_device, (*pipeline)->pipeline, &device->alloc);
vk_free(&device->alloc, *pipeline);
*pipeline = NULL;
}
return false;
}
static void
compute_blit_box(const VkOffset3D *offsets,
struct v3dv_image *image,
uint32_t *x, uint32_t *y, uint32_t *w, uint32_t *h,
bool *mirror_x, bool *mirror_y)
{
if (offsets[1].x >= offsets[0].x) {
*mirror_x = false;
*x = MIN2(offsets[0].x, image->extent.width - 1);
*w = MIN2(offsets[1].x - offsets[0].x,
image->extent.width - offsets[0].x);
} else {
*mirror_x = true;
*x = MIN2(offsets[1].x, image->extent.width - 1);
*w = MIN2(offsets[0].x - offsets[1].x,
image->extent.width - offsets[1].x);
}
if (offsets[1].y >= offsets[0].y) {
*mirror_y = false;
*y = MIN2(offsets[0].y, image->extent.height - 1);
*h = MIN2(offsets[1].y - offsets[0].y,
image->extent.height - offsets[0].y);
} else {
*mirror_y = true;
*y = MIN2(offsets[1].y, image->extent.height - 1);
*h = MIN2(offsets[0].y - offsets[1].y,
image->extent.height - offsets[1].y);
}
}
static bool
blit_shader(struct v3dv_cmd_buffer *cmd_buffer,
struct v3dv_image *dst,
struct v3dv_image *src,
const VkImageBlit *region,
VkFilter filter)
{
/* FIXME: we only support 2D color blits for now */
if (region->dstSubresource.aspectMask != VK_IMAGE_ASPECT_COLOR_BIT)
return false;
if (dst->type != VK_IMAGE_TYPE_2D || src->type != VK_IMAGE_TYPE_2D)
return false;
uint32_t dst_x, dst_y, dst_w, dst_h;
bool dst_mirror_x, dst_mirror_y;
compute_blit_box(region->dstOffsets, dst,
&dst_x, &dst_y, &dst_w, &dst_h,
&dst_mirror_x, &dst_mirror_y);
uint32_t src_x, src_y, src_w, src_h;
bool src_mirror_x, src_mirror_y;
compute_blit_box(region->srcOffsets, src,
&src_x, &src_y, &src_w, &src_h,
&src_mirror_x, &src_mirror_y);
/* Translate source blit coordinates to normalized texture coordinates
* and handle mirroring.
*/
const float coords[4] = {
(float)src_x / (float)src->extent.width,
(float)src_y / (float)src->extent.height,
(float)(src_x + src_w) / (float)src->extent.width,
(float)(src_y + src_h) / (float)src->extent.height
};
const bool mirror_x = dst_mirror_x != src_mirror_x;
const bool mirror_y = dst_mirror_y != src_mirror_y;
const float tex_coords[4] = {
!mirror_x ? coords[0] : coords[2],
!mirror_y ? coords[1] : coords[3],
!mirror_x ? coords[2] : coords[0],
!mirror_y ? coords[3] : coords[1],
};
/* Get the blit pipeline */
struct v3dv_meta_blit_pipeline *pipeline = NULL;
bool ok =
get_blit_pipeline(cmd_buffer->device, dst->vk_format, &pipeline);
if (!ok)
return false;
assert(pipeline && pipeline->pipeline && pipeline->pass);
struct v3dv_device *device = cmd_buffer->device;
assert(device->meta.blit.dspool);
assert(device->meta.blit.dslayout);
/* Push command buffer state before starting meta operation */
v3dv_cmd_buffer_meta_state_push(cmd_buffer, true);
/* Setup framebuffer */
VkDevice _device = v3dv_device_to_handle(device);
VkCommandBuffer _cmd_buffer = v3dv_cmd_buffer_to_handle(cmd_buffer);
VkResult result;
uint32_t dirty_dynamic_state = 0;
for (uint32_t i = 0; i < region->dstSubresource.layerCount; i++) {
VkImageViewCreateInfo dst_image_view_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = v3dv_image_to_handle(dst),
.viewType = VK_IMAGE_VIEW_TYPE_2D, /* FIXME */
.format = dst->vk_format,
.subresourceRange = {
.aspectMask = dst->aspects,
.baseMipLevel = region->dstSubresource.mipLevel,
.levelCount = 1,
.baseArrayLayer = region->dstSubresource.baseArrayLayer + i,
.layerCount = 1
},
};
VkImageView dst_image_view;
result = v3dv_CreateImageView(_device, &dst_image_view_info,
&device->alloc, &dst_image_view);
if (result != VK_SUCCESS) {
ok = false;
goto fail_dst_image_view;
}
VkFramebufferCreateInfo fb_info = {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = pipeline->pass,
.attachmentCount = 1,
.pAttachments = &dst_image_view,
.width = dst->extent.width,
.height = dst->extent.height,
.layers = 1,
};
VkFramebuffer fb;
result = v3dv_CreateFramebuffer(_device, &fb_info,
&cmd_buffer->device->alloc, &fb);
if (result != VK_SUCCESS) {
ok = false;
goto fail_framebuffer;
}
/* Setup descriptor set for blit source texture */
VkDescriptorSet set;
VkDescriptorSetAllocateInfo set_alloc_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = device->meta.blit.dspool,
.descriptorSetCount = 1,
.pSetLayouts = &device->meta.blit.dslayout,
};
result = v3dv_AllocateDescriptorSets(_device, &set_alloc_info, &set);
if (result != VK_SUCCESS) {
ok = false;
goto fail_descriptor_set;
}
VkSamplerCreateInfo sampler_info = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = filter,
.minFilter = filter,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
};
VkSampler sampler;
result = v3dv_CreateSampler(_device, &sampler_info, &device->alloc,
&sampler);
if (result != VK_SUCCESS) {
ok = false;
goto fail_sampler;
}
VkImageViewCreateInfo src_image_view_info = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = v3dv_image_to_handle(src),
.viewType = VK_IMAGE_VIEW_TYPE_2D, /* FIXME */
.format = src->vk_format,
.subresourceRange = {
.aspectMask = src->aspects,
.baseMipLevel = region->srcSubresource.mipLevel,
.levelCount = 1,
.baseArrayLayer = region->srcSubresource.baseArrayLayer + i,
.layerCount = 1
},
};
VkImageView src_image_view;
result = v3dv_CreateImageView(_device, &src_image_view_info,
&device->alloc, &src_image_view);
if (result != VK_SUCCESS) {
ok = false;
goto fail_src_image_view;
}
VkDescriptorImageInfo image_info = {
.sampler = sampler,
.imageView = src_image_view,
.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
};
VkWriteDescriptorSet write = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = set,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &image_info,
};
v3dv_UpdateDescriptorSets(_device, 1, &write, 0, NULL);
/* Record blit */
VkRenderPassBeginInfo rp_info = {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = pipeline->pass,
.framebuffer = fb,
.renderArea = {
.offset = { dst_x, dst_y },
.extent = { dst_w, dst_h }
},
.clearValueCount = 0,
};
v3dv_CmdBeginRenderPass(_cmd_buffer, &rp_info, VK_SUBPASS_CONTENTS_INLINE);
struct v3dv_job *job = cmd_buffer->state.job;
if (!job) {
ok = false;
goto fail_job;
}
v3dv_CmdPushConstants(_cmd_buffer,
device->meta.blit.playout,
VK_SHADER_STAGE_VERTEX_BIT, 0, 16,
&tex_coords);
v3dv_CmdBindPipeline(_cmd_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline->pipeline);
v3dv_CmdBindDescriptorSets(_cmd_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
device->meta.blit.playout,
0, 1, &set,
0, NULL);
const VkViewport viewport = {
.x = dst_x,
.y = dst_y,
.width = dst_w,
.height = dst_h,
.minDepth = 0.0f,
.maxDepth = 1.0f
};
v3dv_CmdSetViewport(_cmd_buffer, 0, 1, &viewport);
const VkRect2D scissor = {
.offset = { dst_x, dst_y },
.extent = { dst_w, dst_h }
};
v3dv_CmdSetScissor(_cmd_buffer, 0, 1, &scissor);
v3dv_CmdDraw(_cmd_buffer, 4, 1, 0, 0);
v3dv_CmdEndRenderPass(_cmd_buffer);
dirty_dynamic_state = V3DV_CMD_DIRTY_VIEWPORT | V3DV_CMD_DIRTY_SCISSOR;
fail_job:
v3dv_DestroySampler(_device, sampler, &cmd_buffer->device->alloc);
fail_src_image_view:
v3dv_DestroyImageView(_device, src_image_view, &cmd_buffer->device->alloc);
fail_sampler:
v3dv_FreeDescriptorSets(_device, device->meta.blit.dspool, 1, &set);
fail_descriptor_set:
v3dv_DestroyFramebuffer(_device, fb, &cmd_buffer->device->alloc);
fail_framebuffer:
v3dv_DestroyImageView(_device, dst_image_view, &cmd_buffer->device->alloc);
}
fail_dst_image_view:
v3dv_cmd_buffer_meta_state_pop(cmd_buffer, dirty_dynamic_state);
return ok;
}
void
v3dv_CmdBlitImage(VkCommandBuffer commandBuffer,
VkImage srcImage,
@@ -1891,12 +2682,19 @@ v3dv_CmdBlitImage(VkCommandBuffer commandBuffer,
V3DV_FROM_HANDLE(v3dv_image, src, srcImage);
V3DV_FROM_HANDLE(v3dv_image, dst, dstImage);
/* This command can only happen outside a render pass */
assert(cmd_buffer->state.pass == NULL);
assert(cmd_buffer->state.job == NULL);
/* From the Vulkan 1.0 spec, vkCmdBlitImage valid usage */
assert (dst->samples == VK_SAMPLE_COUNT_1_BIT &&
src->samples == VK_SAMPLE_COUNT_1_BIT);
assert(dst->samples == VK_SAMPLE_COUNT_1_BIT &&
src->samples == VK_SAMPLE_COUNT_1_BIT);
for (uint32_t i = 0; i < regionCount; i++) {
if (!blit_tfu(cmd_buffer, dst, src, &pRegions[i], filter))
assert(!"Fallback path for vkCmdBlitImage not implemented.");
if (blit_tfu(cmd_buffer, dst, src, &pRegions[i], filter))
continue;
if (blit_shader(cmd_buffer, dst, src, &pRegions[i], filter))
continue;
assert(!"Unsupported blit operation");
}
}
+11
View File
@@ -215,6 +215,11 @@ struct v3dv_meta_color_clear_pipeline {
VkRenderPass pass;
};
struct v3dv_meta_blit_pipeline {
VkPipeline pipeline;
VkRenderPass pass;
};
struct v3dv_device {
VK_LOADER_DATA _loader_data;
@@ -242,6 +247,12 @@ struct v3dv_device {
VkPipelineLayout playout;
struct hash_table *cache; /* v3dv_meta_color_clear_pipeline */
} color_clear;
struct {
VkDescriptorPool dspool;
VkDescriptorSetLayout dslayout;
VkPipelineLayout playout;
struct hash_table *cache; /* v3dv_meta_blit_pipeline */
} blit;
} meta;
};