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