diff --git a/flake.nix b/flake.nix index 968b63f..e3cfcc0 100644 --- a/flake.nix +++ b/flake.nix @@ -78,7 +78,7 @@ export BINDGEN_EXTRA_CLANG_ARGS="-I${pkgs.llvmPackages_21.clang-unwrapped}/lib/clang/21/include" export C_INCLUDE_PATH=${pkgs.opencl-headers}/include:$C_INCLUDE_PATH export LIBRARY_PATH=${pkgs.ocl-icd}/lib:$LIBRARY_PATH - export LD_LIBRARY_PATH="${pkgs.vulkan-loader}/lib:$LD_LIBRARY_PATH" + export LD_LIBRARY_PATH="${pkgs.vulkan-loader}/lib:${pkgs.zlib}/lib:${pkgs.stdenv.cc.cc.lib}/lib:$LD_LIBRARY_PATH" ''; # 32-bit (i686) build için: L4D2 gibi 32-bit oyunlarla test amaçlı. diff --git a/src/compiler/nir/nir_opt_algebraic.py b/src/compiler/nir/nir_opt_algebraic.py index 259bd5b..b129b1c 100644 --- a/src/compiler/nir/nir_opt_algebraic.py +++ b/src/compiler/nir/nir_opt_algebraic.py @@ -496,11 +496,11 @@ for s in [16, 32, 64]: (('~flrp@{}'.format(s), ('fadd(is_used_once)', a, b), ('fadd(is_used_once)', a, c), d), ('fadd', ('flrp', b, c, d), a), 'options->lower_flrp{}'.format(s)), (('~flrp@{}'.format(s), a, ('fmul(is_used_once)', a, b), c), ('fmul', ('flrp', 1.0, b, c), a), 'options->lower_flrp{}'.format(s)), - (('~fadd@{}'.format(s), ('fmul', a, ('fadd', 1.0, ('fneg', c))), ('fmul', b, c)), ('flrp', a, b, c), '!options->lower_flrp{}'.format(s)), + (('~fadd@{}(nsz)'.format(s), ('fmul', a, ('fadd', 1.0, ('fneg', c))), ('fmul', b, c)), ('flrp', a, b, c), '!options->lower_flrp{}'.format(s)), # These are the same as the previous three rules, but it depends on # 1-fsat(x) <=> fsat(1-x). See below. - (('~fadd@{}'.format(s), ('fmul', a, ('fsat', ('fadd', 1.0, ('fneg', c)))), ('fmul', b, ('fsat', c))), ('flrp', a, b, ('fsat', c)), '!options->lower_flrp{}'.format(s)), - (('~fadd@{}'.format(s), a, ('fmul', c, ('fadd', b, ('fneg', a)))), ('flrp', a, b, c), '!options->lower_flrp{}'.format(s)), + (('~fadd@{}(nsz)'.format(s), ('fmul', a, ('fsat', ('fadd', 1.0, ('fneg', c)))), ('fmul', b, ('fsat', c))), ('flrp', a, b, ('fsat', c)), '!options->lower_flrp{}'.format(s)), + (('~fadd@{}(nsz)'.format(s), a, ('fmul', c, ('fadd', b, ('fneg', a)))), ('flrp', a, b, c), '!options->lower_flrp{}'.format(s)), # 1 - ((1 - a) * (1 - b)) # 1 - (1 - a - b + a*b) @@ -509,7 +509,7 @@ for s in [16, 32, 64]: # a + b*(1 - a) # b*(1 - a) + 1*a # flrp(b, 1, a) - (('~fadd@{}'.format(s), 1.0, ('fneg', ('fmul', ('fadd', 1.0, ('fneg', a)), ('fadd', 1.0, ('fneg', b))))), ('flrp', b, 1.0, a), '!options->lower_flrp{}'.format(s)), + (('~fadd@{}(nsz)'.format(s), 1.0, ('fneg', ('fmul', ('fadd', 1.0, ('fneg', a)), ('fadd', 1.0, ('fneg', b))))), ('flrp', b, 1.0, a), '!options->lower_flrp{}'.format(s)), ]) optimizations.extend([ diff --git a/src/intel/compiler/elk/elk_compiler.h b/src/intel/compiler/elk/elk_compiler.h index 6f9f9fe..06a9853 100644 --- a/src/intel/compiler/elk/elk_compiler.h +++ b/src/intel/compiler/elk/elk_compiler.h @@ -185,14 +185,19 @@ struct elk_sampler_prog_key_data { enum elk_robustness_flags { ELK_ROBUSTNESS_UBO = BITFIELD_BIT(0), ELK_ROBUSTNESS_SSBO = BITFIELD_BIT(1), + /* Bounds check typed image access in the shader instead of relying on the + * hardware. Only needed for null descriptors: gfx7 has no zero-sized + * surface, so the null surface still has one addressable texel. + */ + ELK_ROBUSTNESS_IMAGE = BITFIELD_BIT(2), }; struct elk_base_prog_key { unsigned program_string_id; - enum elk_robustness_flags robust_flags:2; + enum elk_robustness_flags robust_flags:3; - unsigned padding:22; + unsigned padding:21; /** * Apply workarounds for SIN and COS input range problems. diff --git a/src/intel/compiler/elk/elk_nir.h b/src/intel/compiler/elk/elk_nir.h index a874e5a..0febcb5 100644 --- a/src/intel/compiler/elk/elk_nir.h +++ b/src/intel/compiler/elk/elk_nir.h @@ -199,6 +199,12 @@ struct elk_nir_lower_storage_image_opts { bool lower_stores; bool lower_atomics; bool lower_get_size; + + /* Bounds check typed image access against the image param instead of + * trusting the surface. Needed to make null descriptors read as zero and + * swallow their stores; see ELK_ROBUSTNESS_IMAGE. + */ + bool robust_image_access; }; bool elk_nir_lower_storage_image(nir_shader *nir, diff --git a/src/intel/compiler/elk/elk_nir_lower_storage_image.c b/src/intel/compiler/elk/elk_nir_lower_storage_image.c index 7bbd117..4142c89 100644 --- a/src/intel/compiler/elk/elk_nir_lower_storage_image.c +++ b/src/intel/compiler/elk/elk_nir_lower_storage_image.c @@ -47,9 +47,16 @@ image_coord_is_in_bounds(nir_builder *b, nir_deref_instr *deref, nir_def *coord) { nir_def *size = load_image_param(b, deref, SIZE); - nir_def *cmp = nir_ilt(b, coord, size); - unsigned coord_comps = glsl_get_sampler_coordinate_components(deref->type); + /* SIZE only carries three components, so a cube array's fourth coordinate + * has no bound to compare against. + */ + unsigned coord_comps = + MIN2(glsl_get_sampler_coordinate_components(deref->type), + size->num_components); + nir_def *cmp = nir_ilt(b, nir_trim_vector(b, coord, coord_comps), + nir_trim_vector(b, size, coord_comps)); + nir_def *in_bounds = nir_imm_true(b); for (unsigned i = 0; i < coord_comps; i++) in_bounds = nir_iand(b, in_bounds, nir_channel(b, cmp, i)); @@ -57,6 +64,21 @@ image_coord_is_in_bounds(nir_builder *b, nir_deref_instr *deref, return in_bounds; } +/* A null descriptor binds the null surface. gfx7's typed data port does not + * honour SURFTYPE_NULL -- and there is no way to describe a zero-sized surface + * either, so the null surface keeps one addressable texel that reads back + * whatever is at graphics address zero and happily absorbs stores. The image + * param is all zeroes for a null descriptor and a real image always has a + * non-zero width, so that is what we test. Same trick lower_image_atomic_instr + * has always used on IVB. + */ +static nir_def * +image_is_bound(nir_builder *b, nir_deref_instr *deref) +{ + nir_def *size = load_image_param(b, deref, SIZE); + return nir_ine_imm(b, nir_channel(b, size, 0), 0); +} + /** Calculate the offset in memory of the texel given by \p coord. * * This is meant to be used with untyped surface messages to access a tiled @@ -345,7 +367,8 @@ static bool lower_image_load_instr(nir_builder *b, const struct intel_device_info *devinfo, nir_intrinsic_instr *intrin, - bool sparse) + bool sparse, + bool robust_image_access) { nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]); nir_variable *var = nir_deref_instr_get_variable(deref); @@ -394,6 +417,18 @@ lower_image_load_instr(nir_builder *b, color = nir_vec(b, sparse_color, dest_components + 1); } + /* A null descriptor binds the null surface, which gfx7's typed data + * port does not honour: it still has one addressable texel and there is + * no way to describe a zero-sized surface. Force that last texel to + * read as zero using the image param, which is all-zero for a null + * descriptor. + */ + if (robust_image_access) { + color = nir_bcsel(b, image_is_bound(b, deref), color, + nir_imm_zero(b, color->num_components, + color->bit_size)); + } + nir_def_replace(placeholder, color); } else { /* This code part is only useful prior to Gfx9, we do not have plans to @@ -522,7 +557,8 @@ convert_color_for_store(nir_builder *b, const struct intel_device_info *devinfo, static bool lower_image_store_instr(nir_builder *b, const struct intel_device_info *devinfo, - nir_intrinsic_instr *intrin) + nir_intrinsic_instr *intrin, + bool robust_image_access) { nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]); nir_variable *var = nir_deref_instr_get_variable(deref); @@ -551,6 +587,18 @@ lower_image_store_instr(nir_builder *b, image_fmt, lower_fmt); intrin->num_components = isl_format_get_num_channels(lower_fmt); nir_src_rewrite(&intrin->src[3], color); + + /* See the matching comment in lower_image_load_instr(): the null + * surface's one live texel would otherwise absorb the store and hand it + * back on the next load. + */ + if (robust_image_access) { + nir_def *bound = image_is_bound(b, deref); + nir_instr_remove(&intrin->instr); + nir_push_if(b, bound); + nir_builder_instr_insert(b, &intrin->instr); + nir_pop_if(b, NULL); + } } else { const struct isl_format_layout *image_fmtl = isl_format_get_layout(image_fmt); @@ -602,9 +650,10 @@ lower_image_store_instr(nir_builder *b, static bool lower_image_atomic_instr(nir_builder *b, const struct intel_device_info *devinfo, - nir_intrinsic_instr *intrin) + nir_intrinsic_instr *intrin, + bool robust_image_access) { - if (devinfo->verx10 >= 75) + if (devinfo->verx10 >= 75 && !robust_image_access) return false; nir_deref_instr *deref = nir_src_as_deref(intrin->src[0]); @@ -694,23 +743,27 @@ elk_nir_lower_storage_image_instr(nir_builder *b, switch (intrin->intrinsic) { case nir_intrinsic_image_deref_load: if (opts->lower_loads) - return lower_image_load_instr(b, opts->devinfo, intrin, false); + return lower_image_load_instr(b, opts->devinfo, intrin, false, + opts->robust_image_access); return false; case nir_intrinsic_image_deref_sparse_load: if (opts->lower_loads) - return lower_image_load_instr(b, opts->devinfo, intrin, true); + return lower_image_load_instr(b, opts->devinfo, intrin, true, + opts->robust_image_access); return false; case nir_intrinsic_image_deref_store: if (opts->lower_stores) - return lower_image_store_instr(b, opts->devinfo, intrin); + return lower_image_store_instr(b, opts->devinfo, intrin, + opts->robust_image_access); return false; case nir_intrinsic_image_deref_atomic: case nir_intrinsic_image_deref_atomic_swap: if (opts->lower_atomics) - return lower_image_atomic_instr(b, opts->devinfo, intrin); + return lower_image_atomic_instr(b, opts->devinfo, intrin, + opts->robust_image_access); return false; case nir_intrinsic_image_deref_size: diff --git a/src/intel/genxml/gen70.xml b/src/intel/genxml/gen70.xml index ec5d03c..716d779 100644 --- a/src/intel/genxml/gen70.xml +++ b/src/intel/genxml/gen70.xml @@ -2191,7 +2191,9 @@ - + + + diff --git a/src/intel/vulkan_hasvk/anv_cmd_buffer.c b/src/intel/vulkan_hasvk/anv_cmd_buffer.c index 2a4c8ce..00b148a 100644 --- a/src/intel/vulkan_hasvk/anv_cmd_buffer.c +++ b/src/intel/vulkan_hasvk/anv_cmd_buffer.c @@ -104,6 +104,8 @@ anv_create_cmd_buffer(struct vk_command_pool *pool, cmd_buffer->vk.dynamic_graphics_state.ms.sample_locations = &cmd_buffer->state.gfx.sample_locations; + cmd_buffer->vk.dynamic_graphics_state.vi = + &cmd_buffer->state.gfx.vertex_input; cmd_buffer->batch.status = VK_SUCCESS; @@ -806,14 +808,19 @@ anv_cmd_buffer_push_descriptor_sets(struct anv_cmd_buffer *cmd_buffer, for (uint32_t j = 0; j < write->descriptorCount; j++) { ANV_FROM_HANDLE(anv_buffer, buffer, write->pBufferInfo[j].buffer); - anv_descriptor_set_write_buffer(cmd_buffer->device, set, - &cmd_buffer->surface_state_stream, - write->descriptorType, - buffer, - write->dstBinding, - write->dstArrayElement + j, - write->pBufferInfo[j].offset, - write->pBufferInfo[j].range); + VkResult result = + anv_descriptor_set_write_buffer(cmd_buffer->device, set, + &cmd_buffer->surface_state_stream, + write->descriptorType, + buffer, + write->dstBinding, + write->dstArrayElement + j, + write->pBufferInfo[j].offset, + write->pBufferInfo[j].range); + if (result != VK_SUCCESS) { + anv_batch_set_error(&cmd_buffer->batch, result); + return; + } } break; @@ -862,10 +869,15 @@ void anv_CmdPushDescriptorSetWithTemplate2KHR( if (!set) return; - anv_descriptor_set_write_template(cmd_buffer->device, set, - &cmd_buffer->surface_state_stream, - template, - pInfo->pData); + VkResult result = + anv_descriptor_set_write_template(cmd_buffer->device, set, + &cmd_buffer->surface_state_stream, + template, + pInfo->pData); + if (result != VK_SUCCESS) { + anv_batch_set_error(&cmd_buffer->batch, result); + return; + } anv_cmd_buffer_bind_descriptor_set(cmd_buffer, template->bind_point, pipeline_layout, pInfo->set, set, NULL, NULL); diff --git a/src/intel/vulkan_hasvk/anv_descriptor_set.c b/src/intel/vulkan_hasvk/anv_descriptor_set.c index fbe0b32..27bdddc 100644 --- a/src/intel/vulkan_hasvk/anv_descriptor_set.c +++ b/src/intel/vulkan_hasvk/anv_descriptor_set.c @@ -1543,7 +1543,7 @@ anv_descriptor_set_write_buffer_view(struct anv_device *device, } } -void +VkResult anv_descriptor_set_write_buffer(struct anv_device *device, struct anv_descriptor_set *set, struct anv_state_stream *alloc_stream, @@ -1572,14 +1572,14 @@ anv_descriptor_set_write_buffer(struct anv_device *device, }; if (set->pool && set->pool->host_only) - return; + return VK_SUCCESS; void *desc_map = set->desc_mem.map + bind_layout->descriptor_offset + element * bind_layout->descriptor_stride; if (buffer == NULL) { memset(desc_map, 0, bind_layout->descriptor_stride); - return; + return VK_SUCCESS; } struct anv_address bind_addr = anv_address_add(buffer->address, offset); @@ -1605,7 +1605,7 @@ anv_descriptor_set_write_buffer(struct anv_device *device, } if (vk_descriptor_type_is_dynamic(type)) - return; + return VK_SUCCESS; assert(data & ANV_DESCRIPTOR_BUFFER_VIEW); struct anv_buffer_view *bview = @@ -1620,6 +1620,13 @@ anv_descriptor_set_write_buffer(struct anv_device *device, * vkAllocateDescriptorSets. */ if (alloc_stream) { bview->surface_state = anv_state_stream_alloc(alloc_stream, 64, 64); + /* The stream is backed by the command buffer's surface state block; it + * can run dry. anv_fill_buffer_surface_state() would then write through + * a NULL map, which is a SIGBUS in release builds because the assert + * below is compiled out. + */ + if (bview->surface_state.map == NULL) + return vk_error(device, VK_ERROR_OUT_OF_DEVICE_MEMORY); } assert(bview->surface_state.alloc_size); @@ -1635,6 +1642,8 @@ anv_descriptor_set_write_buffer(struct anv_device *device, format, ISL_SWIZZLE_IDENTITY, usage, bind_addr, bind_range, 1); desc->set_buffer_view = bview; + + return VK_SUCCESS; } void @@ -1704,6 +1713,7 @@ void anv_UpdateDescriptorSets( for (uint32_t j = 0; j < write->descriptorCount; j++) { ANV_FROM_HANDLE(anv_buffer, buffer, write->pBufferInfo[j].buffer); + ASSERTED VkResult result = anv_descriptor_set_write_buffer(device, set, NULL, write->descriptorType, @@ -1712,6 +1722,9 @@ void anv_UpdateDescriptorSets( write->dstArrayElement + j, write->pBufferInfo[j].offset, write->pBufferInfo[j].range); + /* No alloc_stream, so the surface state comes from the pool and + * this cannot fail. */ + assert(result == VK_SUCCESS); } break; @@ -1789,6 +1802,7 @@ void anv_UpdateDescriptorSets( case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: { + ASSERTED VkResult result = anv_descriptor_set_write_buffer(device, dst, NULL, src_desc[j].type, @@ -1797,6 +1811,7 @@ void anv_UpdateDescriptorSets( copy->dstArrayElement + j, src_desc[j].offset, src_desc[j].range); + assert(result == VK_SUCCESS); break; } @@ -1811,7 +1826,7 @@ void anv_UpdateDescriptorSets( * Descriptor update templates. */ -void +VkResult anv_descriptor_set_write_template(struct anv_device *device, struct anv_descriptor_set *set, struct anv_state_stream *alloc_stream, @@ -1862,13 +1877,16 @@ anv_descriptor_set_write_template(struct anv_device *device, data + entry->offset + j * entry->stride; ANV_FROM_HANDLE(anv_buffer, buffer, info->buffer); - anv_descriptor_set_write_buffer(device, set, - alloc_stream, - entry->type, - buffer, - entry->binding, - entry->array_element + j, - info->offset, info->range); + VkResult result = + anv_descriptor_set_write_buffer(device, set, + alloc_stream, + entry->type, + buffer, + entry->binding, + entry->array_element + j, + info->offset, info->range); + if (result != VK_SUCCESS) + return result; } break; @@ -1884,6 +1902,8 @@ anv_descriptor_set_write_template(struct anv_device *device, break; } } + + return VK_SUCCESS; } void anv_UpdateDescriptorSetWithTemplate( @@ -1897,5 +1917,8 @@ void anv_UpdateDescriptorSetWithTemplate( VK_FROM_HANDLE(vk_descriptor_update_template, template, descriptorUpdateTemplate); - anv_descriptor_set_write_template(device, set, NULL, template, pData); + /* No alloc_stream, so nothing here allocates and this cannot fail. */ + ASSERTED VkResult result = + anv_descriptor_set_write_template(device, set, NULL, template, pData); + assert(result == VK_SUCCESS); } diff --git a/src/intel/vulkan_hasvk/anv_device.c b/src/intel/vulkan_hasvk/anv_device.c index 0d34121..93fb80e 100644 --- a/src/intel/vulkan_hasvk/anv_device.c +++ b/src/intel/vulkan_hasvk/anv_device.c @@ -99,6 +99,7 @@ static const driOptionDescription anv_dri_options[] = { static const struct debug_control debug_control[] = { { "video-decode", ANV_DEBUG_VIDEO_DECODE }, { "video-debug", ANV_DEBUG_VIDEO_DEBUG }, + { "video-encode", ANV_DEBUG_VIDEO_ENCODE }, { NULL, 0 } }; @@ -206,7 +207,10 @@ get_device_extensions(const struct anv_physical_device *device, const bool has_syncobj_wait = (device->sync_syncobj_type.features & VK_SYNC_FEATURE_CPU_WAIT) != 0; - const bool video_decode = VIDEO_CODEC_H264DEC && ANV_DEBUG(VIDEO_DECODE); + /* Video extensions are advertised by default (not gated behind + * HASVK_DEBUG); the compile-time VIDEO_CODEC_* configuration still applies. */ + const bool video_decode = VIDEO_CODEC_H264DEC; + const bool video_encode = VIDEO_CODEC_H264ENC; *ext = (struct vk_device_extension_table) { .KHR_8bit_storage = device->info.ver >= 8, @@ -217,6 +221,7 @@ get_device_extensions(const struct anv_physical_device *device, .KHR_create_renderpass2 = true, .KHR_dedicated_allocation = true, .KHR_deferred_host_operations = true, + .KHR_depth_clamp_zero_one = true, .KHR_depth_stencil_resolve = true, .KHR_descriptor_update_template = true, .KHR_device_group = true, @@ -233,6 +238,7 @@ get_device_extensions(const struct anv_physical_device *device, .KHR_get_memory_requirements2 = true, .KHR_image_format_list = true, .KHR_imageless_framebuffer = true, + .KHR_load_store_op_none = true, #ifdef ANV_USE_WSI_PLATFORM .KHR_incremental_present = true, #endif @@ -264,10 +270,12 @@ get_device_extensions(const struct anv_physical_device *device, .KHR_shader_expect_assume = true, .KHR_shader_float16_int8 = device->info.ver >= 8 && !device->instance->no_16bit, .KHR_shader_float_controls = true, + .KHR_shader_float_controls2 = true, .KHR_shader_integer_dot_product = true, .KHR_shader_non_semantic_info = true, .KHR_shader_relaxed_extended_instruction = true, .KHR_shader_subgroup_extended_types = device->info.ver >= 8, + .KHR_shader_subgroup_rotate = true, .KHR_shader_subgroup_uniform_control_flow = true, .KHR_shader_terminate_invocation = true, .KHR_spirv_1_4 = true, @@ -276,11 +284,13 @@ get_device_extensions(const struct anv_physical_device *device, .KHR_swapchain = true, .KHR_swapchain_mutable_format = true, #endif - .KHR_video_queue = video_decode, + .KHR_video_queue = video_decode || video_encode, .KHR_video_decode_queue = video_decode, .KHR_video_decode_h264 = video_decode, - .KHR_video_maintenance1 = video_decode, - .KHR_video_maintenance2 = video_decode, + .KHR_video_encode_queue = video_encode, + .KHR_video_encode_h264 = video_encode, + .KHR_video_maintenance1 = video_decode || video_encode, + .KHR_video_maintenance2 = video_decode || video_encode, .KHR_synchronization2 = true, .KHR_timeline_semaphore = true, .KHR_internally_synchronized_queues = true, @@ -290,12 +300,12 @@ get_device_extensions(const struct anv_physical_device *device, .KHR_workgroup_memory_explicit_layout = true, .KHR_zero_initialize_workgroup_memory = true, .EXT_4444_formats = true, - .EXT_border_color_swizzle = device->info.ver >= 8, + .EXT_border_color_swizzle = device->info.ver >= 7, .EXT_buffer_device_address = device->has_a64_buffer_access, .EXT_calibrated_timestamps = device->has_reg_timestamp, .EXT_color_write_enable = true, - .EXT_conditional_rendering = device->info.verx10 >= 75, - .EXT_custom_border_color = device->info.ver >= 8, + .EXT_conditional_rendering = device->info.verx10 >= 70, + .EXT_custom_border_color = device->info.ver >= 7, .EXT_debug_marker = true, .EXT_depth_clamp_zero_one = true, .EXT_depth_clamp_control = true, @@ -304,6 +314,7 @@ get_device_extensions(const struct anv_physical_device *device, #ifdef VK_USE_PLATFORM_DISPLAY_KHR .EXT_display_control = true, #endif + .EXT_dynamic_rendering_unused_attachments = true, .EXT_extended_dynamic_state = true, .EXT_extended_dynamic_state2 = true, .EXT_external_memory_dma_buf = true, @@ -312,14 +323,19 @@ get_device_extensions(const struct anv_physical_device *device, INTEL_CONTEXT_MEDIUM_PRIORITY, .EXT_global_priority_query = device->max_context_priority >= INTEL_CONTEXT_MEDIUM_PRIORITY, + .EXT_host_image_copy = true, .EXT_host_query_reset = true, .EXT_image_2d_view_of_3d = true, .EXT_image_robustness = true, .EXT_image_drm_format_modifier = true, .EXT_image_view_min_lod = true, .EXT_index_type_uint8 = true, + .KHR_index_type_uint8 = true, .EXT_inline_uniform_block = true, .EXT_line_rasterization = true, + .KHR_line_rasterization = true, + .EXT_load_store_op_none = true, + .KHR_map_memory2 = true, /* Enable the extension only if we have support on both the local & * system memory */ @@ -329,6 +345,7 @@ get_device_extensions(const struct anv_physical_device *device, .EXT_physical_device_drm = true, .EXT_pipeline_creation_cache_control = true, .EXT_pipeline_creation_feedback = true, + .EXT_pipeline_robustness = true, .EXT_primitives_generated_query = true, .EXT_primitive_topology_list_restart = true, .EXT_private_data = true, @@ -350,6 +367,8 @@ get_device_extensions(const struct anv_physical_device *device, .EXT_tooling_info = true, .EXT_transform_feedback = true, .EXT_vertex_attribute_divisor = true, + .EXT_vertex_input_dynamic_state = true, + .KHR_vertex_attribute_divisor = true, .EXT_ycbcr_image_arrays = true, #if DETECT_OS_ANDROID .ANDROID_external_memory_android_hardware_buffer = true, @@ -364,6 +383,7 @@ get_device_extensions(const struct anv_physical_device *device, .EXT_multi_draw = true, .NV_compute_shader_derivatives = true, .VALVE_mutable_descriptor_type = true, + .EXT_mutable_descriptor_type = true }; } @@ -511,6 +531,9 @@ get_features(const struct anv_physical_device *pdevice, .formatA4R4G4B4 = true, .formatA4B4G4R4 = false, + /* VK_EXT_host_image_copy */ + .hostImageCopy = true, + /* VK_EXT_border_color_swizzle */ .borderColorSwizzle = true, .borderColorSwizzleFromImage = true, @@ -527,12 +550,12 @@ get_features(const struct anv_physical_device *pdevice, .computeDerivativeGroupLinear = true, /* VK_EXT_conditional_rendering */ - .conditionalRendering = pdevice->info.verx10 >= 75, + .conditionalRendering = pdevice->info.verx10 >= 70, .inheritedConditionalRendering = pdevice->info.verx10 >= 75, /* VK_EXT_custom_border_color */ - .customBorderColors = pdevice->info.ver >= 8, - .customBorderColorWithoutFormat = pdevice->info.ver >= 8, + .customBorderColors = pdevice->info.ver >= 7, + .customBorderColorWithoutFormat = pdevice->info.ver >= 7, /* VK_EXT_depth_clamp_zero_one */ .depthClampZeroOne = true, @@ -594,6 +617,9 @@ get_features(const struct anv_physical_device *pdevice, .robustImageAccess2 = true, .nullDescriptor = true, + /* VK_EXT_pipeline_robustness */ + .pipelineRobustness = true, + /* VK_EXT_shader_atomic_float */ .shaderBufferFloat32Atomics = true, .shaderBufferFloat32AtomicAdd = pdevice->info.has_lsc, @@ -619,9 +645,16 @@ get_features(const struct anv_physical_device *pdevice, /* VK_EXT_shader_module_identifier */ .shaderModuleIdentifier = true, + /* VK_KHR_shader_float_controls2 */ + .shaderFloatControls2 = true, + /* VK_EXT_shader_replicated_composites */ .shaderReplicatedComposites = true, + /* VK_KHR_shader_subgroup_rotate */ + .shaderSubgroupRotate = true, + .shaderSubgroupRotateClustered = true, + /* VK_KHR_shader_subgroup_uniform_control_flow */ .shaderSubgroupUniformControlFlow = true, @@ -645,6 +678,9 @@ get_features(const struct anv_physical_device *pdevice, /* VK_EXT_ycbcr_image_arrays */ .ycbcrImageArrays = true, + /* VK_EXT_dynamic_rendering_unused_attachments */ + .dynamicRenderingUnusedAttachments = true, + /* VK_EXT_extended_dynamic_state */ .extendedDynamicState = true, @@ -653,6 +689,9 @@ get_features(const struct anv_physical_device *pdevice, .extendedDynamicState2LogicOp = true, .extendedDynamicState2PatchControlPoints = false, + /* VK_EXT_vertex_input_dynamic_state */ + .vertexInputDynamicState = true, + /* VK_EXT_multi_draw */ .multiDraw = true, @@ -744,7 +783,9 @@ get_properties_1_1(const struct anv_physical_device *pdevice, VK_SUBGROUP_FEATURE_BALLOT_BIT | VK_SUBGROUP_FEATURE_SHUFFLE_BIT | VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT | - VK_SUBGROUP_FEATURE_QUAD_BIT; + VK_SUBGROUP_FEATURE_QUAD_BIT | + VK_SUBGROUP_FEATURE_ROTATE_BIT_KHR | + VK_SUBGROUP_FEATURE_ROTATE_CLUSTERED_BIT_KHR; if (pdevice->info.ver >= 8) { /* TODO: There's no technical reason why these can't be made to * work on gfx7 but they don't at the moment so it's best to leave @@ -1215,6 +1256,22 @@ get_properties(const struct anv_physical_device *pdevice, props->lineSubPixelPrecisionBits = 4; } + /* VK_EXT_host_image_copy */ + { + static const VkImageLayout host_copy_layouts[] = { + VK_IMAGE_LAYOUT_GENERAL, + }; + + props->copySrcLayoutCount = ARRAY_SIZE(host_copy_layouts); + props->pCopySrcLayouts = (VkImageLayout *)host_copy_layouts; + props->copyDstLayoutCount = ARRAY_SIZE(host_copy_layouts); + props->pCopyDstLayouts = (VkImageLayout *)host_copy_layouts; + props->identicalMemoryTypeRequirements = true; + + memcpy(props->optimalTilingLayoutUUID, pdevice->driver_uuid, + VK_UUID_SIZE); + } + /* VK_EXT_multi_draw */ { props->maxMultiDrawCount = 2048; @@ -1238,6 +1295,21 @@ get_properties(const struct anv_physical_device *pdevice, props->drmRenderMinor = pdevice->local_minor; } + /* VK_EXT_pipeline_robustness */ + { + /* Intel HW bounds-checks vertex fetch and image/texel access + * unconditionally, so the device default is robust for those; buffer + * access is unchecked unless robustBufferAccess is enabled. */ + props->defaultRobustnessStorageBuffers = + VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED_EXT; + props->defaultRobustnessUniformBuffers = + VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED_EXT; + props->defaultRobustnessVertexInputs = + VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_ROBUST_BUFFER_ACCESS_2_EXT; + props->defaultRobustnessImages = + VK_PIPELINE_ROBUSTNESS_IMAGE_BEHAVIOR_ROBUST_IMAGE_ACCESS_2_EXT; + } + /* VK_EXT_provoking_vertex */ { props->provokingVertexModePerPipeline = true; @@ -1594,17 +1666,26 @@ anv_physical_device_init_queue_families(struct anv_physical_device *pdevice) .engine_class = INTEL_ENGINE_CLASS_RENDER, }; } - if (v_count > 0 && VIDEO_CODEC_H264DEC && ANV_DEBUG(VIDEO_DECODE)) { + /* Video queues are exposed by default (not gated behind HASVK_DEBUG); + * the compile-time VIDEO_CODEC_* configuration still applies. */ + if (v_count > 0 && VIDEO_CODEC_H264DEC) { pdevice->queue.families[family_count++] = (struct anv_queue_family) { .queueFlags = VK_QUEUE_VIDEO_DECODE_BIT_KHR, .queueCount = v_count, .engine_class = INTEL_ENGINE_CLASS_VIDEO, }; } - /* Increase count below when other families are added as a reminder to + if (v_count > 0 && VIDEO_CODEC_H264ENC) { + pdevice->queue.families[family_count++] = (struct anv_queue_family) { + .queueFlags = VK_QUEUE_VIDEO_ENCODE_BIT_KHR, + .queueCount = v_count, + .engine_class = INTEL_ENGINE_CLASS_VIDEO, + }; + } + /* Increase count below when other families are added and * increase the ANV_MAX_QUEUE_FAMILIES value. */ - STATIC_ASSERT(ANV_MAX_QUEUE_FAMILIES >= 3); + STATIC_ASSERT(ANV_MAX_QUEUE_FAMILIES >= 4); } else { /* Default to a single render queue */ pdevice->queue.families[family_count++] = (struct anv_queue_family) { @@ -2135,7 +2216,8 @@ void anv_GetPhysicalDeviceQueueFamilyProperties2( * to write the query result to memory. The VCS seemingly * cannot do a posted memory write. */ prop->queryResultStatusSupport = - (queue_family->queueFlags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) ? + (queue_family->queueFlags & (VK_QUEUE_VIDEO_DECODE_BIT_KHR | + VK_QUEUE_VIDEO_ENCODE_BIT_KHR)) ? VK_FALSE : VK_TRUE; break; } @@ -2145,6 +2227,9 @@ void anv_GetPhysicalDeviceQueueFamilyProperties2( if (queue_family->queueFlags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) { prop->videoCodecOperations = VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR; + } else if (queue_family->queueFlags & VK_QUEUE_VIDEO_ENCODE_BIT_KHR) { + prop->videoCodecOperations = + VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR; } break; } @@ -2684,7 +2769,7 @@ VkResult anv_CreateDevice( if (result != VK_SUCCESS) goto fail_general_state_pool; - if (device->info->ver >= 8) { + if (device->info->ver >= 7) { /* The border color pointer is limited to 24 bits, so we need to make * sure that any such color used at any point in the program doesn't * exceed that limit. @@ -2818,7 +2903,7 @@ VkResult anv_CreateDevice( fail_instruction_state_pool: anv_state_pool_finish(&device->instruction_state_pool); fail_dynamic_state_pool: - if (device->info->ver >= 8) + if (device->info->ver >= 7) anv_state_reserved_pool_finish(&device->custom_border_colors); anv_state_pool_finish(&device->dynamic_state_pool); fail_general_state_pool: @@ -2870,7 +2955,7 @@ void anv_DestroyDevice( /* We only need to free these to prevent valgrind errors. The backing * BO will go away in a couple of lines so we don't actually leak. */ - if (device->info->ver >= 8) + if (device->info->ver >= 7) anv_state_reserved_pool_finish(&device->custom_border_colors); anv_state_pool_free(&device->dynamic_state_pool, device->border_colors); anv_state_pool_free(&device->dynamic_state_pool, device->slice_hash); @@ -3355,8 +3440,11 @@ void anv_FreeMemory( list_del(&mem->link); pthread_mutex_unlock(&device->mutex); - if (mem->map) - anv_UnmapMemory(_device, _mem); + const VkMemoryUnmapInfoKHR unmap = { + .sType = VK_STRUCTURE_TYPE_MEMORY_UNMAP_INFO_KHR, + .memory = _mem, + }; + anv_UnmapMemory2KHR(_device, &unmap); p_atomic_add(&device->physical->memory.heaps[mem->type->heapIndex].used, -mem->bo->size); @@ -3371,16 +3459,17 @@ void anv_FreeMemory( vk_object_free(&device->vk, pAllocator, mem); } -VkResult anv_MapMemory( +VkResult anv_MapMemory2KHR( VkDevice _device, - VkDeviceMemory _memory, - VkDeviceSize offset, - VkDeviceSize size, - VkMemoryMapFlags flags, + const VkMemoryMapInfoKHR* pMemoryMapInfo, void** ppData) { ANV_FROM_HANDLE(anv_device, device, _device); - ANV_FROM_HANDLE(anv_device_memory, mem, _memory); + ANV_FROM_HANDLE(anv_device_memory, mem, pMemoryMapInfo->memory); + + const VkDeviceSize offset = pMemoryMapInfo->offset; + VkDeviceSize size = pMemoryMapInfo->size; + const VkMemoryMapFlags flags = pMemoryMapInfo->flags; if (mem == NULL) { *ppData = NULL; @@ -3462,21 +3551,23 @@ VkResult anv_MapMemory( return VK_SUCCESS; } -void anv_UnmapMemory( +VkResult anv_UnmapMemory2KHR( VkDevice _device, - VkDeviceMemory _memory) + const VkMemoryUnmapInfoKHR* pMemoryUnmapInfo) { ANV_FROM_HANDLE(anv_device, device, _device); - ANV_FROM_HANDLE(anv_device_memory, mem, _memory); + ANV_FROM_HANDLE(anv_device_memory, mem, pMemoryUnmapInfo->memory); if (mem == NULL || mem->host_ptr) - return; + return VK_SUCCESS; anv_device_unmap_bo(device, mem->bo, mem->map, mem->map_size); mem->map = NULL; mem->map_size = 0; mem->map_delta = 0; + + return VK_SUCCESS; } VkResult anv_FlushMappedMemoryRanges( diff --git a/src/intel/vulkan_hasvk/anv_formats.c b/src/intel/vulkan_hasvk/anv_formats.c index 77df092..1563602 100644 --- a/src/intel/vulkan_hasvk/anv_formats.c +++ b/src/intel/vulkan_hasvk/anv_formats.c @@ -593,7 +593,7 @@ anv_get_image_format_features2(const struct intel_device_info *devinfo, VK_FORMAT_FEATURE_2_BLIT_SRC_BIT | VK_FORMAT_FEATURE_2_BLIT_DST_BIT | VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT | - VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT; + VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT ; if (aspects & VK_IMAGE_ASPECT_DEPTH_BIT) flags |= VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_LINEAR_BIT; @@ -606,9 +606,14 @@ anv_get_image_format_features2(const struct intel_device_info *devinfo, if ((anv_format->flags & ANV_FORMAT_FLAG_CAN_VIDEO) && (anv_format->n_planes > 1 || vk_tiling == VK_IMAGE_TILING_OPTIMAL)) { - flags |= (VIDEO_CODEC_H264DEC && ANV_DEBUG(VIDEO_DECODE)) ? + /* Video format features are exposed by default (not gated behind + * HASVK_DEBUG). */ + flags |= VIDEO_CODEC_H264DEC ? VK_FORMAT_FEATURE_2_VIDEO_DECODE_OUTPUT_BIT_KHR | VK_FORMAT_FEATURE_2_VIDEO_DECODE_DPB_BIT_KHR : 0; + flags |= VIDEO_CODEC_H264ENC ? + VK_FORMAT_FEATURE_2_VIDEO_ENCODE_INPUT_BIT_KHR | + VK_FORMAT_FEATURE_2_VIDEO_ENCODE_DPB_BIT_KHR : 0; } assert(aspects & VK_IMAGE_ASPECT_ANY_COLOR_BIT_ANV); @@ -665,9 +670,18 @@ anv_get_image_format_features2(const struct intel_device_info *devinfo, /* Load/store is determined based on base format. This prevents RGB * formats from showing up as load/store capable. + * + * We never advertise STORAGE_READ_WITHOUT_FORMAT because + * shaderStorageImageReadWithoutFormat is always false here: storage image + * surface state is programmed with isl_lower_storage_image_format(), so a + * formatless typed read returns raw bits for every format that gets + * lowered. Since Vulkan 1.3 keys formatless reads off this per-format bit + * rather than off the device feature, advertising it while + * anv_pipeline.c rejects such shaders with VK_ERROR_FEATURE_NOT_PRESENT is + * a contradiction. Enabling the feature instead is not an option either: + * it would claim formatless reads for every extended storage format, which + * is exactly the set we cannot do. */ - if (isl_format_supports_typed_reads(devinfo, base_isl_format)) - flags |= VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT; if (isl_format_supports_typed_writes(devinfo, base_isl_format)) flags |= VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT; @@ -741,18 +755,18 @@ anv_get_image_format_features2(const struct intel_device_info *devinfo, if (anv_format->n_planes > 1) flags |= VK_FORMAT_FEATURE_2_DISJOINT_BIT; + /* Formats that require a sampler Y'CbCr conversion may not report any of + * these, video capable or not -- a decode DPB/output image does not need + * them, and anything that wants to blit or store goes through a + * single-plane view of one plane, whose format is not a YCbCr format and + * so keeps its own feature bits. + */ VkFormatFeatureFlags2 disallowed_ycbcr_image_features = VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BIT | - VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT; - - /* For video decode formats, allow BLIT and STORAGE operations. */ - if (!(flags & (VK_FORMAT_FEATURE_2_VIDEO_DECODE_OUTPUT_BIT_KHR | - VK_FORMAT_FEATURE_2_VIDEO_DECODE_DPB_BIT_KHR))) { - disallowed_ycbcr_image_features |= - VK_FORMAT_FEATURE_2_BLIT_SRC_BIT | - VK_FORMAT_FEATURE_2_BLIT_DST_BIT | - VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT; - } + VK_FORMAT_FEATURE_2_COLOR_ATTACHMENT_BLEND_BIT | + VK_FORMAT_FEATURE_2_BLIT_SRC_BIT | + VK_FORMAT_FEATURE_2_BLIT_DST_BIT | + VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT; flags &= ~disallowed_ycbcr_image_features; } @@ -822,6 +836,15 @@ anv_get_image_format_features2(const struct intel_device_info *devinfo, } } +/* VK_EXT_host_image_copy: CPU tarafinda isl tiled memcpy kullaniyoruz. + * Power-of-two olmayan bpb'li formatlarda (orn. r32g32b32_sfloat) + * isl'in span matematigi tasma yapiyor, onlari disarida birakiyoruz. + */ + if ((flags & (VK_FORMAT_FEATURE_2_TRANSFER_SRC_BIT | + VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT)) && + util_is_power_of_two_or_zero(isl_format_layouts[base_isl_format].bpb)) + flags |= VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT; + return flags; } @@ -862,8 +885,9 @@ get_buffer_format_features2(const struct intel_device_info *devinfo, if (isl_format == ISL_FORMAT_R32_SINT || isl_format == ISL_FORMAT_R32_UINT) flags |= VK_FORMAT_FEATURE_2_STORAGE_TEXEL_BUFFER_ATOMIC_BIT; - if (isl_format_supports_typed_reads(devinfo, isl_format)) - flags |= VK_FORMAT_FEATURE_2_STORAGE_READ_WITHOUT_FORMAT_BIT; + /* No STORAGE_READ_WITHOUT_FORMAT here either, for the same reason as in + * anv_get_image_format_features2(). + */ if (isl_format_supports_typed_writes(devinfo, isl_format)) flags |= VK_FORMAT_FEATURE_2_STORAGE_WRITE_WITHOUT_FORMAT_BIT; @@ -1241,6 +1265,13 @@ anv_get_image_format_properties( } } + if (view_usage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT) { + if (!(format_feature_flags & + VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT)) { + goto unsupported; + } + } + if (view_usage & VK_IMAGE_USAGE_SAMPLED_BIT) { if (!(format_feature_flags & VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_BIT)) { goto unsupported; @@ -1493,12 +1524,16 @@ VkResult anv_GetPhysicalDeviceImageFormatProperties2( /* Extract output structs */ VkDrmFormatModifierPropertiesListEXT *modifier_props = NULL; VkDrmFormatModifierPropertiesList2EXT *modifier_props_2 = NULL; + VkHostImageCopyDevicePerformanceQueryEXT *host_props = NULL; vk_foreach_struct(s, base_props->pNext) { - switch (s->sType) { + switch ((unsigned)s->sType) { case VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES: external_props = (void *) s; break; + case VK_STRUCTURE_TYPE_HOST_IMAGE_COPY_DEVICE_PERFORMANCE_QUERY_EXT: + host_props = (void *) s; + break; case VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES: ycbcr_props = (void *) s; break; @@ -1680,6 +1715,16 @@ VkResult anv_GetPhysicalDeviceImageFormatProperties2( modifier_props_2); } + /* VK_EXT_host_image_copy performans sorgusu. Gen7'de host-transfer + * imajlari icin suboptimal bir yerlesim zorlamiyoruz (Tile64/Yf/Ys yok, + * bu formatlar aux'suz saklaniyor), dolayisiyla cihaz erisimi optimaldir. + * Bellek yerlesimi ancak LINEAR tiling'de host tarafiyla ayni olur. */ + if (host_props) { + host_props->optimalDeviceAccess = VK_TRUE; + host_props->identicalMemoryLayout = + base_info->tiling != VK_IMAGE_TILING_OPTIMAL; + } + return VK_SUCCESS; fail: diff --git a/src/intel/vulkan_hasvk/anv_genX.h b/src/intel/vulkan_hasvk/anv_genX.h index 69b320a..03db00c 100644 --- a/src/intel/vulkan_hasvk/anv_genX.h +++ b/src/intel/vulkan_hasvk/anv_genX.h @@ -69,8 +69,40 @@ void genX(cmd_buffer_update_dirty_vbs_for_gfx8_vb_flush)(struct anv_cmd_buffer * uint64_t vb_used); void genX(flush_pipeline_select_3d)(struct anv_cmd_buffer *cmd_buffer); +void genX(flush_pipeline_select_media)(struct anv_cmd_buffer *cmd_buffer); void genX(flush_pipeline_select_gpgpu)(struct anv_cmd_buffer *cmd_buffer); +/* Emits the MFC/MFX static state and per-MB PAK objects for one H.264 encode. + * Defined in anv_video_mfc.c (a separate TU due to the genX_pack.h / + * genX_video_pack.h include-guard collision; see that file's header). */ +/* Emits the frame-level MFC/MFX static state (everything up to but not + * including the slices). Call once per frame, before emitting any slice. */ +void genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer, + const struct anv_image *source_image, + struct anv_address recon_address, + const StdVideoH264SequenceParameterSet *sps, + const StdVideoH264PictureParameterSet *pps, + struct anv_address dst_address, + uint32_t dst_page_offset, + uint64_t dst_buffer_range, + uint32_t width_in_mbs, uint32_t height_in_mbs, + bool is_p, struct anv_address ref_address); + +/* Emits one slice: MFX_AVC_SLICE_STATE, the slice-header insert, the per-MB + * PAK objects for the macroblock range [slice_start_mb, slice_end_mb), and the + * slice tail. Call once per slice, after the frame-level static state. Intra + * neighbour availability is computed relative to slice_start_mb so it matches + * the decoder (intra prediction does not cross slice boundaries). */ +void genX(hasvk_mfc_emit_slice)(struct anv_cmd_buffer *cmd_buffer, + const StdVideoH264SequenceParameterSet *sps, + const StdVideoH264PictureParameterSet *pps, + const StdVideoEncodeH264PictureInfo *pic_info, + const StdVideoEncodeH264SliceHeader *slice_hdr, + uint32_t slice_start_mb, uint32_t slice_end_mb, + uint32_t width_in_mbs, uint32_t height_in_mbs, + uint32_t qp, bool is_last_slice, + uint32_t dst_page_offset); + enum anv_pipe_bits genX(emit_apply_pipe_flushes)(struct anv_batch *batch, struct anv_device *device, @@ -115,6 +147,11 @@ genX(emit_urb_setup)(struct anv_device *device, struct anv_batch *batch, VkShaderStageFlags active_stages, const unsigned entry_size[4]); +void +genX(emit_vertex_input)(struct anv_graphics_pipeline *pipeline, + struct anv_batch *batch, + const struct vk_vertex_input_state *vi); + void genX(emit_multisample)(struct anv_batch *batch, uint32_t samples, const struct vk_sample_locations_state *sl); @@ -165,3 +202,13 @@ void genX(emit_query_mi_flush_availability)(struct anv_cmd_buffer *cmd_buffer, struct anv_address addr, bool available); + +void +genX(emit_query_mi_write_u64)(struct anv_cmd_buffer *cmd_buffer, + struct anv_address addr, + uint64_t value); + +void +genX(emit_query_mi_write_reg32)(struct anv_cmd_buffer *cmd_buffer, + struct anv_address addr, + uint32_t reg); diff --git a/src/intel/vulkan_hasvk/anv_host_image_copy.c b/src/intel/vulkan_hasvk/anv_host_image_copy.c new file mode 100644 index 0000000..bf6b56c --- /dev/null +++ b/src/intel/vulkan_hasvk/anv_host_image_copy.c @@ -0,0 +1,429 @@ +/* + * VK_EXT_host_image_copy for hasvk (Gen7/7.5) + */ +#include "anv_private.h" +#include "isl/isl.h" +#include "util/u_math.h" +#include "util/os_memory.h" + +struct host_copy_map { + void *map; /* BO'nun CPU adresi */ + bool mapped_here; /* biz mi map ettik, unmap gerekiyor mu */ + struct anv_address addr; +}; + +static VkResult +host_copy_map_plane(struct anv_device *device, + const struct anv_image *image, + uint32_t plane, + struct host_copy_map *out) +{ + const struct anv_image_memory_range *range = + &image->planes[plane].primary_surface.memory_range; + + out->addr = anv_image_address(image, range); + if (out->addr.bo == NULL) + return VK_ERROR_MEMORY_MAP_FAILED; + + out->map = out->addr.bo->map; + out->mapped_here = false; + + if (out->map == NULL) { + VkResult r = anv_device_map_bo(device, out->addr.bo, 0, + out->addr.bo->size, 0, &out->map); + if (r != VK_SUCCESS) + return r; + out->mapped_here = true; + } + return VK_SUCCESS; +} + +static void +host_copy_unmap_plane(struct anv_device *device, struct host_copy_map *m) +{ + if (m->mapped_here) + anv_device_unmap_bo(device, m->addr.bo, m->map, m->addr.bo->size); +} + +/* crocus'taki get_image_offset_el() karsiligi: 3D ile array ayrimi */ +static void +host_copy_get_image_offset_el(const struct isl_surf *surf, + uint32_t level, uint32_t z, + uint32_t *out_x0_el, uint32_t *out_y0_el) +{ + ASSERTED uint32_t z0_el, a0_el; + + if (surf->dim == ISL_SURF_DIM_3D) { + isl_surf_get_image_offset_el(surf, level, 0, z, + out_x0_el, out_y0_el, &z0_el, &a0_el); + } else { + isl_surf_get_image_offset_el(surf, level, z, 0, + out_x0_el, out_y0_el, &z0_el, &a0_el); + } + + assert(z0_el == 0 && a0_el == 0); +} + +/* crocus'taki tile_extents() karsiligi: x'ler byte, y'ler element satiri */ +static void +host_copy_tile_extents(const struct isl_surf *surf, + const VkOffset3D *offset, + const VkExtent3D *extent, + uint32_t level, uint32_t layer, + uint32_t *x1_B, uint32_t *x2_B, + uint32_t *y1_el, uint32_t *y2_el) +{ + const struct isl_format_layout *fmtl = isl_format_get_layout(surf->format); + const unsigned cpp = fmtl->bpb / 8; + + uint32_t x0_el, y0_el; + host_copy_get_image_offset_el(surf, level, layer, &x0_el, &y0_el); + + assert(offset->x % fmtl->bw == 0); + assert(offset->y % fmtl->bh == 0); + + *x1_B = (offset->x / fmtl->bw + x0_el) * cpp; + *y1_el = offset->y / fmtl->bh + y0_el; + *x2_B = (DIV_ROUND_UP(offset->x + extent->width, fmtl->bw) + x0_el) * cpp; + *y2_el = DIV_ROUND_UP(offset->y + extent->height, fmtl->bh) + y0_el; +} + +/* isl'in tiled memcpy'si linear taraftan 16-byte hizali bir baslangic + * bekliyor (bkz. crocus_map_tiled_memcpy). Uygulamanin verdigi host + * pointer keyfi hizali olabilecegi icin arada hizali bir buffer + * kullaniyoruz. Buffer'i tam satir pitch'i uzerinden ve fazladan pay + * birakarak ayiriyoruz: isl'in "faster" kopyalayicilari 16 byte'lik + * bloklar halinde calisiyor ve bolge sonunu bir miktar asabiliyor. + */ +static void * +host_copy_alloc_bounce(uint32_t bounce_pitch, uint32_t rows, + uint32_t x1_B, uint8_t **bounce_out) +{ + void *base = os_malloc_aligned((size_t)bounce_pitch * (rows + 2) + 128, 16); + if (base == NULL) + return NULL; + + *bounce_out = (uint8_t *)base + (x1_B & 0xf); + return base; +} + +VkResult anv_CopyMemoryToImageEXT( + VkDevice _device, + const VkCopyMemoryToImageInfoEXT* pInfo) +{ + ANV_FROM_HANDLE(anv_device, device, _device); + ANV_FROM_HANDLE(anv_image, image, pInfo->dstImage); + + /* MEMCPY: host bellek image'in kendi (tiled) duzeninde; layout donusumu + * yapilmaz, tile araligi ham kopyalanir. */ + const bool raw_memcpy = + (pInfo->flags & VK_HOST_IMAGE_COPY_MEMCPY_BIT_EXT) != 0; + + for (uint32_t r = 0; r < pInfo->regionCount; r++) { + const VkMemoryToImageCopyEXT *region = &pInfo->pRegions[r]; + const uint32_t plane = + anv_image_aspect_to_plane(image, region->imageSubresource.aspectMask); + const struct isl_surf *surf = &image->planes[plane].primary_surface.isl; + const struct isl_format_layout *fmtl = isl_format_get_layout(surf->format); + const unsigned cpp = fmtl->bpb / 8; + + struct host_copy_map m; + VkResult res = host_copy_map_plane(device, image, plane, &m); + if (res != VK_SUCCESS) + return res; + + /* Host tarafindaki satir/dilim adimlari (texel -> byte) */ + const uint32_t row_len_texels = region->memoryRowLength ? + region->memoryRowLength : region->imageExtent.width; + const uint32_t img_h_texels = region->memoryImageHeight ? + region->memoryImageHeight : region->imageExtent.height; + const uint32_t host_pitch_B = DIV_ROUND_UP(row_len_texels, fmtl->bw) * cpp; + const uint32_t host_slice_B = + host_pitch_B * DIV_ROUND_UP(img_h_texels, fmtl->bh); + + const uint32_t layer_count = + vk_image_subresource_layer_count(&image->vk, ®ion->imageSubresource); + const uint32_t slices = MAX2(layer_count, region->imageExtent.depth); + + for (uint32_t s = 0; s < slices; s++) { + const uint32_t slice_idx = + (surf->dim == ISL_SURF_DIM_3D) ? + (region->imageOffset.z + s) : + (region->imageSubresource.baseArrayLayer + s); + + uint32_t x1_B, x2_B, y1_el, y2_el; + host_copy_tile_extents(surf, ®ion->imageOffset, + ®ion->imageExtent, + region->imageSubresource.mipLevel, + slice_idx, + &x1_B, &x2_B, &y1_el, &y2_el); + + const uint8_t *host_slice = + (const uint8_t *)region->pHostPointer + s * host_slice_B; + const uint32_t rows = y2_el - y1_el; + const uint32_t row_bytes = x2_B - x1_B; + + /* MEMCPY hizli yolu: slice benzersiz tile'lara denk geliyorsa host + * bellek (zaten tiled) dogrudan image BO'ya ham kopyalanir. */ + if (raw_memcpy) { + uint64_t start_tile_B, end_tile_B; + if (isl_surf_image_has_unique_tiles( + surf, region->imageSubresource.mipLevel, + slice_idx, 1, &start_tile_B, &end_tile_B)) { + memcpy((uint8_t *)m.map + m.addr.offset + start_tile_B, + host_slice, end_tile_B - start_tile_B); + continue; + } + } + + if (surf->tiling == ISL_TILING_LINEAR) { + uint8_t *dst = (uint8_t *)m.map + m.addr.offset; + for (uint32_t y = 0; y < rows; y++) { + memcpy(dst + (y1_el + y) * surf->row_pitch_B + x1_B, + host_slice + y * host_pitch_B, + row_bytes); + } + continue; + } + + const uint32_t bounce_pitch = align(surf->row_pitch_B, 16); + uint8_t *bounce; + void *bounce_base = + host_copy_alloc_bounce(bounce_pitch, rows, x1_B, &bounce); + if (bounce_base == NULL) { + host_copy_unmap_plane(device, &m); + return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); + } + + /* Once host -> bounce, sonra bounce -> tiled image */ + for (uint32_t y = 0; y < rows; y++) { + memcpy(bounce + y * bounce_pitch, + host_slice + y * host_pitch_B, + row_bytes); + } + + isl_memcpy_linear_to_tiled(x1_B, x2_B, y1_el, y2_el, + (char *)m.map + m.addr.offset, /* dst: tiled */ + (const char *)bounce, /* src: linear */ + surf->row_pitch_B, /* dst_pitch */ + bounce_pitch, /* src_pitch */ + device->isl_dev.has_bit6_swizzling, + surf->tiling, ISL_MEMCPY); + + os_free_aligned(bounce_base); + } + + host_copy_unmap_plane(device, &m); + } + + return VK_SUCCESS; +} + +VkResult anv_CopyImageToMemoryEXT( + VkDevice _device, + const VkCopyImageToMemoryInfoEXT* pInfo) +{ + ANV_FROM_HANDLE(anv_device, device, _device); + ANV_FROM_HANDLE(anv_image, image, pInfo->srcImage); + + /* MEMCPY: bkz. anv_CopyMemoryToImageEXT */ + const bool raw_memcpy = + (pInfo->flags & VK_HOST_IMAGE_COPY_MEMCPY_BIT_EXT) != 0; + + for (uint32_t r = 0; r < pInfo->regionCount; r++) { + const VkImageToMemoryCopyEXT *region = &pInfo->pRegions[r]; + const uint32_t plane = + anv_image_aspect_to_plane(image, region->imageSubresource.aspectMask); + const struct isl_surf *surf = &image->planes[plane].primary_surface.isl; + const struct isl_format_layout *fmtl = isl_format_get_layout(surf->format); + const unsigned cpp = fmtl->bpb / 8; + + struct host_copy_map m; + VkResult res = host_copy_map_plane(device, image, plane, &m); + if (res != VK_SUCCESS) + return res; + + const uint32_t row_len_texels = region->memoryRowLength ? + region->memoryRowLength : region->imageExtent.width; + const uint32_t img_h_texels = region->memoryImageHeight ? + region->memoryImageHeight : region->imageExtent.height; + const uint32_t host_pitch_B = DIV_ROUND_UP(row_len_texels, fmtl->bw) * cpp; + const uint32_t host_slice_B = + host_pitch_B * DIV_ROUND_UP(img_h_texels, fmtl->bh); + + const uint32_t layer_count = + vk_image_subresource_layer_count(&image->vk, ®ion->imageSubresource); + const uint32_t slices = MAX2(layer_count, region->imageExtent.depth); + + for (uint32_t s = 0; s < slices; s++) { + const uint32_t slice_idx = + (surf->dim == ISL_SURF_DIM_3D) ? + (region->imageOffset.z + s) : + (region->imageSubresource.baseArrayLayer + s); + + uint32_t x1_B, x2_B, y1_el, y2_el; + host_copy_tile_extents(surf, ®ion->imageOffset, + ®ion->imageExtent, + region->imageSubresource.mipLevel, + slice_idx, + &x1_B, &x2_B, &y1_el, &y2_el); + + uint8_t *host_slice = + (uint8_t *)region->pHostPointer + s * host_slice_B; + const uint32_t rows = y2_el - y1_el; + const uint32_t row_bytes = x2_B - x1_B; + + /* MEMCPY hizli yolu: image BO'dan (tiled) host bellege ham kopya. */ + if (raw_memcpy) { + uint64_t start_tile_B, end_tile_B; + if (isl_surf_image_has_unique_tiles( + surf, region->imageSubresource.mipLevel, + slice_idx, 1, &start_tile_B, &end_tile_B)) { + memcpy(host_slice, + (const uint8_t *)m.map + m.addr.offset + start_tile_B, + end_tile_B - start_tile_B); + continue; + } + } + + if (surf->tiling == ISL_TILING_LINEAR) { + const uint8_t *src = (const uint8_t *)m.map + m.addr.offset; + for (uint32_t y = 0; y < rows; y++) { + memcpy(host_slice + y * host_pitch_B, + src + (y1_el + y) * surf->row_pitch_B + x1_B, + row_bytes); + } + continue; + } + + const uint32_t bounce_pitch = align(surf->row_pitch_B, 16); + uint8_t *bounce; + void *bounce_base = + host_copy_alloc_bounce(bounce_pitch, rows, x1_B, &bounce); + if (bounce_base == NULL) { + host_copy_unmap_plane(device, &m); + return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); + } + + /* Once tiled image -> bounce, sonra bounce -> host */ + isl_memcpy_tiled_to_linear(x1_B, x2_B, y1_el, y2_el, + (char *)bounce, /* dst: linear */ + (const char *)m.map + m.addr.offset, /* src: tiled */ + bounce_pitch, /* dst_pitch */ + surf->row_pitch_B, /* src_pitch */ + device->isl_dev.has_bit6_swizzling, + surf->tiling, ISL_MEMCPY); + + for (uint32_t y = 0; y < rows; y++) { + memcpy(host_slice + y * host_pitch_B, + bounce + y * bounce_pitch, + row_bytes); + } + + os_free_aligned(bounce_base); + } + + host_copy_unmap_plane(device, &m); + } + + return VK_SUCCESS; +} + +VkResult anv_CopyImageToImageEXT( + VkDevice _device, + const VkCopyImageToImageInfoEXT* pInfo) +{ + ANV_FROM_HANDLE(anv_device, device, _device); + ANV_FROM_HANDLE(anv_image, src_image, pInfo->srcImage); + + for (uint32_t r = 0; r < pInfo->regionCount; r++) { + const VkImageCopy2 *region = &pInfo->pRegions[r]; + const uint32_t plane = + anv_image_aspect_to_plane(src_image, + region->srcSubresource.aspectMask); + const struct isl_surf *surf = + &src_image->planes[plane].primary_surface.isl; + const struct isl_format_layout *fmtl = isl_format_get_layout(surf->format); + const unsigned cpp = fmtl->bpb / 8; + + /* Ara buffer'in duzeni, iki entrypoint'in memoryRowLength == 0 + * durumunda kullandigi duzenle birebir ayni olmali. + */ + const uint32_t w_el = DIV_ROUND_UP(region->extent.width, fmtl->bw); + const uint32_t h_el = DIV_ROUND_UP(region->extent.height, fmtl->bh); + const uint32_t pitch = w_el * cpp; + const uint32_t layers = + vk_image_subresource_layer_count(&src_image->vk, + ®ion->srcSubresource); + const uint32_t slices = MAX2(layers, region->extent.depth); + const size_t tmp_size = (size_t)pitch * h_el * slices; + + void *tmp = os_malloc_aligned(tmp_size, 16); + if (tmp == NULL) + return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); + + const VkImageToMemoryCopyEXT out_region = { + .sType = VK_STRUCTURE_TYPE_IMAGE_TO_MEMORY_COPY_EXT, + .pHostPointer = tmp, + .memoryRowLength = 0, + .memoryImageHeight = 0, + .imageSubresource = region->srcSubresource, + .imageOffset = region->srcOffset, + .imageExtent = region->extent, + }; + /* Ara buffer bizim sectigimiz sikisik linear duzende; MEMCPY (tiled ham + * kopya) bayragini ic transferlere tasimayiz, aksi halde tiled boyut + * linear boyutlu tmp'yi tasirir. */ + const VkCopyImageToMemoryInfoEXT out_info = { + .sType = VK_STRUCTURE_TYPE_COPY_IMAGE_TO_MEMORY_INFO_EXT, + .flags = 0, + .srcImage = pInfo->srcImage, + .srcImageLayout = pInfo->srcImageLayout, + .regionCount = 1, + .pRegions = &out_region, + }; + + VkResult res = anv_CopyImageToMemoryEXT(_device, &out_info); + if (res != VK_SUCCESS) { + os_free_aligned(tmp); + return res; + } + + const VkMemoryToImageCopyEXT in_region = { + .sType = VK_STRUCTURE_TYPE_MEMORY_TO_IMAGE_COPY_EXT, + .pHostPointer = tmp, + .memoryRowLength = 0, + .memoryImageHeight = 0, + .imageSubresource = region->dstSubresource, + .imageOffset = region->dstOffset, + .imageExtent = region->extent, + }; + const VkCopyMemoryToImageInfoEXT in_info = { + .sType = VK_STRUCTURE_TYPE_COPY_MEMORY_TO_IMAGE_INFO_EXT, + .flags = 0, + .dstImage = pInfo->dstImage, + .dstImageLayout = pInfo->dstImageLayout, + .regionCount = 1, + .pRegions = &in_region, + }; + + res = anv_CopyMemoryToImageEXT(_device, &in_info); + os_free_aligned(tmp); + if (res != VK_SUCCESS) + return res; + } + + return VK_SUCCESS; +} + +VkResult anv_TransitionImageLayoutEXT( + VkDevice device, + uint32_t transitionCount, + const VkHostImageLayoutTransitionInfoEXT* pTransitions) +{ + /* Host copy icin yalnizca aux surface'i olmayan layout'lar + * (VK_IMAGE_LAYOUT_GENERAL) raporlaniyor, dolayisiyla burada + * yapilacak bir is yok. + */ + return VK_SUCCESS; +} \ No newline at end of file diff --git a/src/intel/vulkan_hasvk/anv_image.c b/src/intel/vulkan_hasvk/anv_image.c index 054662f..345f983 100644 --- a/src/intel/vulkan_hasvk/anv_image.c +++ b/src/intel/vulkan_hasvk/anv_image.c @@ -1926,7 +1926,8 @@ VkResult anv_BindImageMemory2( } static void -anv_get_image_subresource_layout(const struct anv_image *image, +anv_get_image_subresource_layout(struct anv_device *device, + const struct anv_image *image, const VkImageSubresource2KHR *subresource, VkSubresourceLayout2KHR *layout) { @@ -2012,14 +2013,49 @@ anv_get_image_subresource_layout(const struct anv_image *image, } else { layout->subresourceLayout.size = surface->memory_range.size; } + + /* VK_EXT_host_image_copy: MEMCPY kopyalari icin uygulamanin ayirmasi + * gereken host bellek boyutu. Benzersiz tile'lara denk gelen subresource + * icin ham tile araligi; aksi halde linear yerlesimin boyutu. */ + VkSubresourceHostMemcpySizeEXT *host_memcpy_size = + vk_find_struct(layout->pNext, SUBRESOURCE_HOST_MEMCPY_SIZE_EXT); + if (host_memcpy_size) { + const struct isl_surf *isl_surf = &surface->isl; + const uint32_t level = subresource->imageSubresource.mipLevel; + const uint32_t layer = subresource->imageSubresource.arrayLayer; + const uint32_t layers = u_minify(isl_surf->logical_level0_px.d, level); + uint64_t start_tile_B, end_tile_B; + + if (isl_surf_image_has_unique_tiles(isl_surf, level, layer, layers, + &start_tile_B, &end_tile_B)) { + host_memcpy_size->size = end_tile_B - start_tile_B; + } else { + /* Ham memcpy mumkun degilse veriyi linear duzende sakliyoruz. */ + struct isl_surf lin_surf; + ASSERTED bool ok = + isl_surf_init(&device->isl_dev, &lin_surf, + .dim = isl_surf->dim, + .format = isl_surf->format, + .width = u_minify(isl_surf->logical_level0_px.w, level), + .height = u_minify(isl_surf->logical_level0_px.h, level), + .depth = u_minify(isl_surf->logical_level0_px.d, level), + .array_len = 1, + .levels = 1, + .samples = isl_surf->samples, + .tiling_flags = ISL_TILING_LINEAR_BIT); + assert(ok); + host_memcpy_size->size = lin_surf.size_B; + } + } } void anv_GetImageSubresourceLayout( - VkDevice device, + VkDevice _device, VkImage _image, const VkImageSubresource* pSubresource, VkSubresourceLayout* pLayout) { + ANV_FROM_HANDLE(anv_device, device, _device); ANV_FROM_HANDLE(anv_image, image, _image); VkImageSubresource2KHR subresource = { @@ -2029,7 +2065,7 @@ void anv_GetImageSubresourceLayout( VkSubresourceLayout2KHR layout = { .sType = VK_STRUCTURE_TYPE_SUBRESOURCE_LAYOUT_2_KHR }; - anv_get_image_subresource_layout(image, &subresource, &layout); + anv_get_image_subresource_layout(device, image, &subresource, &layout); *pLayout = layout.subresourceLayout; } @@ -2048,18 +2084,19 @@ void anv_GetDeviceImageSubresourceLayoutKHR( return; } - anv_get_image_subresource_layout(&image, pInfo->pSubresource, pLayout); + anv_get_image_subresource_layout(device, &image, pInfo->pSubresource, pLayout); } void anv_GetImageSubresourceLayout2KHR( - VkDevice device, + VkDevice _device, VkImage _image, const VkImageSubresource2KHR* pSubresource, VkSubresourceLayout2KHR* pLayout) { + ANV_FROM_HANDLE(anv_device, device, _device); ANV_FROM_HANDLE(anv_image, image, _image); - anv_get_image_subresource_layout(image, pSubresource, pLayout); + anv_get_image_subresource_layout(device, image, pSubresource, pLayout); } /** diff --git a/src/intel/vulkan_hasvk/anv_pipeline.c b/src/intel/vulkan_hasvk/anv_pipeline.c index 5bc8164..50922ce 100644 --- a/src/intel/vulkan_hasvk/anv_pipeline.c +++ b/src/intel/vulkan_hasvk/anv_pipeline.c @@ -531,6 +531,8 @@ anv_pipeline_lower_nir(struct anv_pipeline *pipeline, .lower_stores = true, .lower_atomics = true, .lower_get_size = true, + .robust_image_access = + stage->key.base.robust_flags & ELK_ROBUSTNESS_IMAGE, }); NIR_PASS(_, nir, nir_lower_explicit_io, nir_var_mem_global, @@ -1058,15 +1060,50 @@ anv_pipeline_add_executables(struct anv_pipeline *pipeline, } } +/* VK_EXT_pipeline_robustness: derive the compiler's UBO/SSBO robustness bits + * from a per-pipeline/-stage robustness state. The elk backend only knows + * about UBO and SSBO robustness; image and vertex-input robustness are handled + * by the hardware (always robust on Intel), which is a spec-legal superset of + * any requested behavior. */ static enum elk_robustness_flags -anv_device_get_robust_flags(const struct anv_device *device) +anv_get_robust_flags(const struct vk_pipeline_robustness_state *rs) { - return device->robust_buffer_access ? - (ELK_ROBUSTNESS_UBO | ELK_ROBUSTNESS_SSBO) : 0; + /* A null descriptor is realized through the same bounds-checked address + * format on Gen7, so it also requires the robust flag (matching the old + * robust_buffer_access = robustBufferAccess || nullDescriptor coupling). */ + return + (((rs->storage_buffers != + VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED_EXT) || + rs->null_storage_buffer_descriptor) ? ELK_ROBUSTNESS_SSBO : 0) | + (((rs->uniform_buffers != + VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DISABLED_EXT) || + rs->null_uniform_buffer_descriptor) ? ELK_ROBUSTNESS_UBO : 0); +} + +/* Resolve the effective robustness for one shader stage, honoring any + * VkPipelineRobustnessCreateInfoEXT on the pipeline or the stage, falling back + * to the device default. */ +static enum elk_robustness_flags +anv_stage_get_robust_flags(const struct anv_device *device, + const void *pipeline_pNext, + const void *stage_pNext) +{ + struct vk_pipeline_robustness_state rs; + vk_pipeline_robustness_state_fill(&device->vk.robustness_state, &rs, + pipeline_pNext, stage_pNext); + + /* Image robustness has no per-pipeline knob: nullDescriptor covers image + * descriptors device-wide, and gfx7 needs shader-side bounds checking to + * honour it (the null surface still has one live texel). + */ + return anv_get_robust_flags(&rs) | + (device->vk.enabled_features.nullDescriptor ? + ELK_ROBUSTNESS_IMAGE : 0); } static void anv_graphics_pipeline_init_keys(struct anv_graphics_pipeline *pipeline, + const void *pipeline_pNext, const struct vk_graphics_pipeline_state *state, struct anv_pipeline_stage *stages) { @@ -1080,7 +1117,9 @@ anv_graphics_pipeline_init_keys(struct anv_graphics_pipeline *pipeline, NULL, stages[s].shader_blake3); const struct anv_device *device = pipeline->base.device; - enum elk_robustness_flags robust_flags = anv_device_get_robust_flags(device); + enum elk_robustness_flags robust_flags = + anv_stage_get_robust_flags(device, pipeline_pNext, + stages[s].info->pNext); switch (stages[s].stage) { case MESA_SHADER_VERTEX: populate_vs_prog_key(device, @@ -1265,7 +1304,7 @@ anv_graphics_pipeline_compile(struct anv_graphics_pipeline *pipeline, stages[stage].info = &info->pStages[i]; } - anv_graphics_pipeline_init_keys(pipeline, state, stages); + anv_graphics_pipeline_init_keys(pipeline, info->pNext, state, stages); unsigned char blake3[BLAKE3_KEY_LEN]; anv_pipeline_hash_graphics(pipeline, layout, stages, blake3); @@ -1496,7 +1535,8 @@ anv_pipeline_compile_cs(struct anv_compute_pipeline *pipeline, struct anv_shader_bin *bin = NULL; populate_cs_prog_key(device, - anv_device_get_robust_flags(device), + anv_stage_get_robust_flags(device, info->pNext, + info->stage.pNext), &stage.key.cs); ANV_FROM_HANDLE(anv_pipeline_layout, layout, info->layout); @@ -1796,16 +1836,25 @@ anv_graphics_pipeline_init(struct anv_graphics_pipeline *pipeline, const uint64_t inputs_read = get_vs_prog_data(pipeline)->inputs_read; - u_foreach_bit(a, state->vi->attributes_valid) { - if (inputs_read & BITFIELD64_BIT(VERT_ATTRIB_GENERIC0 + a)) - pipeline->vb_used |= BITFIELD64_BIT(state->vi->attributes[a].binding); - } + /* With VK_EXT_vertex_input_dynamic_state the vertex input is not known + * until draw time, so state->vi is NULL and all of this is recomputed from + * the dynamic state in genX(cmd_buffer_flush_vertex_input). + */ + pipeline->dynamic_vertex_input = + BITSET_TEST(state->dynamic, MESA_VK_DYNAMIC_VI); - u_foreach_bit(b, state->vi->bindings_valid) { - pipeline->vb[b].stride = state->vi->bindings[b].stride; - pipeline->vb[b].instanced = state->vi->bindings[b].input_rate == - VK_VERTEX_INPUT_RATE_INSTANCE; - pipeline->vb[b].instance_divisor = state->vi->bindings[b].divisor; + if (state->vi != NULL) { + u_foreach_bit(a, state->vi->attributes_valid) { + if (inputs_read & BITFIELD64_BIT(VERT_ATTRIB_GENERIC0 + a)) + pipeline->vb_used |= BITFIELD64_BIT(state->vi->attributes[a].binding); + } + + u_foreach_bit(b, state->vi->bindings_valid) { + pipeline->vb[b].stride = state->vi->bindings[b].stride; + pipeline->vb[b].instanced = state->vi->bindings[b].input_rate == + VK_VERTEX_INPUT_RATE_INSTANCE; + pipeline->vb[b].instance_divisor = state->vi->bindings[b].divisor; + } } pipeline->instance_multiplier = 1; diff --git a/src/intel/vulkan_hasvk/anv_private.h b/src/intel/vulkan_hasvk/anv_private.h index 9e82245..9c7a1c2 100644 --- a/src/intel/vulkan_hasvk/anv_private.h +++ b/src/intel/vulkan_hasvk/anv_private.h @@ -103,7 +103,6 @@ struct anv_buffer_view; struct anv_image; struct anv_image_view; struct anv_instance; - struct intel_perf_config; struct intel_perf_counter_pass; struct intel_perf_query_result; @@ -806,7 +805,7 @@ struct anv_queue_family { enum intel_engine_class engine_class; }; -#define ANV_MAX_QUEUE_FAMILIES 3 +#define ANV_MAX_QUEUE_FAMILIES 4 struct anv_memory_type { /* Standard bits passed on to the client */ @@ -944,6 +943,7 @@ struct anv_instance { enum anv_debug { ANV_DEBUG_VIDEO_DECODE = BITFIELD_BIT(0), ANV_DEBUG_VIDEO_DEBUG = BITFIELD_BIT(1), + ANV_DEBUG_VIDEO_ENCODE = BITFIELD_BIT(2), }; extern enum anv_debug anv_debug; @@ -1872,7 +1872,7 @@ anv_descriptor_set_write_buffer_view(struct anv_device *device, uint32_t binding, uint32_t element); -void +VkResult anv_descriptor_set_write_buffer(struct anv_device *device, struct anv_descriptor_set *set, struct anv_state_stream *alloc_stream, @@ -1891,7 +1891,7 @@ anv_descriptor_set_write_inline_uniform_data(struct anv_device *device, size_t offset, size_t size); -void +VkResult anv_descriptor_set_write_template(struct anv_device *device, struct anv_descriptor_set *set, struct anv_state_stream *alloc_stream, @@ -2079,6 +2079,14 @@ anv_pipe_flush_bits_for_access_flags(struct anv_device *device, */ pipe_bits |= ANV_PIPE_HDC_PIPELINE_FLUSH_BIT; pipe_bits |= ANV_PIPE_UNTYPED_DATAPORT_CACHE_FLUSH_BIT; + /* On gfx7, typed surface writes (image stores) are serviced by the + * render cache rather than the data cache, so it has to be flushed + * as well or the writes stay invisible to subsequent operations. + * This only shows up from the fragment stage, where the render cache + * is actually in use. + */ + if (device->info->ver <= 7) + pipe_bits |= ANV_PIPE_RENDER_TARGET_CACHE_FLUSH_BIT; break; case VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT: /* We're transitioning a buffer that was previously used as render @@ -2481,6 +2489,13 @@ struct anv_cmd_graphics_state { struct vk_sample_locations_state sample_locations; + /** Backing storage for vk_dynamic_graphics_state::vi + * + * The common runtime requires the driver to provide this whenever + * VK_EXT_vertex_input_dynamic_state is supported. + */ + struct vk_vertex_input_state vertex_input; + bool has_uint_rt; }; @@ -2541,6 +2556,11 @@ struct anv_cmd_state { bool hiz_enabled; bool conditional_render_enabled; + /* Gen7: buffer address + inverted flag, re-evaluated before every draw + * since IVB has no GPR to latch a precomputed result like Gfx7.5+ does. + */ + struct anv_address conditional_render_value_addr; + bool conditional_render_inverted; /** * Last rendering scale argument provided to @@ -2651,6 +2671,12 @@ struct anv_cmd_buffer { struct { struct anv_video_session *vid; struct anv_video_session_params *params; + + /* ENCODE — destination for VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR. + * Carried from CmdBeginQueryIndexedEXT to CmdEncodeVideoKHR, since only + * the encode command knows the final bitstream offset/size and does the + * actual write. NULL bo means no active feedback query. */ + struct anv_address encode_feedback_query_addr; } video; }; @@ -2874,6 +2900,13 @@ struct anv_graphics_pipeline { bool force_fragment_thread_dispatch; bool negative_one_to_one; + /** True if VK_DYNAMIC_STATE_VERTEX_INPUT_EXT is in use. + * + * When set, vb_used and vb[] below are not filled in at pipeline-creation + * time; the vertex input is emitted from the command buffer instead. + */ + bool dynamic_vertex_input; + uint32_t vb_used; struct anv_pipeline_vertex_binding { uint32_t stride; @@ -3795,6 +3828,16 @@ enum { ANV_VID_MEM_H264_DEBLOCK_FILTER_ROW_STORE, ANV_VID_MEM_H264_BSD_MPC_ROW_SCRATCH, ANV_VID_MEM_H264_MPR_ROW_SCRATCH, + /* Encode-only scratch buffers (MB status, MFX_AVC_DIRECTMODE_STATE MV + * buffers). Exposed as separate memoryBindIndex values and bound through + * the same path as decode. */ + ANV_VID_MEM_H264_ENC_MB_STATUS, + ANV_VID_MEM_H264_ENC_DIRECT_MV_TOP, + ANV_VID_MEM_H264_ENC_DIRECT_MV_BOTTOM, + /* Input motion-vector data read by the PAK for inter/P macroblocks + * (MFX_IND_OBJ_BASE_ADDR_STATE MFXIndirectMVObjectAddress). Kept separate + * from the DIRECTMODE write buffers so the PAK does not clobber it. */ + ANV_VID_MEM_H264_ENC_INPUT_MV, ANV_VID_MEM_H264_MAX, }; diff --git a/src/intel/vulkan_hasvk/anv_video.c b/src/intel/vulkan_hasvk/anv_video.c index fc57e2b..0607333 100644 --- a/src/intel/vulkan_hasvk/anv_video.c +++ b/src/intel/vulkan_hasvk/anv_video.c @@ -28,6 +28,7 @@ #include "vk_common_entrypoints.h" #include "vk_video/vulkan_video_codecs_common.h" #include "vk_video/vulkan_video_codec_h264std.h" +#include "vk_video/vulkan_video_codec_h264std_encode.h" VkResult anv_CreateVideoSessionKHR(VkDevice _device, @@ -114,25 +115,59 @@ anv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkVideoCapabilitiesKHR *pCapabilities) { - if (pVideoProfile->videoCodecOperation != VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR) { + const bool is_encode = + pVideoProfile->videoCodecOperation == VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR; + + if (pVideoProfile->videoCodecOperation != VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR && + !is_encode) { return VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR; } - const VkVideoDecodeH264ProfileInfoKHR *h264_profile = - vk_find_struct_const(pVideoProfile->pNext, VIDEO_DECODE_H264_PROFILE_INFO_KHR); - if (h264_profile) { - switch (h264_profile->stdProfileIdc) { - case STD_VIDEO_H264_PROFILE_IDC_BASELINE: - case STD_VIDEO_H264_PROFILE_IDC_MAIN: - case STD_VIDEO_H264_PROFILE_IDC_HIGH: - break; - default: - return VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR; + if (pVideoProfile->chromaSubsampling != VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR || + pVideoProfile->lumaBitDepth != VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR || + pVideoProfile->chromaBitDepth != VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR) + return VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR; + + if (!is_encode) { + const VkVideoDecodeH264ProfileInfoKHR *h264_profile = + vk_find_struct_const(pVideoProfile->pNext, VIDEO_DECODE_H264_PROFILE_INFO_KHR); + if (h264_profile) { + switch (h264_profile->stdProfileIdc) { + case STD_VIDEO_H264_PROFILE_IDC_BASELINE: + case STD_VIDEO_H264_PROFILE_IDC_MAIN: + case STD_VIDEO_H264_PROFILE_IDC_HIGH: + break; + default: + return VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR; + } + } + } else { + /* Baseline and Main are supported. Main is accepted so that CABAC + * (entropy_coding_mode_flag == 1, forbidden in Baseline) can be selected; + * the PAK performs the entropy coding. Main's inter-prediction features + * (B slices) are still not emitted — only I frames are produced. High is + * rejected (8x8 transform / scaling lists not emitted). */ + const VkVideoEncodeH264ProfileInfoKHR *h264_profile = + vk_find_struct_const(pVideoProfile->pNext, VIDEO_ENCODE_H264_PROFILE_INFO_KHR); + if (h264_profile) { + switch (h264_profile->stdProfileIdc) { + case STD_VIDEO_H264_PROFILE_IDC_BASELINE: + case STD_VIDEO_H264_PROFILE_IDC_MAIN: + break; + default: + return VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR; + } } } - pCapabilities->minBitstreamBufferOffsetAlignment = 32; - pCapabilities->minBitstreamBufferSizeAlignment = 32; + if (is_encode) { + pCapabilities->minBitstreamBufferOffsetAlignment = 4096; + pCapabilities->minBitstreamBufferSizeAlignment = 4096; + } else { + pCapabilities->minBitstreamBufferOffsetAlignment = 32; + pCapabilities->minBitstreamBufferSizeAlignment = 32; + } + pCapabilities->pictureAccessGranularity.width = ANV_MB_WIDTH; pCapabilities->pictureAccessGranularity.height = ANV_MB_HEIGHT; pCapabilities->minCodedExtent.width = ANV_MB_WIDTH; @@ -142,29 +177,87 @@ anv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice, pCapabilities->flags = VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR; - struct VkVideoDecodeCapabilitiesKHR *dec_caps = - (struct VkVideoDecodeCapabilitiesKHR *) - vk_find_struct(pCapabilities->pNext, VIDEO_DECODE_CAPABILITIES_KHR); - if (dec_caps) - dec_caps->flags = - VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR; + if (!is_encode) { + struct VkVideoDecodeCapabilitiesKHR *dec_caps = + (struct VkVideoDecodeCapabilitiesKHR *) + vk_find_struct(pCapabilities->pNext, VIDEO_DECODE_CAPABILITIES_KHR); + if (dec_caps) + dec_caps->flags = + VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR; - struct VkVideoDecodeH264CapabilitiesKHR *ext = - (struct VkVideoDecodeH264CapabilitiesKHR *) - vk_find_struct(pCapabilities->pNext, - VIDEO_DECODE_H264_CAPABILITIES_KHR); - pCapabilities->maxDpbSlots = 16; - pCapabilities->maxActiveReferencePictures = 16; + struct VkVideoDecodeH264CapabilitiesKHR *ext = + (struct VkVideoDecodeH264CapabilitiesKHR *) + vk_find_struct(pCapabilities->pNext, + VIDEO_DECODE_H264_CAPABILITIES_KHR); + pCapabilities->maxDpbSlots = 16; + pCapabilities->maxActiveReferencePictures = 16; - if (ext) { - ext->fieldOffsetGranularity.x = 0; - ext->fieldOffsetGranularity.y = 0; - ext->maxLevelIdc = 51; + if (ext) { + ext->fieldOffsetGranularity.x = 0; + ext->fieldOffsetGranularity.y = 0; + ext->maxLevelIdc = 51; + } + strcpy(pCapabilities->stdHeaderVersion.extensionName, + VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_EXTENSION_NAME); + pCapabilities->stdHeaderVersion.specVersion = + VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_SPEC_VERSION; + } else { + /* Limits reflecting what the encoder emits: I and P frames, CQP, single + * reference. P frames use one reference (the previous reconstructed + * frame) plus the current setup slot, so two DPB slots and one active + * reference. B frames and multi-reference are not implemented. */ + pCapabilities->maxDpbSlots = 2; + pCapabilities->maxActiveReferencePictures = 1; + + struct VkVideoEncodeCapabilitiesKHR *enc_caps = + (struct VkVideoEncodeCapabilitiesKHR *) + vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_CAPABILITIES_KHR); + if (enc_caps) { + enc_caps->flags = 0; + /* Constant QP only; no CBR/VBR rate control. */ + enc_caps->rateControlModes = + VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DISABLED_BIT_KHR; + enc_caps->maxRateControlLayers = 1; + enc_caps->maxBitrate = 0; + enc_caps->maxQualityLevels = 1; + enc_caps->encodeInputPictureGranularity.width = ANV_MB_WIDTH; + enc_caps->encodeInputPictureGranularity.height = ANV_MB_HEIGHT; + /* Both flags are required by the ffmpeg h264_vulkan encoder. Note + * that bytes-written currently reports the full dstBufferRange rather + * than the real encoded size; reading back the MFC bit counter is not + * yet implemented. */ + enc_caps->supportedEncodeFeedbackFlags = + VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BUFFER_OFFSET_BIT_KHR | + VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BYTES_WRITTEN_BIT_KHR; + } + + struct VkVideoEncodeH264CapabilitiesKHR *h264_enc_ext = + (struct VkVideoEncodeH264CapabilitiesKHR *) + vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_H264_CAPABILITIES_KHR); + if (h264_enc_ext) { + h264_enc_ext->flags = + VK_VIDEO_ENCODE_H264_CAPABILITY_PER_SLICE_CONSTANT_QP_BIT_KHR; + h264_enc_ext->maxLevelIdc = STD_VIDEO_H264_LEVEL_IDC_4_1; + /* One slice per MB row at most (matches the encoder's row-granular + * slice splitting). */ + h264_enc_ext->maxSliceCount = 68; /* 1088/16, covers up to 1080p */ + h264_enc_ext->maxPPictureL0ReferenceCount = 1; + h264_enc_ext->maxBPictureL0ReferenceCount = 0; + h264_enc_ext->maxL1ReferenceCount = 0; + h264_enc_ext->maxTemporalLayerCount = 1; + h264_enc_ext->expectDyadicTemporalLayerPattern = VK_FALSE; + h264_enc_ext->minQp = 0; + h264_enc_ext->maxQp = 51; + h264_enc_ext->prefersGopRemainingFrames = VK_FALSE; + h264_enc_ext->requiresGopRemainingFrames = VK_FALSE; + h264_enc_ext->stdSyntaxFlags = 0; + } + + strcpy(pCapabilities->stdHeaderVersion.extensionName, + VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_EXTENSION_NAME); + pCapabilities->stdHeaderVersion.specVersion = + VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_SPEC_VERSION; } - strcpy(pCapabilities->stdHeaderVersion.extensionName, - VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_EXTENSION_NAME); - pCapabilities->stdHeaderVersion.specVersion = - VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_SPEC_VERSION; return VK_SUCCESS; } @@ -224,6 +317,8 @@ get_h264_video_mem_size(struct anv_video_session *vid, uint32_t mem_idx) { uint32_t width_in_mb = align(vid->vk.max_coded.width, ANV_MB_WIDTH) / ANV_MB_WIDTH; + uint32_t height_in_mb = + align(vid->vk.max_coded.height, ANV_MB_HEIGHT) / ANV_MB_HEIGHT; switch (mem_idx) { case ANV_VID_MEM_H264_INTRA_ROW_STORE: @@ -238,6 +333,15 @@ get_h264_video_mem_size(struct anv_video_session *vid, uint32_t mem_idx) case ANV_VID_MEM_H264_MPR_ROW_SCRATCH: /* mpr row scratch is width in macroblocks * 64 * 2 */ return width_in_mb * 64 * 2; + /* Encode scratch buffer sizes, matching gen6_mfc_init (VA-API). */ + case ANV_VID_MEM_H264_ENC_MB_STATUS: + return width_in_mb * height_in_mb * 16; + case ANV_VID_MEM_H264_ENC_DIRECT_MV_TOP: + case ANV_VID_MEM_H264_ENC_DIRECT_MV_BOTTOM: + return 68ull * width_in_mb * height_in_mb; + case ANV_VID_MEM_H264_ENC_INPUT_MV: + /* 16 4x4 motion-vector pairs (32MV) per MB, 4 bytes each = 64 bytes. */ + return 64ull * width_in_mb * height_in_mb; default: UNREACHABLE("unknown memory"); } @@ -247,21 +351,32 @@ static void get_h264_video_session_mem_reqs(struct anv_video_session *vid, VkVideoSessionMemoryRequirementsKHR *mem_reqs, uint32_t *pVideoSessionMemoryRequirementsCount, - uint32_t memory_types) + uint32_t memory_types, + uint32_t first_mem_idx, + uint32_t count) { VK_OUTARRAY_MAKE_TYPED(VkVideoSessionMemoryRequirementsKHR, out, mem_reqs, pVideoSessionMemoryRequirementsCount); - for (unsigned i = 0; i < ANV_VID_MEM_H264_MAX; i++) { - uint32_t bind_index = ANV_VID_MEM_H264_INTRA_ROW_STORE + i; - uint64_t size = get_h264_video_mem_size(vid, i); + for (unsigned i = 0; i < count; i++) { + uint32_t bind_index = first_mem_idx + i; + uint64_t size = get_h264_video_mem_size(vid, bind_index); + + /* The row-store and direct-MV scratch buffers reach the hardware through + * MFX_PIPE_BUF_ADDR_STATE / MFX_BSP_BUF_BASE_ADDR_STATE, whose address + * fields are bits 31:6, so 64 bytes is enough. The encode input-MV buffer + * instead feeds MFX_IND_OBJ_BASE_ADDR_STATE, which stores addresses in + * bits 31:12; a base that is not 4KiB aligned would be truncated and the + * MFC would fetch its per-MB motion vectors from the wrong address. */ + uint64_t alignment = bind_index == ANV_VID_MEM_H264_ENC_INPUT_MV ? + 4096 : 64; vk_outarray_append_typed(VkVideoSessionMemoryRequirementsKHR, &out, p) { p->memoryBindIndex = bind_index; p->memoryRequirements.size = size; - p->memoryRequirements.alignment = 64; + p->memoryRequirements.alignment = alignment; p->memoryRequirements.memoryTypeBits = memory_types; } } @@ -283,7 +398,24 @@ anv_GetVideoSessionMemoryRequirementsKHR(VkDevice _device, case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: get_h264_video_session_mem_reqs(vid, mem_reqs, pVideoSessionMemoryRequirementsCount, - memory_types); + memory_types, + ANV_VID_MEM_H264_INTRA_ROW_STORE, + ANV_VID_MEM_H264_MPR_ROW_SCRATCH - + ANV_VID_MEM_H264_INTRA_ROW_STORE + 1); + break; + case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: + /* A single call over the whole contiguous enum range. + * VK_OUTARRAY_MAKE_TYPED resets *count to 0 and writes from index 0 on + * every call, so two separate calls would make the second clobber the + * first, leaving some scratch buffers unbound. MPR_ROW_SCRATCH falls in + * the range and is included even though encode does not use it (a small + * unused allocation). */ + get_h264_video_session_mem_reqs(vid, mem_reqs, + pVideoSessionMemoryRequirementsCount, + memory_types, + ANV_VID_MEM_H264_INTRA_ROW_STORE, + ANV_VID_MEM_H264_ENC_INPUT_MV - + ANV_VID_MEM_H264_INTRA_ROW_STORE + 1); break; default: UNREACHABLE("unknown codec"); @@ -303,6 +435,87 @@ anv_UpdateVideoSessionParametersKHR(VkDevice _device, return vk_video_session_parameters_update(¶ms->vk, pUpdateInfo); } +/* Required entry point for VK_KHR_video_encode_queue. Emits the SPS/PPS RBSP + * (start code and NAL header included) using the common + * vk_video_encode_h264_sps/pps() helpers; the H.264-only reduction of the + * mainline anv implementation. */ +VkResult +anv_GetEncodedVideoSessionParametersKHR(VkDevice _device, + const VkVideoEncodeSessionParametersGetInfoKHR *pVideoSessionParametersInfo, + VkVideoEncodeSessionParametersFeedbackInfoKHR *pFeedbackInfo, + size_t *pDataSize, + void *pData) +{ + ANV_FROM_HANDLE(anv_video_session_params, params, + pVideoSessionParametersInfo->videoSessionParameters); + size_t total_size = 0; + size_t size_limit = pData ? *pDataSize : 0; + + if (params->vk.op == VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR) { + const struct VkVideoEncodeH264SessionParametersGetInfoKHR *h264_get_info = + vk_find_struct_const(pVideoSessionParametersInfo->pNext, + VIDEO_ENCODE_H264_SESSION_PARAMETERS_GET_INFO_KHR); + size_t sps_size = 0, pps_size = 0; + if (h264_get_info->writeStdSPS) { + for (unsigned i = 0; i < params->vk.h264_enc.h264_sps_count; i++) + if (params->vk.h264_enc.h264_sps[i].base.seq_parameter_set_id == h264_get_info->stdSPSId) + vk_video_encode_h264_sps(¶ms->vk.h264_enc.h264_sps[i].base, + size_limit, &sps_size, pData); + } + if (h264_get_info->writeStdPPS) { + char *data_ptr = pData ? (char *)pData + sps_size : NULL; + for (unsigned i = 0; i < params->vk.h264_enc.h264_pps_count; i++) + if (params->vk.h264_enc.h264_pps[i].base.pic_parameter_set_id == h264_get_info->stdPPSId) + vk_video_encode_h264_pps(¶ms->vk.h264_enc.h264_pps[i].base, + params->vk.h264_enc.profile_idc == STD_VIDEO_H264_PROFILE_IDC_HIGH, + size_limit, &pps_size, data_ptr); + } + total_size = sps_size + pps_size; + } + + /* The size check is done here against *pDataSize (mainline anv pattern). */ + if (pData && *pDataSize < total_size) { + *pDataSize = 0; + return VK_INCOMPLETE; + } + + *pDataSize = total_size; + return VK_SUCCESS; +} + +/* Required entry point for VK_KHR_video_encode_queue; the H.264-only reduction + * of the mainline anv implementation. The reported values are preferred + * defaults and do not constrain what the application may request. */ +VkResult +anv_GetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR(VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceVideoEncodeQualityLevelInfoKHR *pQualityLevelInfo, + VkVideoEncodeQualityLevelPropertiesKHR *pQualityLevelProperties) +{ + const VkVideoProfileInfoKHR *profile = pQualityLevelInfo->pVideoProfile; + + pQualityLevelProperties->preferredRateControlMode = VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DEFAULT_KHR; + + switch (profile->videoCodecOperation) { + case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: { + VkVideoEncodeH264QualityLevelPropertiesKHR *ext = + vk_find_struct(pQualityLevelProperties->pNext, VIDEO_ENCODE_H264_QUALITY_LEVEL_PROPERTIES_KHR); + if (ext) { + ext->preferredIdrPeriod = 60; + ext->preferredGopFrameCount = 60; + ext->preferredConstantQp = (VkVideoEncodeH264QpKHR) { 26, 26, 26 }; + ext->preferredMaxL0ReferenceCount = 0; /* no references */ + ext->preferredMaxL1ReferenceCount = 0; + ext->preferredStdEntropyCodingModeFlag = false; /* Baseline: CAVLC */ + } + break; + } + default: + return VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR; + } + + return VK_SUCCESS; +} + static void copy_bind(struct anv_vid_mem *dst, const VkBindVideoSessionMemoryInfoKHR *src) { diff --git a/src/intel/vulkan_hasvk/anv_video_h264_bitstream.c b/src/intel/vulkan_hasvk/anv_video_h264_bitstream.c new file mode 100644 index 0000000..02f2af2 --- /dev/null +++ b/src/intel/vulkan_hasvk/anv_video_h264_bitstream.c @@ -0,0 +1,186 @@ +/* + * Copyright © 2026 Intel Corporation + * + * 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 +#include + +#include "util/macros.h" +#include "anv_video_h264_bitstream.h" + +struct anv_h264_bw { + uint8_t *buf; + uint32_t cap_bits; + uint32_t pos_bits; + bool overflow; +}; + +static void +bw_init(struct anv_h264_bw *bw, uint8_t *buf, uint32_t cap_bytes) +{ + bw->buf = buf; + bw->cap_bits = cap_bytes * 8; + bw->pos_bits = 0; + bw->overflow = false; + memset(buf, 0, cap_bytes); +} + +static void +bw_put_bit(struct anv_h264_bw *bw, unsigned bit) +{ + if (bw->pos_bits >= bw->cap_bits) { + bw->overflow = true; + return; + } + if (bit) + bw->buf[bw->pos_bits >> 3] |= 1u << (7 - (bw->pos_bits & 7)); + bw->pos_bits++; +} + +/* u(n): most significant bit first. */ +static void +bw_put_u(struct anv_h264_bw *bw, uint32_t value, unsigned n) +{ + for (int i = (int)n - 1; i >= 0; i--) + bw_put_bit(bw, (value >> i) & 1); +} + +/* ue(v) — Exp-Golomb, H.264 9.1. */ +static void +bw_put_ue(struct anv_h264_bw *bw, uint32_t value) +{ + uint32_t v = value + 1; + int leading = 0; + for (uint32_t t = v; t > 1; t >>= 1) + leading++; + for (int i = 0; i < leading; i++) + bw_put_bit(bw, 0); + bw_put_u(bw, v, leading + 1); +} + +/* se(v) — H.264 9.1.1. */ +static void +bw_put_se(struct anv_h264_bw *bw, int32_t value) +{ + uint32_t code = value <= 0 ? (uint32_t)(-2 * (int64_t)value) + : (uint32_t)(2 * (int64_t)value - 1); + bw_put_ue(bw, code); +} + +uint32_t +anv_h264_build_encode_slice_header(uint8_t *out_buf, uint32_t out_cap, + const StdVideoH264SequenceParameterSet *sps, + const StdVideoH264PictureParameterSet *pps, + const StdVideoEncodeH264PictureInfo *pic_info, + const StdVideoEncodeH264SliceHeader *slice_hdr) +{ + /* Assumptions for syntax elements outside the current scope: frame_mbs_only + * (no field pictures), no weighted prediction (I slice), single slice group, + * single slice. */ + if (out_cap < 32) + return 0; + + struct anv_h264_bw bw; + bw_init(&bw, out_buf, out_cap); + + /* Start code + NAL header (4-byte start code + 1-byte NAL header, which + * SkipEmulationByteCount = 5 accounts for). */ + bw_put_u(&bw, 0x00000001, 32); + + bool is_idr = pic_info->flags.IdrPicFlag; + unsigned nal_unit_type = is_idr ? 5 : 1; + unsigned nal_ref_idc = 1; /* I-frame is always a reference */ + + bw_put_bit(&bw, 0); /* forbidden_zero_bit */ + bw_put_u(&bw, nal_ref_idc, 2); + bw_put_u(&bw, nal_unit_type, 5); + + bw_put_ue(&bw, slice_hdr->first_mb_in_slice); + bw_put_ue(&bw, slice_hdr->slice_type); + bw_put_ue(&bw, pic_info->pic_parameter_set_id); + + uint32_t log2_max_frame_num = sps->log2_max_frame_num_minus4 + 4; + bw_put_u(&bw, pic_info->frame_num, log2_max_frame_num); + + /* Field pictures (frame_mbs_only_flag == 0) are out of scope; the + * StdVideoEncodeH264PictureInfo has no field_pic_flag. */ + + if (is_idr) + bw_put_ue(&bw, pic_info->idr_pic_id); + + if (sps->pic_order_cnt_type == 0) { + uint32_t log2_max_poc_lsb = sps->log2_max_pic_order_cnt_lsb_minus4 + 4; + bw_put_u(&bw, (uint32_t)pic_info->PicOrderCnt, log2_max_poc_lsb); + /* delta_pic_order_cnt_bottom is never written for frame-only encode. */ + } else if (sps->pic_order_cnt_type == 1 && + !sps->flags.delta_pic_order_always_zero_flag) { + bw_put_se(&bw, pic_info->PicOrderCnt); + } + + if (pps->flags.redundant_pic_cnt_present_flag) + bw_put_ue(&bw, 0); + + /* P-slice reference-list syntax (H.264 7.3.3). B slices are out of scope, so + * direct_spatial_mv_pred_flag is never emitted. */ + bool is_p = (slice_hdr->slice_type == STD_VIDEO_H264_SLICE_TYPE_P); + if (is_p) { + /* num_ref_idx_active_override_flag = 0: use the PPS default L0 count. */ + bw_put_bit(&bw, 0); + /* ref_pic_list_modification_flag_l0 = 0: default frame_num-ordered list. */ + bw_put_bit(&bw, 0); + } + + /* pred_weight_table is absent (weighted_pred_flag is off). */ + + if (nal_ref_idc != 0) { + if (is_idr) { + bw_put_bit(&bw, pic_info->flags.no_output_of_prior_pics_flag); + bw_put_bit(&bw, pic_info->flags.long_term_reference_flag); + } else { + bw_put_bit(&bw, pic_info->flags.adaptive_ref_pic_marking_mode_flag); + if (pic_info->flags.adaptive_ref_pic_marking_mode_flag) + bw_put_ue(&bw, 0); /* memory_management_control_operation: end (0) */ + } + } + + /* cabac_init_idc is present only for CABAC P/SP/B slices (H.264 7.3.3), not + * for I slices. */ + if (pps->flags.entropy_coding_mode_flag && is_p) + bw_put_ue(&bw, slice_hdr->cabac_init_idc); + + bw_put_se(&bw, slice_hdr->slice_qp_delta); + + if (pps->flags.deblocking_filter_control_present_flag) { + bw_put_ue(&bw, slice_hdr->disable_deblocking_filter_idc); + if (slice_hdr->disable_deblocking_filter_idc != 1) { + bw_put_se(&bw, slice_hdr->slice_alpha_c0_offset_div2); + bw_put_se(&bw, slice_hdr->slice_beta_offset_div2); + } + } + + /* FMO (num_slice_groups_minus1 > 0) is out of scope. */ + + if (bw.overflow) + return 0; + + return bw.pos_bits; +} diff --git a/src/intel/vulkan_hasvk/anv_video_h264_bitstream.h b/src/intel/vulkan_hasvk/anv_video_h264_bitstream.h new file mode 100644 index 0000000..38daa57 --- /dev/null +++ b/src/intel/vulkan_hasvk/anv_video_h264_bitstream.h @@ -0,0 +1,47 @@ +/* + * Copyright © 2026 Intel Corporation + * + * 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. + */ + +#ifndef ANV_VIDEO_H264_BITSTREAM_H +#define ANV_VIDEO_H264_BITSTREAM_H + +#include + +#include "vk_video/vulkan_video_codec_h264std.h" +#include "vk_video/vulkan_video_codec_h264std_encode.h" + +/* Builds a single I/IDR slice header (start code + NAL header + RBSP), the + * equivalent of VA-API's build_avc_slice_header(). Emulation-prevention bytes + * are NOT inserted here: MFX_PAK_INSERT_OBJECT does that in hardware via + * EmulationByteBitsInsertEnable / SkipEmulationByteCount, so this writer emits + * raw RBSP bits only. + * + * Returns the number of bits written (including start code and NAL header), or + * 0 if out_buf was too small. */ +uint32_t +anv_h264_build_encode_slice_header(uint8_t *out_buf, uint32_t out_cap, + const StdVideoH264SequenceParameterSet *sps, + const StdVideoH264PictureParameterSet *pps, + const StdVideoEncodeH264PictureInfo *pic_info, + const StdVideoEncodeH264SliceHeader *slice_hdr); + +#endif /* ANV_VIDEO_H264_BITSTREAM_H */ diff --git a/src/intel/vulkan_hasvk/anv_video_h264_slice.c b/src/intel/vulkan_hasvk/anv_video_h264_slice.c index 370d25b..0b397cd 100644 --- a/src/intel/vulkan_hasvk/anv_video_h264_slice.c +++ b/src/intel/vulkan_hasvk/anv_video_h264_slice.c @@ -22,6 +22,7 @@ * IN THE SOFTWARE. */ #include +#include #include #include "util/macros.h" @@ -143,6 +144,8 @@ anv_h264_gather_frefs( int32_t poc_top = ri->PicOrderCnt[0]; int32_t poc_bot = ri->PicOrderCnt[1]; + if (poc_top == INT32_MAX) poc_top = poc_bot; + if (poc_bot == INT32_MAX) poc_bot = poc_top; if (parities == ANV_H264_PAR_TOP) f.frame_poc = poc_top; else if (parities == ANV_H264_PAR_BOTTOM) @@ -210,6 +213,86 @@ anv_h264_sort_ltidx_asc(struct anv_h264_fref *a, int n) } } +static void +anv_h264_sort_poc_desc(struct anv_h264_fref *a, int n) +{ + for (int i = 1; i < n; i++) { + struct anv_h264_fref v = a[i]; + int j = i - 1; + while (j >= 0 && a[j].frame_poc < v.frame_poc) { a[j+1] = a[j]; j--; } + a[j+1] = v; + } +} + +static void +anv_h264_sort_poc_asc(struct anv_h264_fref *a, int n) +{ + for (int i = 1; i < n; i++) { + struct anv_h264_fref v = a[i]; + int j = i - 1; + while (j >= 0 && a[j].frame_poc > v.frame_poc) { a[j+1] = a[j]; j--; } + a[j+1] = v; + } +} + +/* 8.2.4.2.3 and 8.2.4.2.4 */ +static void +anv_h264_swap_identical_l1(uint8_t *l0, uint8_t *l0b, + uint8_t *l1, uint8_t *l1b, int count) +{ + if (count < 2) + return; + if (memcmp(l0, l1, ANV_H264_MAX_REF_FRAMES)) + return; + if (l0b && l1b && memcmp(l0b, l1b, ANV_H264_MAX_REF_FRAMES)) + return; + + uint8_t t = l1[0]; l1[0] = l1[1]; l1[1] = t; + if (l1b) { t = l1b[0]; l1b[0] = l1b[1]; l1b[1] = t; } +} + +/* 8.2.4.3.1 */ +static void +anv_h264_ref_list_insert(uint8_t *list, uint8_t *listb, int active_count, + int refIdx, uint8_t slot, uint8_t bottom) +{ + uint8_t tmp[ANV_H264_MAX_REF_FRAMES + 1]; + uint8_t tmpb[ANV_H264_MAX_REF_FRAMES + 1]; + + if (refIdx < 0 || refIdx >= active_count || + active_count > ANV_H264_MAX_REF_FRAMES) + return; + + memcpy(tmp, list, ANV_H264_MAX_REF_FRAMES); + tmp[ANV_H264_MAX_REF_FRAMES] = ANV_H264_INVALID_SLOT; + if (listb) { + memcpy(tmpb, listb, ANV_H264_MAX_REF_FRAMES); + tmpb[ANV_H264_MAX_REF_FRAMES] = 0; + } else { + memset(tmpb, 0, sizeof(tmpb)); + } + + for (int k = active_count; k > refIdx; k--) { + tmp[k] = tmp[k - 1]; + tmpb[k] = tmpb[k - 1]; + } + tmp[refIdx] = slot; + tmpb[refIdx] = bottom; + + int w = refIdx + 1; + for (int k = refIdx + 1; k <= active_count; k++) { + if (tmp[k] == slot && tmpb[k] == bottom) + continue; + tmp[w] = tmp[k]; + tmpb[w] = tmpb[k]; + w++; + } + + memcpy(list, tmp, active_count); + if (listb) + memcpy(listb, tmpb, active_count); +} + static void anv_h264_build_field_ref_lists( int slice_type, int sel, int32_t cur_field_poc, @@ -284,12 +367,17 @@ anv_h264_build_field_ref_lists( anv_h264_emit_field_list(l0, l0b, ANV_H264_MAX_REF_FRAMES, &idx0, lt, nlt, sel); anv_h264_emit_field_list(l1, l1b, ANV_H264_MAX_REF_FRAMES, &idx1, o1, no1, sel); anv_h264_emit_field_list(l1, l1b, ANV_H264_MAX_REF_FRAMES, &idx1, lt, nlt, sel); + + anv_h264_swap_identical_l1(l0, l0b, l1, l1b, idx1); } +/* 8.2.4.2.1 and 8.2.4.2.3 */ static void anv_h264_build_default_ref_list( int slice_type, int32_t curr_poc, + uint32_t cur_frame_num, + uint32_t MaxFrameNum, uint32_t ref_slot_count, const VkVideoReferenceSlotInfoKHR *ref_slots, uint8_t list0[ANV_H264_MAX_REF_FRAMES], @@ -301,95 +389,54 @@ anv_h264_build_default_ref_list( if (slice_type == ANV_H264_SLICE_I || slice_type == ANV_H264_SLICE_SI) return; - uint8_t st_slots[ANV_H264_MAX_REF_FRAMES]; - int32_t st_poc[ANV_H264_MAX_REF_FRAMES]; - int st_count = 0; + struct anv_h264_fref st[ANV_H264_MAX_REF_FRAMES]; + struct anv_h264_fref lt[ANV_H264_MAX_REF_FRAMES]; + int nst = 0, nlt = 0; + anv_h264_gather_frefs(ref_slot_count, ref_slots, cur_frame_num, MaxFrameNum, + st, &nst, lt, &nlt); - uint8_t lt_slots[ANV_H264_MAX_REF_FRAMES]; - uint32_t lt_idx[ANV_H264_MAX_REF_FRAMES]; - int lt_count = 0; - - for (uint32_t i = 0; i < ref_slot_count; i++) { - const VkVideoDecodeH264DpbSlotInfoKHR *dpb = - vk_find_struct_const(ref_slots[i].pNext, - VIDEO_DECODE_H264_DPB_SLOT_INFO_KHR); - if (!dpb) continue; - const StdVideoDecodeH264ReferenceInfo *ri = dpb->pStdReferenceInfo; - if (!ri) continue; - int slot = ref_slots[i].slotIndex; - if (ri->flags.used_for_long_term_reference) { - lt_slots[lt_count] = (uint8_t)slot; - lt_idx[lt_count] = ri->FrameNum; - lt_count++; - } else { - st_slots[st_count] = (uint8_t)slot; - st_poc[st_count] = ri->PicOrderCnt[0]; - st_count++; - } - } - - for (int i = 1; i < st_count; i++) { - uint8_t si = st_slots[i]; int32_t pi = st_poc[i]; - int j = i - 1; - while (j >= 0 && st_poc[j] < pi) { - st_slots[j+1] = st_slots[j]; st_poc[j+1] = st_poc[j]; j--; - } - st_slots[j+1] = si; st_poc[j+1] = pi; - } - - for (int i = 1; i < lt_count; i++) { - uint8_t si = lt_slots[i]; uint32_t li = lt_idx[i]; - int j = i - 1; - while (j >= 0 && lt_idx[j] > li) { - lt_slots[j+1] = lt_slots[j]; lt_idx[j+1] = lt_idx[j]; j--; - } - lt_slots[j+1] = si; lt_idx[j+1] = li; - } + anv_h264_sort_ltidx_asc(lt, nlt); if (slice_type == ANV_H264_SLICE_P || slice_type == ANV_H264_SLICE_SP) { + /* 8.2.4.2.1 */ + anv_h264_sort_fnwrap_desc(st, nst); int p = 0; - for (int i = 0; i < st_count && p < ANV_H264_MAX_REF_FRAMES; i++) - list0[p++] = st_slots[i]; - for (int i = 0; i < lt_count && p < ANV_H264_MAX_REF_FRAMES; i++) - list0[p++] = lt_slots[i]; + for (int i = 0; i < nst && p < ANV_H264_MAX_REF_FRAMES; i++) + list0[p++] = st[i].slot; + for (int i = 0; i < nlt && p < ANV_H264_MAX_REF_FRAMES; i++) + list0[p++] = lt[i].slot; return; } - uint8_t past_slots[ANV_H264_MAX_REF_FRAMES]; - int32_t past_poc[ANV_H264_MAX_REF_FRAMES]; - int past_cnt = 0; - uint8_t fut_slots[ANV_H264_MAX_REF_FRAMES]; - int32_t fut_poc[ANV_H264_MAX_REF_FRAMES]; - int fut_cnt = 0; + struct anv_h264_fref past[ANV_H264_MAX_REF_FRAMES]; + struct anv_h264_fref fut[ANV_H264_MAX_REF_FRAMES]; + int past_cnt = 0, fut_cnt = 0; - for (int i = 0; i < st_count; i++) { - if (st_poc[i] < curr_poc) { - past_slots[past_cnt] = st_slots[i]; - past_poc[past_cnt++] = st_poc[i]; - } + for (int i = 0; i < nst; i++) { + if (st[i].frame_poc < curr_poc) + past[past_cnt++] = st[i]; + else + fut[fut_cnt++] = st[i]; } - for (int i = st_count - 1; i >= 0; i--) { - if (st_poc[i] >= curr_poc) { - fut_slots[fut_cnt] = st_slots[i]; - fut_poc[fut_cnt++] = st_poc[i]; - } - } - (void)past_poc; (void)fut_poc; + anv_h264_sort_poc_desc(past, past_cnt); + anv_h264_sort_poc_asc(fut, fut_cnt); int p0 = 0, p1 = 0; for (int i = 0; i < past_cnt && p0 < ANV_H264_MAX_REF_FRAMES; i++) - list0[p0++] = past_slots[i]; + list0[p0++] = past[i].slot; for (int i = 0; i < fut_cnt && p0 < ANV_H264_MAX_REF_FRAMES; i++) - list0[p0++] = fut_slots[i]; - for (int i = 0; i < lt_count && p0 < ANV_H264_MAX_REF_FRAMES; i++) - list0[p0++] = lt_slots[i]; + list0[p0++] = fut[i].slot; + for (int i = 0; i < nlt && p0 < ANV_H264_MAX_REF_FRAMES; i++) + list0[p0++] = lt[i].slot; for (int i = 0; i < fut_cnt && p1 < ANV_H264_MAX_REF_FRAMES; i++) - list1[p1++] = fut_slots[i]; + list1[p1++] = fut[i].slot; for (int i = 0; i < past_cnt && p1 < ANV_H264_MAX_REF_FRAMES; i++) - list1[p1++] = past_slots[i]; - for (int i = 0; i < lt_count && p1 < ANV_H264_MAX_REF_FRAMES; i++) - list1[p1++] = lt_slots[i]; + list1[p1++] = past[i].slot; + for (int i = 0; i < nlt && p1 < ANV_H264_MAX_REF_FRAMES; i++) + list1[p1++] = lt[i].slot; + + anv_h264_swap_identical_l1(list0, NULL, list1, NULL, p1); } static void @@ -415,6 +462,8 @@ anv_h264_apply_ref_pic_list_mod( if (idc == 3) break; if (idc > 3) { st->error = true; return; } + uint8_t found_slot = ANV_H264_INVALID_SLOT; + if (idc == 0 || idc == 1) { unsigned diff = anv_h264_rbsp_ue(st) + 1; if (st->error) return; @@ -423,7 +472,6 @@ anv_h264_apply_ref_pic_list_mod( else picNumL = (picNumL + diff) % MaxFrameNum; - uint8_t found_slot = ANV_H264_INVALID_SLOT; for (uint32_t j = 0; j < ref_slot_count; j++) { const VkVideoDecodeH264DpbSlotInfoKHR *dpb = vk_find_struct_const(ref_slots[j].pNext, @@ -435,25 +483,29 @@ anv_h264_apply_ref_pic_list_mod( break; } } - if (found_slot == ANV_H264_INVALID_SLOT) continue; - - for (int k = active_count - 1; k > refIdxL; k--) - list[k] = list[k - 1]; - list[refIdxL] = found_slot; - - int w = refIdxL + 1; - for (int k = refIdxL + 1; k < active_count + 1 && w < ANV_H264_MAX_REF_FRAMES; k++) { - if (list[k] != found_slot) - list[w++] = list[k]; - } - if (w < ANV_H264_MAX_REF_FRAMES) - list[w] = ANV_H264_INVALID_SLOT; - - refIdxL++; - } else if (idc == 2) { - anv_h264_rbsp_ue(st); + } else { + uint32_t ltpn = anv_h264_rbsp_ue(st); if (st->error) return; + + for (uint32_t j = 0; j < ref_slot_count; j++) { + const VkVideoDecodeH264DpbSlotInfoKHR *dpb = + vk_find_struct_const(ref_slots[j].pNext, + VIDEO_DECODE_H264_DPB_SLOT_INFO_KHR); + if (!dpb || !dpb->pStdReferenceInfo) continue; + if (dpb->pStdReferenceInfo->flags.used_for_long_term_reference && + dpb->pStdReferenceInfo->FrameNum == ltpn) { + found_slot = (uint8_t)ref_slots[j].slotIndex; + break; + } + } } + + if (found_slot == ANV_H264_INVALID_SLOT) continue; + if (refIdxL >= active_count) continue; + + anv_h264_ref_list_insert(list, NULL, active_count, refIdxL, + found_slot, 0); + refIdxL++; } while (idc != 3); } @@ -531,29 +583,10 @@ anv_h264_apply_ref_pic_list_mod_field( } if (found_slot == ANV_H264_INVALID_SLOT) continue; + if (refIdx >= active_count) continue; - for (int k = active_count - 1; k > refIdx && k < ANV_H264_MAX_REF_FRAMES; k--) { - list[k] = list[k - 1]; - listb[k] = listb[k - 1]; - } - if (refIdx < ANV_H264_MAX_REF_FRAMES) { - list[refIdx] = found_slot; - listb[refIdx] = found_bottom; - } - - int w = refIdx + 1; - for (int k = refIdx + 1; - k < active_count + 1 && k < ANV_H264_MAX_REF_FRAMES && - w < ANV_H264_MAX_REF_FRAMES; k++) { - if (!(list[k] == found_slot && listb[k] == found_bottom)) { - list[w] = list[k]; - listb[w] = listb[k]; - w++; - } - } - if (w < ANV_H264_MAX_REF_FRAMES) - list[w] = ANV_H264_INVALID_SLOT; - + anv_h264_ref_list_insert(list, listb, active_count, refIdx, + found_slot, found_bottom); refIdx++; } while (idc != 3); } @@ -711,9 +744,14 @@ anv_h264_parse_slice_header( uint32_t cur_frame_num = pic_info->pStdPictureInfo->frame_num; uint32_t MaxFrameNumForList = 1u << (sps->log2_max_frame_num_minus4 + 4); - int32_t curr_poc = cur_field_pic && cur_bottom ? - pic_info->pStdPictureInfo->PicOrderCnt[1] : - pic_info->pStdPictureInfo->PicOrderCnt[0]; + int32_t poc_top = pic_info->pStdPictureInfo->PicOrderCnt[0]; + int32_t poc_bot = pic_info->pStdPictureInfo->PicOrderCnt[1]; + if (poc_top == INT32_MAX) poc_top = poc_bot; + if (poc_bot == INT32_MAX) poc_bot = poc_top; + + /* 8.2.1 */ + int32_t curr_poc = !cur_field_pic ? MIN2(poc_top, poc_bot) : + (cur_bottom ? poc_bot : poc_top); if (cur_field_pic) { anv_h264_build_field_ref_lists(slice_type, cur_sel, curr_poc, @@ -723,6 +761,7 @@ anv_h264_parse_slice_header( out->ref_list1, out->ref_list1_bottom); } else { anv_h264_build_default_ref_list(slice_type, curr_poc, + cur_frame_num, MaxFrameNumForList, ref_slot_count, ref_slots, out->ref_list0, out->ref_list1); } diff --git a/src/intel/vulkan_hasvk/anv_video_mfc.c b/src/intel/vulkan_hasvk/anv_video_mfc.c new file mode 100644 index 0000000..ccdded5 --- /dev/null +++ b/src/intel/vulkan_hasvk/anv_video_mfc.c @@ -0,0 +1,450 @@ +/* + * Copyright © 2024 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + * H.264 encode: MFC/MFX static state emission for the AVC encode pipeline. + * + * This lives in a separate translation unit because genX_pack.h and + * genX_video_pack.h share the same include guard (GFX7_IVB_PACK_H); a TU that + * includes both silently drops the second. The MFX and MFC encode commands + * need genX_video_pack.h, mirroring the decode split between genX_cmd_video.c + * and genX_cmd_buffer.c. + * + * Field names are taken from the decode paths in genX_cmd_video.c where the + * same commands are used. MFX_AVC_SLICE_STATE is the exception: it is not used + * by decode, so its fields are matched against the VA-API reference encoder + * (gen6_mfc_avc_slice_state, intel-vaapi-driver) rather than an in-tree user. + * + * All values below are validated only for the single-slice, I-frame, CQP case + * currently implemented. Inter prediction, references and rate control are not + * emitted. + */ + +#include + +#include "anv_private.h" +#include "genxml/gen_macros.h" +#include "anv_genX.h" +#include "genxml/genX_video_pack.h" +#include "util/vl_zscan_data.h" +#include "anv_video_h264_bitstream.h" + +void +genX(hasvk_mfc_emit_static_state)(struct anv_cmd_buffer *cmd_buffer, + const struct anv_image *source_image, + struct anv_address recon_address, + const StdVideoH264SequenceParameterSet *sps, + const StdVideoH264PictureParameterSet *pps, + struct anv_address dst_address, + uint32_t dst_page_offset, + uint64_t dst_buffer_range, + uint32_t width_in_mbs, uint32_t height_in_mbs, + bool is_p, struct anv_address ref_address) +{ + struct anv_video_session *vid = cmd_buffer->video.vid; + + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_PIPE_MODE_SELECT), sel) { + sel.StandardSelect = SS_AVC; + sel.CodecSelect = Encode; + /* PAK requires the long-format interface and writes its output through + * the stream-out path. Deblocking is disabled here, so reconstruction + * uses the pre-deblocking destination. */ + sel.DecoderShortFormatMode = LongFormatDriverInterface; + sel.StreamOutEnable = true; + sel.PreDeblockingOutputEnable = true; + } + + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_SURFACE_STATE), ss) { + ss.Width = source_image->vk.extent.width - 1; + ss.Height = height_in_mbs * 16 - 1; + ss.SurfaceFormat = PLANAR_420_8; + ss.InterleaveChroma = 1; + ss.SurfacePitch = source_image->planes[0].primary_surface.isl.row_pitch_B - 1; + ss.TiledSurface = source_image->planes[0].primary_surface.isl.tiling != ISL_TILING_LINEAR; + ss.TileWalk = TW_YMAJOR; + ss.YOffsetforUCb = + MIN2((source_image->planes[1].primary_surface.memory_range.offset - + source_image->planes[0].primary_surface.memory_range.offset) / + source_image->planes[0].primary_surface.isl.row_pitch_B, 32767u); + ss.YOffsetforVCr = 0; + } + + struct anv_address mv_object_address = + (struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_ENC_INPUT_MV].mem->bo, + vid->vid_mem[ANV_VID_MEM_H264_ENC_INPUT_MV].offset }; + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_IND_OBJ_BASE_ADDR_STATE), index_obj) { + index_obj.MFCIndirectPAKBSEObjectAddress = dst_address; + /* Base for the per-MB indirect PAK-MV input data (inter/skip MBs). The + * MFC sets up its MV fetch unit from this base even when a given MB reads + * no MV data, so it must be a valid address rather than null. */ + index_obj.MFXIndirectMVObjectAddress = mv_object_address; +#if GFX_VERx10 == 70 + /* AccessUpperBound and CacheabilityControl exist under these names only + * on Gen7; Gen7.5 has only the MOCS field and Gen8 renames the bound. + * Encode is Gen7-only, so gate the extra fields accordingly. */ + index_obj.MFCIndirectPAKBSEObjectAccessUpperBound = + anv_address_add(dst_address, dst_page_offset + dst_buffer_range); + index_obj.MFCIndirectPAKBSEObjectCacheabilityControl = 2; + index_obj.MFXIndirectMVObjectCacheabilityControl = 2; +#endif + } + + /* Zero the first 32 bytes of the indirect MV buffer so every P macroblock, + * which reads its 8MV (32-byte) block from offset 0, sees a zero motion + * vector (co-located prediction). The MFC executes these serialising writes + * before it fetches the MV data for the PAK objects that follow. */ + if (is_p) { + for (unsigned i = 0; i < 32; i += 8) + genX(emit_query_mi_write_u64)(cmd_buffer, + anv_address_add(mv_object_address, i), 0); + } + + /* Scratch buffers are bound at video-session level via + * vkBindVideoSessionMemoryKHR (ANV_VID_MEM_H264_ENC_*). The reconstructed + * picture (pre-deblocking destination) is the caller's setup DPB slot. + * + * This block (PIPE_BUF_ADDR_STATE .. REF_IDX_STATE) is Gen7-only: field + * names and array layouts differ across gens (e.g. DirectMVBufferAddress[] + * vs the per-index fields on Gen7.5, and MFX_FQM_STATE not existing at all + * on Gen7.5/Gen8). */ +#if GFX_VERx10 == 70 + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_PIPE_BUF_ADDR_STATE), buf) { + buf.PreDeblockingDestinationAddress = recon_address; + buf.OriginalUncompressedPictureSourceCacheabilityControl = 2; + buf.StreamOutDataDestinationCacheabilityControl = 2; + buf.IntraRowStoreScratchBufferCacheabilityControl = 2; + buf.DeblockingFilterRowStoreScratchBufferCacheabilityControl = 2; + buf.MBStatusBufferCacheabilityControl = 2; + memset(buf.ReferencePictureCacheabilityControl, 0, + sizeof(buf.ReferencePictureCacheabilityControl)); + buf.OriginalUncompressedPictureSourceAddress = + anv_image_address(source_image, + &source_image->planes[0].primary_surface.memory_range); + buf.StreamOutDataDestinationAddress = + (struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_ENC_MB_STATUS].mem->bo, + vid->vid_mem[ANV_VID_MEM_H264_ENC_MB_STATUS].offset }; + buf.IntraRowStoreScratchBufferAddress = + (struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_INTRA_ROW_STORE].mem->bo, + vid->vid_mem[ANV_VID_MEM_H264_INTRA_ROW_STORE].offset }; + buf.DeblockingFilterRowStoreScratchBufferAddress = + (struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_DEBLOCK_FILTER_ROW_STORE].mem->bo, + vid->vid_mem[ANV_VID_MEM_H264_DEBLOCK_FILTER_ROW_STORE].offset }; + buf.MBStatusBufferAddress = + (struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_ENC_MB_STATUS].mem->bo, + vid->vid_mem[ANV_VID_MEM_H264_ENC_MB_STATUS].offset }; + /* PRM Vol2 Pt3, MFX_PIPE_BUF_ADDR_STATE, ReferencePictureAddress[0..15]: + * "AVC: Always specifies all 16 addresses ... All invalid addresses must + * be set to a valid address RefAddr[0] by the driver." Leaving these at + * zero risks a fault even when references are unused (I-frame), so fill + * them with the valid, mapped recon address. */ + for (unsigned i = 0; i < ARRAY_SIZE(buf.ReferencePictureAddress); i++) + buf.ReferencePictureAddress[i] = recon_address; + /* Frame store 0 is the P-frame's reference (previous reconstructed + * frame); REF_IDX_STATE L0[0] maps ref index 0 to this frame store. */ + if (is_p) + buf.ReferencePictureAddress[0] = ref_address; + } + + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_BSP_BUF_BASE_ADDR_STATE), bsp) { + /* Only the BSD/MPC row store is used by PAK; MPR row store and bitplane + * read buffer are left null, matching gen6_mfc_bsp_buf_base_addr_state. */ + bsp.BSDMPCRowStoreScratchBufferAddress = + (struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_BSD_MPC_ROW_SCRATCH].mem->bo, + vid->vid_mem[ANV_VID_MEM_H264_BSD_MPC_ROW_SCRATCH].offset }; + bsp.BSDMPCRowStoreScratchBufferCacheabilityControl = 2; + } +#endif /* GFX_VERx10 == 70 */ + + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_IMG_STATE), avc_img) { + avc_img.FrameWidth = MIN2(width_in_mbs - 1, 255u); + avc_img.FrameHeight = MIN2(height_in_mbs - 1, 254u); + avc_img.FrameSize = width_in_mbs * height_in_mbs - 1; + avc_img.ImageStructure = FramePicture; + + avc_img.WeightedBiPredictionIDC = pps->weighted_bipred_idc; + avc_img.WeightedPredictionEnable = pps->flags.weighted_pred_flag; + avc_img.FirstChromaQPOffset = CLAMP(pps->chroma_qp_index_offset, -12, 12); + avc_img.SecondChromaQPOffset = CLAMP(pps->second_chroma_qp_index_offset, -12, 12); + avc_img.FieldPicture = false; + avc_img.MBAFFMode = false; + avc_img.FrameMBOnly = sps->flags.frame_mbs_only_flag; + avc_img._8x8IDCTTransformMode = pps->flags.transform_8x8_mode_flag; + avc_img.Direct8x8Inference = sps->flags.direct_8x8_inference_flag; + avc_img.ConstrainedIntraPrediction = pps->flags.constrained_intra_pred_flag; + avc_img.NonReferencePicture = false; + avc_img.EntropyCodingSyncEnable = pps->flags.entropy_coding_mode_flag; + avc_img.ChromaFormatIDC = sps->chroma_format_idc; + avc_img.MVUnpackedEnable = true; + avc_img.TrellisQuantizationChromaDisable = false; + avc_img.IntraMBConformanceMaxSize = 0xEE8; + avc_img.InterMBConformanceMaxSize = 0xBB8; + avc_img.NumberofReferenceFrames = is_p ? 1 : 0; + avc_img.NumberofActiveReferencePicturesfromL0 = is_p ? 1 : 0; + avc_img.NumberofActiveReferencePicturesfromL1 = 0; + avc_img.InitialQPValue = 0; /* per-slice QP comes from SLICE_STATE */ + avc_img.PicOrderPresent = pps->flags.bottom_field_pic_order_in_frame_present_flag; + avc_img.DeltaPicOrderAlwaysZero = sps->flags.delta_pic_order_always_zero_flag; + avc_img.PicOrderCountType = sps->pic_order_cnt_type; + avc_img.DeblockingFilterControlPresent = pps->flags.deblocking_filter_control_present_flag; + avc_img.RedundantPicCountPresent = pps->flags.redundant_pic_cnt_present_flag; + avc_img.Log2MaxFrameNumber = MIN2((uint32_t)sps->log2_max_frame_num_minus4, 12u); + avc_img.Log2MaxPicOrderCountLSB = MIN2((uint32_t)sps->log2_max_pic_order_cnt_lsb_minus4, 12u); + avc_img.CurrentPictureFrameNumber = 0; /* single IDR frame */ + } + + /* Quantizer matrices. With no custom scaling list in the PPS, + * vk_video_derive_h264_scaling_list() returns the flat default (all 16). */ + StdVideoH264ScalingLists scaling_lists; + vk_video_derive_h264_scaling_list(sps, pps, &scaling_lists); + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_QM_STATE), qm) { + qm.DWordLength = 16; + qm.AVC = AVC_4x4_Intra_MATRIX; + for (unsigned m = 0; m < 3; m++) + for (unsigned q = 0; q < 16; q++) + qm.ForwardQuantizerMatrix[m * 16 + vl_zscan_normal_16[q]] = + scaling_lists.ScalingList4x4[m][q]; + } + if (pps->flags.transform_8x8_mode_flag) { + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_QM_STATE), qm) { + qm.DWordLength = 16; + qm.AVC = AVC_8x8_Intra_MATRIX; + for (unsigned q = 0; q < 64; q++) + qm.ForwardQuantizerMatrix[vl_zscan_normal[q]] = + scaling_lists.ScalingList8x8[0][q]; + } + } + + /* MFX_FQM_STATE (forward quantizer matrix) does not exist on Gen7.5/Gen8; + * the block below is Gen7-only. For a flat scaling list the VA-API encoder + * (gen7_mfc_avc_fqm_state) programs the constant 0x10001000; a custom PPS + * scaling list would require the proper reciprocal and is not handled. */ +#if GFX_VERx10 == 70 + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_FQM_STATE), fqm) { + fqm.DWordLength = 32; + fqm.AVC = AVC_4x4_Intra_MATRIX; + for (unsigned q = 0; q < 32; q++) + fqm.ForwardQuantizerMatrix[q] = 0x10001000; + } + if (pps->flags.transform_8x8_mode_flag) { + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_FQM_STATE), fqm) { + fqm.DWordLength = 32; + fqm.AVC = AVC_8x8_Intra_MATRIX; + for (unsigned q = 0; q < 32; q++) + fqm.ForwardQuantizerMatrix[q] = 0x10001000; + } + } + + /* Direct MV buffers. I-frames scan no reference direct MVs, so only the + * current-picture write targets (top/bottom) are set. The POC list is + * filled as gen6_mfc_avc_directmode_state does (POCList[i] = i/2). */ + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_DIRECTMODE_STATE), dmv) { + memset(dmv.DirectMVBufferAddress, 0, sizeof(dmv.DirectMVBufferAddress)); + dmv.DirectMVBufferWriteAddress[0] = + (struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_ENC_DIRECT_MV_TOP].mem->bo, + vid->vid_mem[ANV_VID_MEM_H264_ENC_DIRECT_MV_TOP].offset }; + dmv.DirectMVBufferWriteAddress[1] = + (struct anv_address) { vid->vid_mem[ANV_VID_MEM_H264_ENC_DIRECT_MV_BOTTOM].mem->bo, + vid->vid_mem[ANV_VID_MEM_H264_ENC_DIRECT_MV_BOTTOM].offset }; + for (unsigned i = 0; i < 32; i++) + dmv.POCList[i] = i / 2; + } + + /* Reference index lists. Entries are set to the "non-existing" marker 0x80 + * (intel_mfc_avc_ref_idx_state). For a P frame, L0[0] points at frame store + * 0: entry = (is_long_term<<6)|((top^bottom^1)<<5)|(frame_store_id<<1)|... ; + * for a short-term progressive frame at store 0 this is 0x20 + * (intel_get_ref_idx_state_1). L1 is unused (P has no backward references). */ + for (unsigned list = 0; list < 2; list++) { + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_REF_IDX_STATE), ref_idx) { + ref_idx.ReferencePictureListSelect = list; + memset(ref_idx.ReferenceListEntry, 0x80, sizeof(ref_idx.ReferenceListEntry)); + if (is_p && list == 0) + ref_idx.ReferenceListEntry[0] = 0x20; + } + } +#endif /* GFX_VERx10 == 70 */ + +} + +void +genX(hasvk_mfc_emit_slice)(struct anv_cmd_buffer *cmd_buffer, + const StdVideoH264SequenceParameterSet *sps, + const StdVideoH264PictureParameterSet *pps, + const StdVideoEncodeH264PictureInfo *pic_info, + const StdVideoEncodeH264SliceHeader *slice_hdr, + uint32_t slice_start_mb, uint32_t slice_end_mb, + uint32_t width_in_mbs, uint32_t height_in_mbs, + uint32_t qp, bool is_last_slice, + uint32_t dst_page_offset) +{ + /* A P slice references the previous reconstructed frame; an I slice does + * not. B slices are out of scope. */ + bool is_p = (slice_hdr->slice_type == STD_VIDEO_H264_SLICE_TYPE_P); + + /* MFX_AVC_SLICE_STATE is not used by decode; fields are matched against the + * VA-API reference encoder (gen6_mfc_avc_slice_state). The slice covers the + * macroblock range [slice_start_mb, slice_end_mb) in raster order. */ + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_AVC_SLICE_STATE), slice) { + slice.SliceType = is_p ? PSlice : ISlice; /* P=0,B=1,I=2 */ + slice.NumberofReferencePicturesinInterpredictionList0 = is_p ? 1 : 0; + slice.SliceQuantizationParameter = qp; + slice.CABACInitIDC = is_p ? slice_hdr->cabac_init_idc : 0; + slice.DisableDeblockingFilterIndicator = 1; + slice.SliceAlphaC0OffsetDiv2 = 0; + slice.SliceBetaOffsetDiv2 = 0; + + slice.SliceStartMBNumber = slice_start_mb; + slice.SliceHorizontalPosition = slice_start_mb % width_in_mbs; + slice.SliceVerticalPosition = slice_start_mb / width_in_mbs; + slice.NextSliceHorizontalPosition = slice_end_mb % width_in_mbs; + slice.NextSliceVerticalPosition = slice_end_mb / width_in_mbs; + + slice.LastSliceGroup = is_last_slice; + slice.HeaderInsertionPresent = true; + slice.SliceDataInsertionPresent = true; + slice.TailInsertionPresent = true; + slice.EmulationByteSliceInsertEnable = true; + slice.CABACZeroWordInsertionEnable = false; + + /* Without these the inserted slice header/tail are not emitted into the + * bitstream (gen6_mfc_avc_slice_state). */ + slice.ResetRateControlCounter = true; + slice.RateControlStableTolerance = 4; + + /* All slices point at the frame's bitstream start; the MFC advances the + * write pointer internally between slices because LoadBitstreamPointerPer- + * Slice is left 0 in MFX_AVC_IMG_STATE (pointer loaded once per frame). + * The start is a byte offset from the 4KiB-aligned PAK-BSE object base in + * MFX_IND_OBJ_BASE_ADDR_STATE, so it carries the sub-page part of the + * destination buffer offset that the page-granular base cannot encode. */ + slice.IndirectPAKBSEDataStartAddress = dst_page_offset; + } + + /* Slice header RBSP. SkipEmulationByteCount = 5 covers the 4-byte start + * code plus the 1-byte NAL header. */ + uint8_t slice_header_buf[64]; + uint32_t slice_header_bits = + anv_h264_build_encode_slice_header(slice_header_buf, sizeof(slice_header_buf), + sps, pps, pic_info, slice_hdr); + if (slice_header_bits == 0) + return; + uint32_t slice_header_dws = DIV_ROUND_UP(slice_header_bits, 32); + /* PRM Vol2 Pt3, MFX_PAK_INSERT_OBJECT, DataBitsInLastDW: valid range is + * [1,32]. A naive bits % 32 yields 0 when bits is a multiple of 32, which + * is illegal; map that to 32. */ + uint32_t slice_header_bits_in_last_dw = slice_header_bits & 0x1f; + if (slice_header_bits_in_last_dw == 0) + slice_header_bits_in_last_dw = 32; + + uint32_t *header_dst = + anv_batch_emitn(&cmd_buffer->batch, 2 + slice_header_dws, + GENX(MFX_PAK_INSERT_OBJECT), + .EmulationByteBitsInsertEnable = true, + .SkipEmulationByteCount = 5, + .DataBitsInLastDW = slice_header_bits_in_last_dw, + .LastHeader = true, + .EndofSlice = false); + if (header_dst) + memcpy(header_dst + 2, slice_header_buf, slice_header_dws * 4); + + /* One MFC_AVC_PAK_OBJECT per macroblock. Field layout per PRM Vol2 Pt3 + * §2.3.1.1 "PAK Object Inline Data Description". An I slice codes every MB as + * I_16x16 DC; a P slice codes every MB as P_Skip (see below). + * + * InlineData[0] (DW3): I-frame path: + * bit 13 IntraMbFlag = 1. + * bits 12:8 MbType5Bits = 23: the raw AVC mb_type (PRM: "unified + * encoding ... according to AVC Spec", not mb_type-1). Per + * Table 7-11, I_16x16 mb_type = 1 + PredMode + 4*CBPChroma + + * 12*CBPLuma, so 23 = I_16x16_2_2_1 (PredMode = DC). + * bits 19:17 CbpDcY/U/V = 1: DC sub-blocks coded. + * InlineData[4] (DW7) bits 1:0: LumaIntraMode[0] = DC (only [1:0] valid for + * Intra16x16). + * InlineData[6] (DW9): IntraStruct = per-MB IntraPredAvailFlags plus + * ChromaIntraPredMode = DC in bits [1:0]. + * + * P-frame path: every MB is P_Skip. Per PRM §2.3.1.1, DW3 bit13 IntraMbFlag=0 + * (inter), bits12:8 MbType5Bits=1 (BP_L0_16x16 — the only P type convertible + * to skip, PRM MbType table), bit2 SkipMbFlag=1 (forces P_Skip: HW uses the + * predicted MV and forces CBP to 0), bits22:20 MvFormat=0 (no MV data). No + * residual and no indirect MV buffer are needed; the reconstructed MB equals + * the motion-compensated (predicted-MV) reference block. For static content + * relative to the reference this is exact. DW7-9 carry the inter sub-mb shape + * (0 = 16x16) and reference indices L0/L1 (0 for the single reference). */ + for (uint32_t mb = slice_start_mb; mb < slice_end_mb; mb++) { + uint32_t x = mb % width_in_mbs; + uint32_t y = mb / width_in_mbs; + bool last_mb = (mb == slice_end_mb - 1); + + anv_batch_emit(&cmd_buffer->batch, GENX(MFC_AVC_PAK_OBJECT), pak) { + pak.IndirectPAKMVDataLength = 0; + pak.IndirectPAKMVDataStartAddressOffset = 0; + pak.InlineData[1] = (y << 8) | x; /* Cbp4x4Y=0, MbY/XCnt */ + pak.InlineData[3] = ((uint32_t)last_mb << 26) | qp; + + if (is_p) { + /* P_Skip: SkipMbFlag forces the skip (predicted MV = 0 for the + * uniform all-skip case, CBP forced to 0), so the reconstructed MB + * equals the co-located reference block. Exact for content identical + * to the reference. An inter MB requires a valid MvFormat (100/110); + * MvFormat=0 hangs the MFC, so 8MV (100) reads a zeroed 32-byte block + * from the dedicated indirect MV buffer. + * + * NOTE: coded inter (SkipMbFlag=0, CBP set) for changed content was + * implemented and works at QP0 (bit-exact residual) but shows per-MB + * blocky artefacts at higher QP — an encoder/decoder inter MV- + * predictor (mvp) mismatch, analogous to the intra edge-DC corner. + * Left as future work (needs per-MB mvp/neighbour handling); skip is + * the validated path. */ + pak.IndirectPAKMVDataLength = 32; /* 8MV * 4 bytes */ + pak.InlineData[0] = (1u << 8) /* MbType5Bits = 1: BP_L0_16x16 */ + | (4u << 20) /* MvFormat = 100 (8MV) */ + | (1u << 2); /* SkipMbFlag: force P_Skip */ + pak.InlineData[1] = (y << 8) | x; /* Cbp4x4Y = 0 for skip */ + pak.InlineData[2] = 0; /* Cbp4x4V/U = 0 for skip */ + pak.InlineData[4] = 0; /* inter sub-mb shape = 16x16 */ + pak.InlineData[5] = 0; /* ref index L0 = 0 */ + pak.InlineData[6] = 0; /* ref index L1 = 0 */ + pak.InlineData[7] = 0; + } else { + pak.InlineData[0] = 0x000e0000 /* CbpDcY/U/V = 1 */ + | (1u << 13) /* IntraMbFlag */ + | (0x17u << 8); /* MbType5Bits = 23: I_16x16 DC */ + pak.InlineData[1] = 0xFFFF0000 | (y << 8) | x; + pak.InlineData[2] = 0x000F000F; + pak.InlineData[4] = 2; /* LumaIntraMode[0] = DC */ + pak.InlineData[5] = 0; + /* IntraPredAvailFlags must reflect real neighbor availability so PAK + * DC prediction matches the decoder; intra prediction does not cross + * slice boundaries, so a neighbour counts only when its MB address is + * >= slice_start_mb as well as inside the picture. + * bit7 F, bit6 A, bit5 E: left | bit4 B: top | bit3 C: top-right | + * bit2 D: top-left. Bits [1:0] hold ChromaIntraPredMode (DC = 0). */ + uint32_t intra_avail = 0; + if (x > 0 && mb - 1 >= slice_start_mb) + intra_avail |= (1u << 7) | (1u << 6) | (1u << 5); + if (y > 0 && mb - width_in_mbs >= slice_start_mb) + intra_avail |= (1u << 4); + if (y > 0 && x < width_in_mbs - 1 && mb - width_in_mbs + 1 >= slice_start_mb) + intra_avail |= (1u << 3); + if (x > 0 && y > 0 && mb - width_in_mbs - 1 >= slice_start_mb) + intra_avail |= (1u << 2); + pak.InlineData[6] = intra_avail; + pak.InlineData[7] = 0; + } + } + } + + /* Slice tail: two zero bytes, marking end of slice (gen6_mfc_avc). */ + uint32_t tail_data[2] = { 0, 0 }; + uint32_t *tail_dst = + anv_batch_emitn(&cmd_buffer->batch, 2 + 2, GENX(MFX_PAK_INSERT_OBJECT), + .EmulationByteBitsInsertEnable = false, + .SkipEmulationByteCount = 2, + .DataBitsInLastDW = 8, + .LastHeader = true, + .EndofSlice = true); + if (tail_dst) + memcpy(tail_dst + 2, tail_data, sizeof(tail_data)); +} diff --git a/src/intel/vulkan_hasvk/genX_cmd_buffer.c b/src/intel/vulkan_hasvk/genX_cmd_buffer.c index 2027dc3..204931b 100644 --- a/src/intel/vulkan_hasvk/genX_cmd_buffer.c +++ b/src/intel/vulkan_hasvk/genX_cmd_buffer.c @@ -90,7 +90,8 @@ static bool is_video_queue_cmd_buffer(const struct anv_cmd_buffer *cmd_buffer) { struct anv_queue_family *queue_family = cmd_buffer->queue_family; - return (queue_family->queueFlags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) != 0; + return (queue_family->queueFlags & (VK_QUEUE_VIDEO_DECODE_BIT_KHR | + VK_QUEUE_VIDEO_ENCODE_BIT_KHR)) != 0; } void @@ -2196,6 +2197,8 @@ emit_binding_table(struct anv_cmd_buffer *cmd_buffer, case ANV_DESCRIPTOR_SET_SHADER_CONSTANTS: { struct anv_state surface_state = anv_cmd_buffer_alloc_surface_state(cmd_buffer); + if (surface_state.map == NULL) + return VK_ERROR_OUT_OF_DEVICE_MEMORY; struct anv_address constant_data = { .bo = cmd_buffer->device->instruction_state_pool.block_pool.bo, @@ -2224,6 +2227,8 @@ emit_binding_table(struct anv_cmd_buffer *cmd_buffer, struct anv_state surface_state = anv_cmd_buffer_alloc_surface_state(cmd_buffer); + if (surface_state.map == NULL) + return VK_ERROR_OUT_OF_DEVICE_MEMORY; const enum isl_format format = anv_isl_format_for_descriptor_type(cmd_buffer->device, @@ -2382,6 +2387,9 @@ emit_binding_table(struct anv_cmd_buffer *cmd_buffer, surface_state = anv_state_stream_alloc(&cmd_buffer->surface_state_stream, 64, 64); + if (surface_state.map == NULL) + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + enum isl_format format = anv_isl_format_for_descriptor_type(cmd_buffer->device, desc->type); @@ -3220,6 +3228,30 @@ cmd_buffer_emit_streamout(struct anv_cmd_buffer *cmd_buffer) anv_batch_emit_merge(&cmd_buffer->batch, dwords, streamout_state_dw); } +/* Which vertex bindings the VS actually reads. + * + * Without VK_EXT_vertex_input_dynamic_state this was computed once at + * pipeline-creation time; with it, the attribute list is only known at draw + * time so we have to derive it from the dynamic state. + */ +static uint32_t +get_vb_used(const struct anv_graphics_pipeline *pipeline, + const struct vk_dynamic_graphics_state *dyn) +{ + if (!pipeline->dynamic_vertex_input) + return pipeline->vb_used; + + const uint64_t inputs_read = get_vs_prog_data(pipeline)->inputs_read; + uint32_t vb_used = 0; + + u_foreach_bit(a, dyn->vi->attributes_valid) { + if (inputs_read & BITFIELD64_BIT(VERT_ATTRIB_GENERIC0 + a)) + vb_used |= BITFIELD_BIT(dyn->vi->attributes[a].binding); + } + + return vb_used; +} + ALWAYS_INLINE static void genX(cmd_buffer_flush_gfx_state)(struct anv_cmd_buffer *cmd_buffer) { @@ -3240,9 +3272,21 @@ genX(cmd_buffer_flush_gfx_state)(struct anv_cmd_buffer *cmd_buffer) */ genX(cmd_buffer_apply_pipe_flushes)(cmd_buffer); - uint32_t vb_emit = cmd_buffer->state.gfx.vb_dirty & pipeline->vb_used; + /* With dynamic vertex input the pipeline has no baked vb_used; recompute + * which bindings the VS actually reads from the dynamic attribute list. + */ + const uint32_t vb_used = get_vb_used(pipeline, dyn); + + /* A vertex input change alters strides and instancing, so every buffer we + * are about to emit has to be re-emitted along with the new elements. + */ + if (pipeline->dynamic_vertex_input && + BITSET_TEST(dyn->dirty, MESA_VK_DYNAMIC_VI)) + cmd_buffer->state.gfx.vb_dirty |= vb_used; + + uint32_t vb_emit = cmd_buffer->state.gfx.vb_dirty & vb_used; if (cmd_buffer->state.gfx.dirty & ANV_CMD_DIRTY_PIPELINE) - vb_emit |= pipeline->vb_used; + vb_emit |= vb_used; if (vb_emit) { const uint32_t num_buffers = __builtin_popcount(vb_emit); @@ -3261,9 +3305,22 @@ genX(cmd_buffer_flush_gfx_state)(struct anv_cmd_buffer *cmd_buffer) UNUSED uint32_t size = cmd_buffer->state.vertex_bindings[vb].size; #if GFX_VER <= 7 - bool per_instance = pipeline->vb[vb].instanced; - uint32_t divisor = pipeline->vb[vb].instance_divisor * - pipeline->instance_multiplier; + /* On gfx7 instancing lives in VERTEX_BUFFER_STATE rather than in + * a separate 3DSTATE_VF_INSTANCING packet, so it has to track the + * dynamic vertex input too. + */ + bool per_instance; + uint32_t divisor; + if (pipeline->dynamic_vertex_input) { + per_instance = dyn->vi->bindings[vb].input_rate == + VK_VERTEX_INPUT_RATE_INSTANCE; + divisor = dyn->vi->bindings[vb].divisor * + pipeline->instance_multiplier; + } else { + per_instance = pipeline->vb[vb].instanced; + divisor = pipeline->vb[vb].instance_divisor * + pipeline->instance_multiplier; + } #endif state = (struct GENX(VERTEX_BUFFER_STATE)) { @@ -3313,6 +3370,16 @@ genX(cmd_buffer_flush_gfx_state)(struct anv_cmd_buffer *cmd_buffer) cmd_buffer->state.gfx.vb_dirty &= ~vb_emit; + /* With VK_EXT_vertex_input_dynamic_state the pipeline batch carries no + * 3DSTATE_VERTEX_ELEMENTS, so emit it here whenever the vertex input or + * the pipeline (and hence the VS's inputs_read) changed. + */ + if (pipeline->dynamic_vertex_input && + ((cmd_buffer->state.gfx.dirty & ANV_CMD_DIRTY_PIPELINE) || + BITSET_TEST(dyn->dirty, MESA_VK_DYNAMIC_VI))) { + genX(emit_vertex_input)(pipeline, &cmd_buffer->batch, dyn->vi); + } + uint32_t descriptors_dirty = cmd_buffer->state.descriptors_dirty & pipeline->active_stages; if (!cmd_buffer->state.gfx.dirty && !descriptors_dirty && @@ -3632,7 +3699,8 @@ update_dirty_vbs_for_gfx8_vb_flush(struct anv_cmd_buffer *cmd_buffer, struct anv_graphics_pipeline *pipeline = cmd_buffer->state.gfx.pipeline; const struct elk_vs_prog_data *vs_prog_data = get_vs_prog_data(pipeline); - uint64_t vb_used = pipeline->vb_used; + uint64_t vb_used = get_vb_used(pipeline, + &cmd_buffer->vk.dynamic_graphics_state); if (vs_prog_data->uses_firstvertex || vs_prog_data->uses_baseinstance) vb_used |= 1ull << ANV_SVGS_VB_INDEX; @@ -4846,6 +4914,26 @@ void genX(CmdDispatchIndirect)( } #endif +#if GFX_VERx10 < 75 + if (cmd_buffer->state.conditional_render_enabled) { + /* predicate &= conditional render result, computed fresh from the + * buffer since IVB has no GPR to latch it into. + */ + struct anv_cmd_state *cmd_state = &cmd_buffer->state; + + mi_store(&b, mi_reg64(MI_PREDICATE_SRC0), + mi_mem32(cmd_state->conditional_render_value_addr)); + mi_store(&b, mi_reg64(MI_PREDICATE_SRC1), mi_imm(0)); + + anv_batch_emit(batch, GENX(MI_PREDICATE), mip) { + mip.LoadOperation = cmd_state->conditional_render_inverted ? + LOAD_LOAD : LOAD_LOADINV; + mip.CombineOperation = COMBINE_AND; + mip.CompareOperation = COMPARE_SRCS_EQUAL; + } + } +#endif + #else /* GFX_VER > 7 */ if (cmd_buffer->state.conditional_render_enabled) genX(cmd_emit_conditional_render_predicate)(cmd_buffer); @@ -4923,6 +5011,12 @@ genX(flush_pipeline_select_gpgpu)(struct anv_cmd_buffer *cmd_buffer) genX(flush_pipeline_select)(cmd_buffer, GPGPU); } +void +genX(flush_pipeline_select_media)(struct anv_cmd_buffer *cmd_buffer) +{ + genX(flush_pipeline_select)(cmd_buffer, Media); +} + void genX(cmd_buffer_emit_gfx7_depth_flush)(struct anv_cmd_buffer *cmd_buffer) { @@ -5833,6 +5927,32 @@ genX(cmd_emit_conditional_render_predicate)(struct anv_cmd_buffer *cmd_buffer) mip.CombineOperation = COMBINE_SET; mip.CompareOperation = COMPARE_SRCS_EQUAL; } +#else + /* IVB has no GPR, so we can't latch a precomputed result the way + * Gfx7.5+ does with ANV_PREDICATE_RESULT_REG. Instead, re-read the + * conditional render buffer directly before every draw and compute + * the predicate straight into MI_PREDICATE_RESULT. This is spec + * legal: "it's perfectly fine to read a value from the buffer once + * [per draw]" (VK_EXT_conditional_rendering spec commentary). + */ + struct anv_cmd_state *cmd_state = &cmd_buffer->state; + + if (!cmd_state->conditional_render_enabled) + return; + + struct mi_builder b; + mi_builder_init(&b, cmd_buffer->device->info, &cmd_buffer->batch); + + mi_store(&b, mi_reg64(MI_PREDICATE_SRC0), + mi_mem32(cmd_state->conditional_render_value_addr)); + mi_store(&b, mi_reg64(MI_PREDICATE_SRC1), mi_imm(0)); + + anv_batch_emit(&cmd_buffer->batch, GENX(MI_PREDICATE), mip) { + mip.LoadOperation = cmd_state->conditional_render_inverted ? + LOAD_LOAD : LOAD_LOADINV; + mip.CombineOperation = COMBINE_SET; + mip.CompareOperation = COMPARE_SRCS_EQUAL; + } #endif } @@ -5857,27 +5977,35 @@ void genX(CmdBeginConditionalRenderingEXT)( struct mi_builder b; mi_builder_init(&b, cmd_buffer->device->info, &cmd_buffer->batch); - /* Section 19.4 of the Vulkan 1.1.85 spec says: - * - * If the value of the predicate in buffer memory changes - * while conditional rendering is active, the rendering commands - * may be discarded in an implementation-dependent way. - * Some implementations may latch the value of the predicate - * upon beginning conditional rendering while others - * may read it before every rendering command. - * - * So it's perfectly fine to read a value from the buffer once. - */ - struct mi_value value = mi_mem32(value_address); + struct mi_value value = mi_mem32(value_address); - /* Precompute predicate result, it is necessary to support secondary - * command buffers since it is unknown if conditional rendering is - * inverted when populating them. - */ mi_store(&b, mi_reg64(ANV_PREDICATE_RESULT_REG), isInverted ? mi_uge(&b, mi_imm(0), value) : mi_ult(&b, mi_imm(0), value)); } +#else +void genX(CmdBeginConditionalRenderingEXT)( + VkCommandBuffer commandBuffer, + const VkConditionalRenderingBeginInfoEXT* pConditionalRenderingBegin) +{ + ANV_FROM_HANDLE(anv_cmd_buffer, cmd_buffer, commandBuffer); + ANV_FROM_HANDLE(anv_buffer, buffer, pConditionalRenderingBegin->buffer); + struct anv_cmd_state *cmd_state = &cmd_buffer->state; + + cmd_state->conditional_render_enabled = true; + cmd_state->conditional_render_value_addr = + anv_address_add(buffer->address, pConditionalRenderingBegin->offset); + cmd_state->conditional_render_inverted = + pConditionalRenderingBegin->flags & + VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT; + + genX(cmd_buffer_apply_pipe_flushes)(cmd_buffer); + + /* No MI_PREDICATE emission here — no GPR to precompute into. + * cmd_emit_conditional_render_predicate() re-reads the buffer and + * computes the predicate fresh before every draw instead. */ +} +#endif void genX(CmdEndConditionalRenderingEXT)( VkCommandBuffer commandBuffer) @@ -5887,7 +6015,6 @@ void genX(CmdEndConditionalRenderingEXT)( cmd_state->conditional_render_enabled = false; } -#endif /* Set of stage bits for which are pipelined, i.e. they get queued * by the command streamer for later execution. diff --git a/src/intel/vulkan_hasvk/genX_cmd_video.c b/src/intel/vulkan_hasvk/genX_cmd_video.c index 4796fda..64542a0 100644 --- a/src/intel/vulkan_hasvk/genX_cmd_video.c +++ b/src/intel/vulkan_hasvk/genX_cmd_video.c @@ -34,6 +34,8 @@ #include "anv_video_h264_slice.h" +#include "anv_genX.h" + static uint32_t anv_h264_find_slice_nal(const uint8_t *buf, uint32_t off, uint32_t limit) { @@ -120,6 +122,15 @@ genX(CmdBeginVideoCodingKHR) (VkCommandBuffer commandBuffer, cmd_buffer->video.vid = vid; cmd_buffer->video.params = params; + /* Command buffers are reused from the pool and the reset path does not + * touch this struct, so encode_feedback_query_addr may hold a stale address + * from a previous use. Clear it here; otherwise CmdEncodeVideoKHR would try + * to write to that stale address even when no feedback query is active. */ + cmd_buffer->video.encode_feedback_query_addr = ANV_NULL_ADDRESS; + + if (getenv("HASVK_ENC_LOG")) + mesa_logi("ENC CmdBeginVideoCodingKHR op=%u refSlots=%u", + vid->vk.op, pBeginInfo->referenceSlotCount); } void @@ -127,7 +138,8 @@ genX(CmdControlVideoCodingKHR) (VkCommandBuffer commandBuffer, const VkVideoCodingControlInfoKHR * pCodingControlInfo) { - + if (getenv("HASVK_ENC_LOG")) + mesa_logi("ENC CmdControlVideoCodingKHR flags=0x%x", pCodingControlInfo->flags); } void genX(CmdEndVideoCodingKHR) (VkCommandBuffer commandBuffer, @@ -379,9 +391,12 @@ anv_h264_decode_video(struct anv_cmd_buffer *cmd_buffer, #endif } + uint64_t bs_abs = src_buffer->address.offset + frame_info->srcBufferOffset; + struct anv_address bs_base = + { .bo = src_buffer->address.bo, .offset = bs_abs & ~4095ull }; + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_IND_OBJ_BASE_ADDR_STATE), index_obj) { - index_obj.MFXIndirectBitstreamObjectAddress = anv_address_add(src_buffer->address, - frame_info->srcBufferOffset & ~4095); + index_obj.MFXIndirectBitstreamObjectAddress = bs_base; #if GFX_VERx10 == 70 index_obj.MFXIndirectBitstreamObjectCacheabilityControl = 2; #elif GFX_VERx10 == 75 @@ -555,6 +570,9 @@ anv_h264_decode_video(struct anv_cmd_buffer *cmd_buffer, } } + const uint8_t *bs_cpu = bs_map ? bs_map + (uintptr_t)(bs_abs & ~4095ull) : NULL; + uint32_t bs_page_off = (uint32_t)(bs_abs & 4095); + uint32_t mb_width = sps->pic_width_in_mbs_minus1 + 1; uint32_t pic_height_in_map_units = sps->pic_height_in_map_units_minus1 + 1; uint32_t mb_height = sps->flags.frame_mbs_only_flag ? @@ -577,22 +595,20 @@ anv_h264_decode_video(struct anv_cmd_buffer *cmd_buffer, h264_pic_info->pSliceOffsets[s + 1] : frame_info->srcBufferRange; - uint32_t bo_base = frame_info->srcBufferOffset & ~4095u; - uint32_t page_off = frame_info->srcBufferOffset & 4095u; - - uint32_t bo_nalu = bs_map ? - anv_h264_find_slice_nal(bs_map + bo_base, - page_off + slice_off, - page_off + buf_end) : - page_off + slice_off + 3; - uint32_t nalu_size = (page_off + buf_end > bo_nalu) ? - (page_off + buf_end - bo_nalu) : 0; + uint32_t bo_nalu = bs_cpu ? + anv_h264_find_slice_nal(bs_cpu, + bs_page_off + slice_off, + bs_page_off + buf_end) : + bs_page_off + slice_off + 3; + uint32_t nalu_size = (bs_page_off + buf_end > bo_nalu) ? + (bs_page_off + buf_end - bo_nalu) : 0; bool ok = false; - if (bs_map && nalu_size > 0 && - bo_base + bo_nalu + nalu_size <= src_buffer->address.bo->size) { + if (bs_cpu && nalu_size > 0 && + (bs_abs & ~4095ull) + bo_nalu + nalu_size <= + src_buffer->address.bo->size) { ok = anv_h264_parse_slice_header( - bs_map + bo_base, bo_nalu, nalu_size, + bs_cpu, bo_nalu, nalu_size, sps, pps, h264_pic_info, frame_info->referenceSlotCount, frame_info->pReferenceSlots, @@ -615,7 +631,7 @@ anv_h264_decode_video(struct anv_cmd_buffer *cmd_buffer, } } - uint32_t buffer_offset = frame_info->srcBufferOffset & 4095; + uint32_t buffer_offset = bs_page_off; #define HEADER_OFFSET 3 for (unsigned s = 0; s < h264_pic_info->sliceCount; s++) { bool last_slice = s == (h264_pic_info->sliceCount - 1); @@ -887,7 +903,7 @@ anv_h264_decode_video(struct anv_cmd_buffer *cmd_buffer, ss.SliceID = s & (uint32_t)((1u << 4) - 1); } - if (ANV_DEBUG(VIDEO_DEBUG) && field_pic && cur) { + if (ANV_DEBUG(VIDEO_DEBUG) && cur) { char l0[128] = {0}, l1[128] = {0}; for (int e = 0; e < 8; e++) { char t[16]; @@ -909,9 +925,9 @@ anv_h264_decode_video(struct anv_cmd_buffer *cmd_buffer, } } - uint32_t bsd_nal = bs_map ? + uint32_t bsd_nal = bs_cpu ? anv_h264_find_slice_nal( - bs_map + (frame_info->srcBufferOffset & ~4095u), + bs_cpu, buffer_offset + current_offset, buffer_offset + this_end) : buffer_offset + current_offset + HEADER_OFFSET; @@ -968,3 +984,265 @@ genX(CmdDecodeVideoKHR) (VkCommandBuffer commandBuffer, UNREACHABLE("Unsupported video codec operation!"); } } + +/* MMIO offset of the MFC bitstream byte counter for the frame. On a single + * VDBOX (Gen7/7.5) the base is 0, so the raw offset is used directly. Matches + * i965 (i965_defines.h) and mainline anv for GFX_VER < 11. */ +#define MFC_BITSTREAM_BYTECOUNT_FRAME_REG 0x128A0 + +/* Encodes a single H.264 I/IDR frame (CQP, single slice) via the MFC PAK + * pipeline: resolves the source, SPS/PPS, destination buffer and + * reconstructed-picture slot, then emits the static state and per-MB PAK + * objects. Inter prediction and references are not implemented. */ +static VkResult +anv_h264_encode_video(struct anv_cmd_buffer *cmd_buffer, + const VkVideoEncodeInfoKHR *encode_info) +{ + /* Source YUV frame. */ + const struct anv_image_view *iv = + anv_image_view_from_handle(encode_info->srcPictureResource.imageViewBinding); + const struct anv_image *img = iv->image; + + /* Constant QP from pNext -> VkVideoEncodeH264PictureInfoKHR -> + * pNaluSliceEntries[0].constantQp. Single slice, so only the first entry is + * read. */ + const struct VkVideoEncodeH264PictureInfoKHR *h264_pic_info = + vk_find_struct_const(encode_info->pNext, + VIDEO_ENCODE_H264_PICTURE_INFO_KHR); + if (!h264_pic_info || h264_pic_info->naluSliceEntryCount < 1) + return VK_ERROR_INITIALIZATION_FAILED; + + /* Macroblock dimensions from the image extent. */ + uint32_t width_in_mbs = DIV_ROUND_UP(img->vk.extent.width, 16); + uint32_t height_in_mbs = DIV_ROUND_UP(img->vk.extent.height, 16); + uint32_t total_mbs = width_in_mbs * height_in_mbs; + + /* SPS/PPS. The PPS id lives in StdVideoEncodeH264PictureInfo, not in the + * slice header. */ + if (!h264_pic_info->pStdPictureInfo) + return VK_ERROR_INITIALIZATION_FAILED; + uint32_t pps_id = h264_pic_info->pStdPictureInfo->pic_parameter_set_id; + const StdVideoH264PictureParameterSet *pps = + vk_video_find_h264_enc_std_pps(&cmd_buffer->video.params->vk, pps_id); + if (!pps) + return VK_ERROR_INITIALIZATION_FAILED; + const StdVideoH264SequenceParameterSet *sps = + vk_video_find_h264_enc_std_sps(&cmd_buffer->video.params->vk, + pps->seq_parameter_set_id); + if (!sps) + return VK_ERROR_INITIALIZATION_FAILED; + + /* DEBUG (HASVK_ENC_LOG): dump the client-supplied encode parameters so a + * general client (ffmpeg h264_vulkan) can be compared to the test harness. */ + if (getenv("HASVK_ENC_LOG")) { + const struct isl_surf *s0 = &img->planes[0].primary_surface.isl; + const VkVideoInlineQueryInfoKHR *inline_q = + vk_find_struct_const(encode_info->pNext, VIDEO_INLINE_QUERY_INFO_KHR); + const StdVideoEncodeH264SliceHeader *sh0 = + h264_pic_info->pNaluSliceEntries[0].pStdSliceHeader; + mesa_logi("ENC src img %ux%u planes=%u fmt=%u tiling=%u pitch=%u aspects=0x%x", + img->vk.extent.width, img->vk.extent.height, img->n_planes, + s0->format, s0->tiling, s0->row_pitch_B, img->vk.aspects); + if (encode_info->pSetupReferenceSlot && + encode_info->pSetupReferenceSlot->pPictureResource) { + const struct anv_image_view *riv = anv_image_view_from_handle( + encode_info->pSetupReferenceSlot->pPictureResource->imageViewBinding); + const struct isl_surf *rs = &riv->image->planes[0].primary_surface.isl; + mesa_logi("ENC recon img %ux%u planes=%u fmt=%u tiling=%u pitch=%u aspects=0x%x", + riv->image->vk.extent.width, riv->image->vk.extent.height, + riv->image->n_planes, rs->format, rs->tiling, rs->row_pitch_B, + riv->image->vk.aspects); + } + mesa_logi("ENC SPS profile=%u chroma=%u frame_mbs_only=%u wxh_mbs=%ux%u " + "poc_type=%u", + sps->profile_idc, sps->chroma_format_idc, + sps->flags.frame_mbs_only_flag, + sps->pic_width_in_mbs_minus1 + 1, + sps->pic_height_in_map_units_minus1 + 1, sps->pic_order_cnt_type); + mesa_logi("ENC PPS entropy=%u transform8x8=%u deblock_ctrl=%u " + "num_ref_l0=%u init_qp=%d", + pps->flags.entropy_coding_mode_flag, + pps->flags.transform_8x8_mode_flag, + pps->flags.deblocking_filter_control_present_flag, + pps->num_ref_idx_l0_default_active_minus1 + 1, + pps->pic_init_qp_minus26 + 26); + mesa_logi("ENC scaling: sps_present=%u pps_present=%u sps_pScaling=%p " + "pps_pScaling=%p level=%u log2maxfn=%u cip=%u", + sps->flags.seq_scaling_matrix_present_flag, + pps->flags.pic_scaling_matrix_present_flag, + (void *)sps->pScalingLists, (void *)pps->pScalingLists, + sps->level_idc, sps->log2_max_frame_num_minus4, + pps->flags.constrained_intra_pred_flag); + mesa_logi("ENC slices=%u slice0_type=%u qp=%d refSlots=%u setup=%p " + "inline_query=%p dstOff=%llu dstRange=%llu", + h264_pic_info->naluSliceEntryCount, + sh0 ? sh0->slice_type : 99u, + h264_pic_info->pNaluSliceEntries[0].constantQp, + encode_info->referenceSlotCount, + (void *)encode_info->pSetupReferenceSlot, (void *)inline_q, + (unsigned long long)encode_info->dstBufferOffset, + (unsigned long long)encode_info->dstBufferRange); + } + + /* Destination bitstream buffer. MFX_IND_OBJ_BASE_ADDR_STATE holds the + * PAK-BSE object address in bits 31:12 only, so the base handed to the + * hardware must be 4KiB aligned or the low bits are silently dropped and + * the PAK writes land at the wrong address. Align the base down and fold + * the remainder into MFX_AVC_SLICE_STATE's IndirectPAKBSEDataStartAddress, + * a 29-bit byte offset from that base. This mirrors the decode-side + * bitstream base handling. */ + ANV_FROM_HANDLE(anv_buffer, dst_buffer, encode_info->dstBuffer); + uint64_t bs_abs = dst_buffer->address.offset + encode_info->dstBufferOffset; + struct anv_address dst_address = + { .bo = dst_buffer->address.bo, .offset = bs_abs & ~4095ull }; + uint32_t dst_page_off = (uint32_t)(bs_abs & 4095); + + /* Reconstructed picture (pre-deblocking destination). PAK requires a valid + * target since PreDeblockingOutputEnable is set, so the setup DPB slot is + * mandatory; there is no internal recon-image fallback. */ + if (!encode_info->pSetupReferenceSlot || + !encode_info->pSetupReferenceSlot->pPictureResource) + return VK_ERROR_INITIALIZATION_FAILED; + const struct anv_image_view *recon_iv = + anv_image_view_from_handle( + encode_info->pSetupReferenceSlot->pPictureResource->imageViewBinding); + struct anv_address recon_address = + anv_image_dpb_address(recon_iv, + encode_info->pSetupReferenceSlot->pPictureResource->baseArrayLayer); + + /* P frame: the reconstructed picture of the previous frame, supplied as + * pReferenceSlots[0], is the single L0 reference. Absence of a reference + * slot means this is an I frame. */ + bool is_p = encode_info->referenceSlotCount > 0 && + encode_info->pReferenceSlots && + encode_info->pReferenceSlots[0].pPictureResource; + struct anv_address ref_address = ANV_NULL_ADDRESS; + if (is_p) { + const struct anv_image_view *ref_iv = + anv_image_view_from_handle( + encode_info->pReferenceSlots[0].pPictureResource->imageViewBinding); + ref_address = + anv_image_dpb_address(ref_iv, + encode_info->pReferenceSlots[0].pPictureResource->baseArrayLayer); + } + + /* Frame-level static state, emitted once before any slice. */ + genX(hasvk_mfc_emit_static_state)(cmd_buffer, img, recon_address, sps, pps, + dst_address, dst_page_off, + encode_info->dstBufferRange, + width_in_mbs, height_in_mbs, is_p, ref_address); + + /* Emit each slice. Slice s covers the macroblock range + * [first_mb_in_slice[s], first_mb_in_slice[s+1]); the last slice runs to the + * end of the frame. Each NALU slice entry carries its own QP and slice + * header. The MFC appends slices in the destination buffer automatically. */ + uint32_t slice_count = h264_pic_info->naluSliceEntryCount; + for (uint32_t s = 0; s < slice_count; s++) { + const VkVideoEncodeH264NaluSliceInfoKHR *entry = + &h264_pic_info->pNaluSliceEntries[s]; + const StdVideoEncodeH264SliceHeader *sh = entry->pStdSliceHeader; + if (!sh) + return VK_ERROR_INITIALIZATION_FAILED; + if (entry->constantQp < 0 || entry->constantQp > 51) + return VK_ERROR_INITIALIZATION_FAILED; + + uint32_t slice_start_mb = sh->first_mb_in_slice; + uint32_t slice_end_mb = total_mbs; + if (s + 1 < slice_count) { + const StdVideoEncodeH264SliceHeader *next_sh = + h264_pic_info->pNaluSliceEntries[s + 1].pStdSliceHeader; + if (!next_sh) + return VK_ERROR_INITIALIZATION_FAILED; + slice_end_mb = next_sh->first_mb_in_slice; + } + + /* H.264 requires the first slice of a picture to start at macroblock 0 + * (7.4.3: first_mb_in_slice of the first slice is 0). ffmpeg's h264_vulkan + * encoder violates this — it passes first_mb_in_slice=1 for the sole slice. + * Trusting that value skips MB 0, so the MFC only receives total_mbs-1 PAK + * objects for a frame it knows has total_mbs macroblocks; it then waits + * forever for the missing MB and the VCS ring hangs ("stopped heartbeat", + * no page fault, parked on MFC_AVC_PAK_OBJECT). Force the first slice to + * start at MB 0 so the frame is fully covered and the emitted slice header + * stays consistent with the coded data. */ + if (s == 0) + slice_start_mb = 0; + + if (getenv("HASVK_ENC_LOG")) + mesa_logi("ENC slice[%u] first_mb_in_slice=%u -> start=%u end=%u total_mbs=%u", + s, sh->first_mb_in_slice, slice_start_mb, slice_end_mb, total_mbs); + + if (slice_start_mb >= slice_end_mb || slice_end_mb > total_mbs) + return VK_ERROR_INITIALIZATION_FAILED; + + /* Corrected slice header: the bitstream writer emits first_mb_in_slice + * directly from the std struct, so hand it the normalised start MB to keep + * the written header in sync with SLICE_STATE and the PAK object range. */ + StdVideoEncodeH264SliceHeader sh_fixed = *sh; + sh_fixed.first_mb_in_slice = slice_start_mb; + + genX(hasvk_mfc_emit_slice)(cmd_buffer, sps, pps, + h264_pic_info->pStdPictureInfo, &sh_fixed, + slice_start_mb, slice_end_mb, + width_in_mbs, height_in_mbs, + (uint32_t)entry->constantQp, + s + 1 == slice_count, dst_page_off); + } + + /* Write the VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR result here, where the + * real dstBufferOffset/Range is known. slot[0] = availability, slot[8] = + * bitstream buffer offset, slot[16] = bytes written. The byte count is read + * from the MFC bitstream byte counter (MFC_BITSTREAM_BYTECOUNT_FRAME_REG), + * which holds the number of bytes the PAK unit emitted for the frame. The + * counter is only stable after the MFC pipe has drained, so a CS stall plus + * MFX_WAIT is issued first (mirroring the frame-end sync in + * CmdEndVideoCodingKHR). */ + if (cmd_buffer->video.encode_feedback_query_addr.bo != NULL) { + struct anv_address addr = cmd_buffer->video.encode_feedback_query_addr; + +#if GFX_VER == 8 + anv_batch_emit(&cmd_buffer->batch, GENX(MI_FLUSH_DW), flush) { + flush.PostSyncOperation = 0; + } +#elif GFX_VER <= 75 + anv_batch_emit(&cmd_buffer->batch, GENX(PIPE_CONTROL), pc) { + pc.CommandStreamerStallEnable = 1; + pc.StallAtPixelScoreboard = 1; + } + anv_batch_emit(&cmd_buffer->batch, GENX(MFX_WAIT), wait) { + wait.MFXSyncControlFlag = 1; + } +#endif + + genX(emit_query_mi_write_u64)(cmd_buffer, addr, 1); + genX(emit_query_mi_write_u64)(cmd_buffer, anv_address_add(addr, 8), 0); + genX(emit_query_mi_write_reg32)(cmd_buffer, anv_address_add(addr, 16), + MFC_BITSTREAM_BYTECOUNT_FRAME_REG); + } + + return VK_SUCCESS; +} + +void +genX(CmdEncodeVideoKHR) (VkCommandBuffer commandBuffer, + const VkVideoEncodeInfoKHR * pEncodeInfo) +{ + ANV_FROM_HANDLE(anv_cmd_buffer, cmd_buffer, commandBuffer); + + if (!cmd_buffer->video.vid) + abort(); + + switch (cmd_buffer->video.vid->vk.op) { + case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: { + /* vkCmdEncodeVideoKHR returns void, so on failure the command buffer is + * marked errored and the error is surfaced at vkEndCommandBuffer / + * vkQueueSubmit. */ + VkResult result = anv_h264_encode_video(cmd_buffer, pEncodeInfo); + if (result != VK_SUCCESS) + anv_batch_set_error(&cmd_buffer->batch, result); + break; + } + default: + UNREACHABLE("Unsupported video codec operation!"); + } +} diff --git a/src/intel/vulkan_hasvk/genX_pipeline.c b/src/intel/vulkan_hasvk/genX_pipeline.c index d4a950c..57f4fe8 100644 --- a/src/intel/vulkan_hasvk/genX_pipeline.c +++ b/src/intel/vulkan_hasvk/genX_pipeline.c @@ -89,9 +89,16 @@ vertex_element_comp_control(enum isl_format format, unsigned comp) } } -static void -emit_vertex_input(struct anv_graphics_pipeline *pipeline, - const struct vk_vertex_input_state *vi) +/* Emits 3DSTATE_VERTEX_ELEMENTS (plus VF_INSTANCING/VF_SGVS on Gfx8+) for the + * given vertex input state. With VK_EXT_vertex_input_dynamic_state this is + * called from the command buffer at draw time; otherwise it is baked into the + * pipeline batch at pipeline-creation time. Everything it needs beyond the + * pipeline itself comes from `vi`, so both callers share this one path. + */ +void +genX(emit_vertex_input)(struct anv_graphics_pipeline *pipeline, + struct anv_batch *batch, + const struct vk_vertex_input_state *vi) { const struct elk_vs_prog_data *vs_prog_data = get_vs_prog_data(pipeline); @@ -116,7 +123,7 @@ emit_vertex_input(struct anv_graphics_pipeline *pipeline, uint32_t *p; const uint32_t num_dwords = 1 + total_elems * 2; - p = anv_batch_emitn(&pipeline->base.batch, num_dwords, + p = anv_batch_emitn(batch, num_dwords, GENX(3DSTATE_VERTEX_ELEMENTS)); if (!p) return; @@ -185,9 +192,10 @@ emit_vertex_input(struct anv_graphics_pipeline *pipeline, * that controls instancing. On Haswell and prior, that's part of * VERTEX_BUFFER_STATE which we emit later. */ - anv_batch_emit(&pipeline->base.batch, GENX(3DSTATE_VF_INSTANCING), vfi) { - bool per_instance = pipeline->vb[binding].instanced; - uint32_t divisor = pipeline->vb[binding].instance_divisor * + anv_batch_emit(batch, GENX(3DSTATE_VF_INSTANCING), vfi) { + bool per_instance = vi->bindings[binding].input_rate == + VK_VERTEX_INPUT_RATE_INSTANCE; + uint32_t divisor = vi->bindings[binding].divisor * pipeline->instance_multiplier; vfi.InstancingEnable = per_instance; @@ -229,14 +237,14 @@ emit_vertex_input(struct anv_graphics_pipeline *pipeline, GENX(VERTEX_ELEMENT_STATE_pack)(NULL, &p[1 + id_slot * 2], &element); #if GFX_VER >= 8 - anv_batch_emit(&pipeline->base.batch, GENX(3DSTATE_VF_INSTANCING), vfi) { + anv_batch_emit(batch, GENX(3DSTATE_VF_INSTANCING), vfi) { vfi.VertexElementIndex = id_slot; } #endif } #if GFX_VER >= 8 - anv_batch_emit(&pipeline->base.batch, GENX(3DSTATE_VF_SGVS), sgvs) { + anv_batch_emit(batch, GENX(3DSTATE_VF_SGVS), sgvs) { sgvs.VertexIDEnable = vs_prog_data->uses_vertexid; sgvs.VertexIDComponentNumber = 2; sgvs.VertexIDElementOffset = id_slot; @@ -262,7 +270,7 @@ emit_vertex_input(struct anv_graphics_pipeline *pipeline, &element); #if GFX_VER >= 8 - anv_batch_emit(&pipeline->base.batch, GENX(3DSTATE_VF_INSTANCING), vfi) { + anv_batch_emit(batch, GENX(3DSTATE_VF_INSTANCING), vfi) { vfi.VertexElementIndex = drawid_slot; } #endif @@ -1886,7 +1894,8 @@ genX(graphics_pipeline_emit)(struct anv_graphics_pipeline *pipeline, gfx7_emit_vs_workaround_flush(elk); #endif - emit_vertex_input(pipeline, state->vi); + if (!BITSET_TEST(state->dynamic, MESA_VK_DYNAMIC_VI)) + genX(emit_vertex_input)(pipeline, &pipeline->base.batch, state->vi); /* DIAGNOSTIC v2: always emit here (both Gen7 and Gen8+), matching * vanilla hasvk's original position exactly. */ diff --git a/src/intel/vulkan_hasvk/genX_query.c b/src/intel/vulkan_hasvk/genX_query.c index 2220223..0baeca0 100644 --- a/src/intel/vulkan_hasvk/genX_query.c +++ b/src/intel/vulkan_hasvk/genX_query.c @@ -71,6 +71,35 @@ genX(emit_query_mi_flush_availability)(struct anv_cmd_buffer *cmd_buffer, } } +/* Writes an arbitrary 64-bit value to a query slot, unlike + * emit_query_mi_flush_availability() which only writes a boolean. Used for the + * buffer-offset and bytes-written values of a video encode feedback query. */ +void +genX(emit_query_mi_write_u64)(struct anv_cmd_buffer *cmd_buffer, + struct anv_address addr, + uint64_t value) +{ + anv_batch_emit(&cmd_buffer->batch, GENX(MI_FLUSH_DW), flush) { + flush.PostSyncOperation = WriteImmediateData; + flush.Address = addr; + flush.ImmediateData = value; + } +} + +/* Copies a 32-bit MMIO register into a query slot, zero-extended to 64 bits. + * Used to read the MFC bitstream byte counter (MFC_BITSTREAM_BYTECOUNT_FRAME) + * into the bytes-written field of a video encode feedback query. The caller + * must have drained the MFC pipe first so the counter holds the final value. */ +void +genX(emit_query_mi_write_reg32)(struct anv_cmd_buffer *cmd_buffer, + struct anv_address addr, + uint32_t reg) +{ + struct mi_builder b; + mi_builder_init(&b, cmd_buffer->device->info, &cmd_buffer->batch); + mi_store(&b, mi_mem64(addr), mi_reg32(reg)); +} + VkResult genX(CreateQueryPool)( VkDevice _device, const VkQueryPoolCreateInfo* pCreateInfo, @@ -178,6 +207,10 @@ VkResult genX(CreateQueryPool)( case VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR: uint64s_per_slot = 1; break; + case VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR: + /* availability + buffer-offset + bytes-written. */ + uint64s_per_slot = 1 + 2; + break; default: UNREACHABLE("Invalid query type"); } @@ -469,7 +502,8 @@ VkResult genX(GetQueryPoolResults)( pool->type == VK_QUERY_TYPE_PERFORMANCE_QUERY_KHR || pool->type == VK_QUERY_TYPE_PERFORMANCE_QUERY_INTEL || pool->type == VK_QUERY_TYPE_PRIMITIVES_GENERATED_EXT || - pool->type == VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR); + pool->type == VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR || + pool->type == VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR); if (vk_device_is_lost(&device->vk)) return VK_ERROR_DEVICE_LOST; @@ -615,6 +649,23 @@ VkResult genX(GetQueryPoolResults)( cpu_write_query_result(pData, flags, idx, result); break; + case VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR: { + /* slot[0] = availability, slot[1] = buffer offset (always 0 for a + * single slice), slot[2] = bytes written (the MFC bitstream byte + * count read from MFC_BITSTREAM_BYTECOUNT_FRAME_REG; see + * anv_h264_encode_video()). */ + uint64_t *slot = query_slot(pool, firstQuery + i); + if (write_results) { + cpu_write_query_result(pData, flags, idx, slot[1]); + cpu_write_query_result(pData, flags, idx + 1, slot[2]); + } + /* Two feedback values (offset, bytes written); advance past both so an + * appended availability/status word lands after them rather than + * overwriting the byte count. */ + idx += 2; + break; + } + default: UNREACHABLE("invalid pool type"); } @@ -625,6 +676,17 @@ VkResult genX(GetQueryPoolResults)( if (flags & VK_QUERY_RESULT_WITH_AVAILABILITY_BIT) cpu_write_query_result(pData, flags, idx, available); + /* VK_KHR_video_queue: when the app requests the per-query status (as + * ffmpeg's h264_vulkan does for encode-feedback queries), append a + * VkQueryResultStatusKHR. A completed query reports COMPLETE (1); an + * unavailable one reports NOT_READY (0). Without this the app reads a + * stale/zero status and treats the encode as failed ("Unable to + * encode: 0") even though the bitstream was produced. */ + if (flags & VK_QUERY_RESULT_WITH_STATUS_BIT_KHR) + cpu_write_query_result(pData, flags, idx, + available ? VK_QUERY_RESULT_STATUS_COMPLETE_KHR + : VK_QUERY_RESULT_STATUS_NOT_READY_KHR); + pData += stride; if (pData >= data_end) break; @@ -818,6 +880,13 @@ void genX(CmdResetQueryPool)( genX(emit_query_mi_flush_availability)(cmd_buffer, anv_query_address(pool, firstQuery + i), false); break; + case VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR: + /* Without this case the query type would fall through to the UNREACHABLE + * default, which under -DNDEBUG is undefined behavior on reset. */ + for (uint32_t i = 0; i < queryCount; i++) + genX(emit_query_mi_flush_availability)(cmd_buffer, anv_query_address(pool, firstQuery + i), false); + break; + default: UNREACHABLE("Unsupported query type"); } @@ -1119,6 +1188,14 @@ void genX(CmdBeginQueryIndexedEXT)( case VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR: genX(emit_query_mi_flush_availability)(cmd_buffer, query_addr, false); break; + case VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR: + /* The offset/bytes-written values are written by the enclosed + * vkCmdEncodeVideoKHR (only it knows the real dstBufferOffset/Range; see + * anv_h264_encode_video()). Here we just mark the query not-ready and + * record the address for the encode command to write to. */ + genX(emit_query_mi_flush_availability)(cmd_buffer, query_addr, false); + cmd_buffer->video.encode_feedback_query_addr = query_addr; + break; default: UNREACHABLE(""); } @@ -1290,6 +1367,13 @@ void genX(CmdEndQueryIndexedEXT)( genX(emit_query_mi_flush_availability)(cmd_buffer, query_addr, true); break; + case VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR: + /* The data and availability were already written by + * anv_h264_encode_video() earlier in the same batch; just clear the + * active-query tracking here. */ + cmd_buffer->video.encode_feedback_query_addr = ANV_NULL_ADDRESS; + break; + default: UNREACHABLE(""); } diff --git a/src/intel/vulkan_hasvk/genX_state.c b/src/intel/vulkan_hasvk/genX_state.c index 6cb5572..46cb9a6 100644 --- a/src/intel/vulkan_hasvk/genX_state.c +++ b/src/intel/vulkan_hasvk/genX_state.c @@ -408,7 +408,7 @@ VkResult genX(CreateSampler)( pCreateInfo->borderColor * border_color_stride; } else { - assert(GFX_VER >= 8); + assert(GFX_VER >= 7); sampler->custom_border_color = anv_state_reserved_pool_alloc(&device->custom_border_colors); border_color_offset = sampler->custom_border_color.offset; diff --git a/src/intel/vulkan_hasvk/meson.build b/src/intel/vulkan_hasvk/meson.build index 68223ba..ec54979 100644 --- a/src/intel/vulkan_hasvk/meson.build +++ b/src/intel/vulkan_hasvk/meson.build @@ -63,6 +63,7 @@ anv_per_hw_ver_files = files( 'genX_pipeline.c', 'genX_query.c', 'genX_state.c', + 'anv_video_mfc.c', ) foreach g : [['70', ['gfx7_cmd_buffer.c']], ['75', ['gfx7_cmd_buffer.c']], @@ -100,6 +101,7 @@ libanv_files = files( 'anv_formats.c', 'anv_genX.h', 'anv_image.c', + 'anv_host_image_copy.c', 'anv_measure.c', 'anv_measure.h', 'anv_nir.h', @@ -116,6 +118,7 @@ libanv_files = files( 'anv_util.c', 'anv_utrace.c', 'anv_video.c', + 'anv_video_h264_bitstream.c', 'anv_video_h264_slice.c', 'anv_wsi.c', ) diff --git a/src/intel/vulkan_hasvk/shaders/vme/intra_frame_ivb.g7b b/src/intel/vulkan_hasvk/shaders/vme/intra_frame_ivb.g7b new file mode 100644 index 0000000..d2aa1eb --- /dev/null +++ b/src/intel/vulkan_hasvk/shaders/vme/intra_frame_ivb.g7b @@ -0,0 +1,53 @@ + { 0x00800001, 0x24000061, 0x00000000, 0x00000000 }, + { 0x00800001, 0x24400061, 0x00000000, 0x00000000 }, + { 0x00800001, 0x24800061, 0x00000000, 0x00000000 }, + { 0x00800001, 0x24c00061, 0x00000000, 0x00000000 }, + { 0x00200009, 0x24002e25, 0x004500a0, 0x00040004 }, + { 0x00000040, 0x24003ca5, 0x00000400, 0xfff8fff8 }, + { 0x00000040, 0x24043ca5, 0x00000404, 0xffffffff }, + { 0x00000001, 0x240800e1, 0x00000000, 0x0000001f }, + { 0x00000001, 0x24140231, 0x00000014, 0x00000000 }, + { 0x00200009, 0x24202e25, 0x004500a0, 0x00040004 }, + { 0x00000040, 0x24203ca5, 0x00000420, 0xfffcfffc }, + { 0x00000001, 0x242800e1, 0x00000000, 0x000f0003 }, + { 0x00000001, 0x24340231, 0x00000014, 0x00000000 }, + { 0x00200009, 0x24482e29, 0x004500a0, 0x00040004 }, + { 0x00000001, 0x24540231, 0x00000014, 0x00000000 }, + { 0x00000041, 0x24884521, 0x000000a2, 0x000000a1 }, + { 0x00000040, 0x24884421, 0x00000488, 0x000000a0 }, + { 0x00000001, 0x24940231, 0x00000014, 0x00000000 }, + { 0x00600001, 0x28000021, 0x008d0400, 0x00000000 }, + { 0x04600031, 0x23801cb1, 0x00000800, 0x02190004 }, + { 0x00600001, 0x28000021, 0x008d0420, 0x00000000 }, + { 0x04600031, 0x23a01cb1, 0x00000800, 0x02290004 }, + { 0x00600001, 0x28000021, 0x008d0440, 0x00000000 }, + { 0x00600001, 0x28400061, 0x00000000, 0x00000000 }, + { 0x00600001, 0x28000021, 0x008d0440, 0x00000000 }, + { 0x00600001, 0x24600061, 0x00000000, 0x00000000 }, + { 0x01000005, 0x20002e28, 0x000000a4, 0x00010001 }, + { 0x00010001, 0x247c0171, 0x00000000, 0x00020002 }, + { 0x00000001, 0x247d0231, 0x000000a5, 0x00000000 }, + { 0x00600001, 0x28200021, 0x008d0460, 0x00000000 }, + { 0x00600001, 0x28400061, 0x00000000, 0x00000000 }, + { 0x00000001, 0x23800061, 0x00000000, 0x00000000 }, + { 0x00000005, 0x23840c21, 0x00000384, 0xff000000 }, + { 0x00600001, 0x28600021, 0x008d0380, 0x00000000 }, + { 0x00600001, 0x288000e1, 0x00000000, 0x00000000 }, + { 0x00800001, 0x28800231, 0x00cf03a3, 0x00000000 }, + { 0x00000001, 0x28900061, 0x00000000, 0x11111111 }, + { 0x00000001, 0x24000221, 0x000000a8, 0x00000000 }, + { 0x00000005, 0x24200c21, 0x00000400, 0x00000006 }, + { 0x00000009, 0x24040c21, 0x00000420, 0x0000000a }, + { 0x00000005, 0x24200c21, 0x00000400, 0x00000038 }, + { 0x00000009, 0x24240c21, 0x00000420, 0x00000005 }, + { 0x00000040, 0x24000421, 0x00000404, 0x00000424 }, + { 0x00000040, 0x22000c20, 0x00000400, 0x0a184000 }, + { 0x08000031, 0x21800221, 0x00000800, 0x00000200 }, + { 0x00600001, 0x28000021, 0x008d0480, 0x00000000 }, + { 0x00000001, 0x28200021, 0x00000180, 0x00000000 }, + { 0x00000001, 0x28240021, 0x00000190, 0x00000000 }, + { 0x00000001, 0x28280021, 0x00000194, 0x00000000 }, + { 0x00000001, 0x282c0021, 0x00000198, 0x00000000 }, + { 0x0a800031, 0x20001cac, 0x00000800, 0x040a0003 }, + { 0x00600001, 0x2e000021, 0x008d0000, 0x00000000 }, + { 0x07800031, 0x24001ca8, 0x00000e00, 0x82000010 }, diff --git a/subprojects/SPIRV-LLVM-Translator.wrap b/subprojects/SPIRV-LLVM-Translator.wrap new file mode 100644 index 0000000..4be5df2 --- /dev/null +++ b/subprojects/SPIRV-LLVM-Translator.wrap @@ -0,0 +1,8 @@ +[wrap-file] +directory = SPIRV-LLVM-Translator-19.1.19 +source_url = https://github.com/KhronosGroup/SPIRV-LLVM-Translator/archive/refs/tags/v19.1.19.tar.gz +source_filename = SPIRV-LLVM-Translator-19.1.19.tar.gz +source_hash = 284a7e6b33e80e221bdba9b606971590c19a86adee1c89c868474996d9d91058 + +[provide] +LLVMSPIRVLib = spirvlib_dep