From: Elizabeth Figura <zfigura@codeweavers.com> --- dlls/wined3d/decoder.c | 214 ++++++++++++++++++++++++++++++++--- dlls/wined3d/unixlib.h | 9 ++ dlls/wined3d/va.c | 248 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 455 insertions(+), 16 deletions(-) diff --git a/dlls/wined3d/decoder.c b/dlls/wined3d/decoder.c index e335adce7cb..bf59265c128 100644 --- a/dlls/wined3d/decoder.c +++ b/dlls/wined3d/decoder.c @@ -1367,6 +1367,8 @@ struct wined3d_decoder_va_vk { VkImage image; uint64_t command_buffer_id; + bool valid, used; + uint8_t dxva_index; } images[VA_DECODER_SURFACE_COUNT]; }; @@ -1395,13 +1397,12 @@ static void wined3d_decoder_va_vk_get_profiles(struct wined3d_adapter *adapter, WINE_UNIX_CALL(unix_va_get_profiles_vk, ¶ms); } -static void wined3d_decoder_va_vk_cs_init(void *object) +static void wined3d_decoder_va_vk_create_va_decoder(struct wined3d_decoder_va_vk *decoder_va, + struct wined3d_context_vk *context_vk) { - struct wined3d_decoder_va_vk *decoder_va = object; struct wined3d_adapter_vk *adapter_vk = wined3d_adapter_vk(decoder_va->d.device->adapter); struct wined3d_device_vk *device_vk = wined3d_device_vk(decoder_va->d.device); struct va_decoder_create_vk_params params; - struct wined3d_context_vk *context_vk; HRESULT hr; params.desc = decoder_va->d.desc; @@ -1418,8 +1419,6 @@ static void wined3d_decoder_va_vk_cs_init(void *object) decoder_va->va_decoder = params.decoder; - context_vk = wined3d_context_vk(context_acquire(&device_vk->d, NULL, 0)); - for (unsigned int i = 0; i < VA_DECODER_SURFACE_COUNT; ++i) { struct wined3d_decoder_image_va_vk *image = &decoder_va->images[i]; @@ -1434,7 +1433,15 @@ static void wined3d_decoder_va_vk_cs_init(void *object) VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, image->image, &vk_range); } +} +static void wined3d_decoder_va_vk_cs_init(void *object) +{ + struct wined3d_decoder_va_vk *decoder_va = object; + struct wined3d_context_vk *context_vk; + + context_vk = wined3d_context_vk(context_acquire(decoder_va->d.device, NULL, 0)); + wined3d_decoder_va_vk_create_va_decoder(decoder_va, context_vk); context_release(&context_vk->c); } @@ -1477,16 +1484,11 @@ void wined3d_decoder_va_vk_destroy_va_decoder(struct wined3d_device_vk *device_v WINE_UNIX_CALL(unix_va_decoder_destroy_vk, ¶ms); } -static void wined3d_decoder_va_vk_destroy_object(void *object) +static void wined3d_decoder_va_vk_cleanup(struct wined3d_decoder_va_vk *decoder_va, + struct wined3d_context_vk *context_vk) { - struct wined3d_decoder_va_vk *decoder_va = object; - struct wined3d_context_vk *context_vk; uint64_t command_buffer_id = 0; - TRACE("decoder_va %p.\n", decoder_va); - - context_vk = wined3d_context_vk(context_acquire(decoder_va->d.device, NULL, 0)); - for (unsigned int i = 0; i < ARRAY_SIZE(decoder_va->images); ++i) { struct wined3d_decoder_image_va_vk *image = &decoder_va->images[i]; @@ -1499,9 +1501,19 @@ static void wined3d_decoder_va_vk_destroy_object(void *object) * a Vulkan object, but we did allocate Vulkan memory on the Unix side * which we need to make sure isn't freed until the GPU is done with it. */ wined3d_context_vk_destroy_va_decoder(context_vk, decoder_va->va_decoder, command_buffer_id); - free(decoder_va); +} + +static void wined3d_decoder_va_vk_destroy_object(void *object) +{ + struct wined3d_decoder_va_vk *decoder_va = object; + struct wined3d_context_vk *context_vk; + + TRACE("decoder_va %p.\n", decoder_va); + context_vk = wined3d_context_vk(context_acquire(decoder_va->d.device, NULL, 0)); + wined3d_decoder_va_vk_cleanup(decoder_va, context_vk); context_release(&context_vk->c); + free(decoder_va); } static void wined3d_decoder_va_vk_destroy(struct wined3d_decoder *decoder) @@ -1512,11 +1524,185 @@ static void wined3d_decoder_va_vk_destroy(struct wined3d_decoder *decoder) wined3d_cs_destroy_object(decoder->device->cs, wined3d_decoder_va_vk_destroy_object, decoder_va); } +static bool decoder_va_vk_find_reference_slot(struct wined3d_decoder_va_vk *decoder_va, + uint8_t dxva_index, unsigned int *slot) +{ + for (unsigned int i = 0; i < VA_DECODER_SURFACE_COUNT; ++i) + { + if (decoder_va->images[i].valid && decoder_va->images[i].dxva_index == dxva_index) + { + *slot = i; + return true; + } + } + + ERR("Reference index %u was never written.\n", dxva_index); + return false; +} + +static unsigned int decoder_va_vk_find_available_output_slot(struct wined3d_decoder_va_vk *decoder_va, + struct wined3d_context_vk *context_vk) +{ + uint64_t earliest_command_buffer_id = UINT64_MAX; + unsigned int earliest_slot = 0; + + for (unsigned int i = 0; i < VA_DECODER_SURFACE_COUNT; ++i) + { + struct wined3d_decoder_image_va_vk *image = &decoder_va->images[i]; + + if (image->used) + continue; + + if (!image->valid || context_vk->completed_command_buffer_id >= image->command_buffer_id) + return i; + + if (image->command_buffer_id < earliest_command_buffer_id) + { + earliest_command_buffer_id = image->command_buffer_id; + earliest_slot = i; + } + } + + /* We need to make sure that Vulkan is done reading from this image. + * Unfortunately, we cannot do GPU-side synchronization between VA and + * Vulkan. We need to do a CPU wait. */ + if (earliest_command_buffer_id == context_vk->current_command_buffer.id) + wined3d_context_vk_submit_command_buffer(context_vk, 0, NULL, NULL, 0, NULL); + wined3d_context_vk_wait_command_buffer(context_vk, earliest_command_buffer_id); + return earliest_slot; +} + +/* Copy from the shared VA image to the application destination image. */ +static void blit_va_image(struct wined3d_decoder_va_vk *decoder_va, struct wined3d_context_vk *context_vk, + unsigned int output_idx, struct wined3d_decoder_output_view_vk *output_view_vk) +{ + struct wined3d_texture_vk *texture_vk = wined3d_texture_vk(output_view_vk->v.texture); + unsigned int sub_resource_idx = output_view_vk->v.desc.u.texture.layer_idx; + const struct wined3d_vk_info *vk_info = context_vk->vk_info; + VkCommandBuffer command_buffer; + VkImageSubresourceRange range; + VkImageLayout dst_layout; + VkImageCopy region = {0}; + + command_buffer = wined3d_context_vk_get_command_buffer(context_vk); + + if (texture_vk->layout == VK_IMAGE_LAYOUT_GENERAL) + dst_layout = VK_IMAGE_LAYOUT_GENERAL; + else + dst_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + range.aspectMask = vk_aspect_mask_from_format(texture_vk->t.resource.format); + range.baseMipLevel = sub_resource_idx % texture_vk->t.level_count; + range.levelCount = 1; + range.baseArrayLayer = sub_resource_idx / texture_vk->t.level_count; + range.layerCount = 1; + + wined3d_context_vk_image_barrier(context_vk, command_buffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + vk_access_mask_from_bind_flags(texture_vk->t.resource.bind_flags), + VK_ACCESS_TRANSFER_WRITE_BIT, + texture_vk->layout, dst_layout, texture_vk->image.vk_image, &range); + + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT; + region.srcSubresource.baseArrayLayer = 0; + region.srcSubresource.layerCount = 1; + region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT; + region.dstSubresource.baseArrayLayer = sub_resource_idx; + region.dstSubresource.layerCount = 1; + region.extent.width = decoder_va->d.width; + region.extent.height = decoder_va->d.height; + region.extent.depth = 1; + + VK_CALL(vkCmdCopyImage(command_buffer, decoder_va->images[output_idx].image, + VK_IMAGE_LAYOUT_GENERAL, texture_vk->image.vk_image, dst_layout, 1, ®ion)); + region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_1_BIT; + region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_1_BIT; + region.extent.width /= 2; + region.extent.height /= 2; + VK_CALL(vkCmdCopyImage(command_buffer, decoder_va->images[output_idx].image, + VK_IMAGE_LAYOUT_GENERAL, texture_vk->image.vk_image, dst_layout, 1, ®ion)); + + wined3d_context_vk_image_barrier(context_vk, command_buffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_ACCESS_TRANSFER_WRITE_BIT, + vk_access_mask_from_bind_flags(texture_vk->t.resource.bind_flags), + dst_layout, texture_vk->layout, texture_vk->image.vk_image, &range); + + decoder_va->images[output_idx].command_buffer_id = context_vk->current_command_buffer.id; +} + static void wined3d_decoder_va_vk_decode(struct wined3d_context *context, struct wined3d_decoder *decoder, struct wined3d_decoder_output_view *output_view, unsigned int bitstream_size, unsigned int slice_control_size) { - FIXME("Not implemented.\n"); + struct wined3d_decoder_output_view_vk *output_view_vk = wined3d_decoder_output_view_vk(output_view); + const DXVA_PicParams_H264 *h264_params = wined3d_buffer_load_sysmem(decoder->parameters, context); + struct wined3d_decoder_va_vk *decoder_va = wined3d_decoder_va_vk(decoder); + unsigned int sub_resource_idx = output_view_vk->v.desc.u.texture.layer_idx; + struct wined3d_context_vk *context_vk = wined3d_context_vk(context); + struct wined3d_texture *texture = output_view->texture; + struct va_decoder_decode_params params; + unsigned int output_idx, width, height; + + wined3d_texture_prepare_location(texture, sub_resource_idx, &context_vk->c, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_validate_location(texture, sub_resource_idx, WINED3D_LOCATION_TEXTURE_RGB); + wined3d_texture_invalidate_location(texture, sub_resource_idx, ~WINED3D_LOCATION_TEXTURE_RGB); + + for (unsigned int i = 0; i < VA_DECODER_SURFACE_COUNT; ++i) + decoder_va->images[i].used = false; + + for (unsigned int i = 0; i < ARRAY_SIZE(h264_params->RefFrameList); ++i) + { + unsigned int slot_index; + + if (h264_params->RefFrameList[i].bPicEntry == 0xff) + continue; + + /* NVidia's DXVA implementation apparently expects each frame to appear + * in its own references list. VA does not expect or need this. */ + if (h264_params->RefFrameList[i].Index7Bits == h264_params->CurrPic.Index7Bits) + continue; + + if (!decoder_va_vk_find_reference_slot(decoder_va, h264_params->RefFrameList[i].Index7Bits, &slot_index)) + return; + + decoder_va->images[slot_index].used = true; + } + + output_idx = decoder_va_vk_find_available_output_slot(decoder_va, context_vk); + decoder_va->images[output_idx].valid = true; + decoder_va->images[output_idx].dxva_index = h264_params->CurrPic.Index7Bits; + + width = (h264_params->wFrameWidthInMbsMinus1 + 1) * 16; + height = (h264_params->wFrameHeightInMbsMinus1 + 1) * 16; + if (width != decoder_va->d.width || height != decoder_va->d.height) + { + TRACE("Dynamic resize from %ux%u to %ux%u.\n", decoder_va->d.width, decoder_va->d.height, width, height); + wined3d_decoder_va_vk_cleanup(decoder_va, context_vk); + decoder_va->d.width = width; + decoder_va->d.height = height; + wined3d_decoder_va_vk_create_va_decoder(decoder_va, context_vk); + } + + params.decoder = decoder_va->va_decoder; + params.bitstream = (uintptr_t)wined3d_buffer_load_sysmem(decoder_va->d.bitstream, context); + params.parameters = (uintptr_t)wined3d_buffer_load_sysmem(decoder_va->d.parameters, context); + params.matrix = (uintptr_t)wined3d_buffer_load_sysmem(decoder_va->d.matrix, context); + params.slice_control = (uintptr_t)wined3d_buffer_load_sysmem(decoder_va->d.slice_control, context); + params.bitstream_size = bitstream_size; + params.slice_control_size = slice_control_size; + params.output_idx = output_idx; + WINE_UNIX_CALL(unix_va_decoder_decode, ¶ms); + + blit_va_image(decoder_va, context_vk, output_idx, output_view_vk); + + EnterCriticalSection(&decoder_va->d.feedback_cs); + decoder_va->d.feedback_number = h264_params->StatusReportFeedbackNumber; + decoder_va->d.feedback_pic_entry = h264_params->CurrPic.bPicEntry; + decoder_va->d.feedback_field = h264_params->field_pic_flag; + LeaveCriticalSection(&decoder_va->d.feedback_cs); + + wined3d_context_vk_reference_texture(context_vk, wined3d_texture_vk(texture)); } const struct wined3d_decoder_ops wined3d_decoder_va_vk_ops = diff --git a/dlls/wined3d/unixlib.h b/dlls/wined3d/unixlib.h index ec53d636afb..6e5a83da21a 100644 --- a/dlls/wined3d/unixlib.h +++ b/dlls/wined3d/unixlib.h @@ -58,11 +58,20 @@ struct va_decoder_destroy_vk_params UINT64 decoder; }; +struct va_decoder_decode_params +{ + UINT64 decoder; + UINT64 bitstream, parameters, matrix, slice_control; + UINT32 bitstream_size, slice_control_size; + UINT32 output_idx; +}; + enum unix_funcs { unix_va_get_profiles_vk, unix_va_decoder_create_vk, unix_va_decoder_destroy_vk, + unix_va_decoder_decode, }; #endif diff --git a/dlls/wined3d/va.c b/dlls/wined3d/va.c index 59d3b3c938e..74030a8f341 100644 --- a/dlls/wined3d/va.c +++ b/dlls/wined3d/va.c @@ -46,17 +46,23 @@ struct va_context { void *libva_handle, *libva_drm_handle; #define MAKE_FUNCPTR(f) typeof(f) *f + MAKE_FUNCPTR(vaBeginPicture); + MAKE_FUNCPTR(vaCreateBuffer); MAKE_FUNCPTR(vaCreateConfig); MAKE_FUNCPTR(vaCreateContext); MAKE_FUNCPTR(vaCreateSurfaces); + MAKE_FUNCPTR(vaDestroyBuffer); MAKE_FUNCPTR(vaDestroyConfig); MAKE_FUNCPTR(vaDestroyContext); MAKE_FUNCPTR(vaDestroySurfaces); + MAKE_FUNCPTR(vaEndPicture); MAKE_FUNCPTR(vaExportSurfaceHandle); MAKE_FUNCPTR(vaGetDisplayDRM); MAKE_FUNCPTR(vaInitialize); MAKE_FUNCPTR(vaMaxNumProfiles); MAKE_FUNCPTR(vaQueryConfigProfiles); + MAKE_FUNCPTR(vaRenderPicture); + MAKE_FUNCPTR(vaSyncSurface); MAKE_FUNCPTR(vaTerminate); #undef MAKE_FUNCPTR @@ -71,6 +77,12 @@ struct va_decoder VAContextID context; VASurfaceID surfaces[VA_DECODER_SURFACE_COUNT]; VkDeviceMemory vk_memory[VA_DECODER_SURFACE_COUNT]; + struct + { + bool valid; + uint8_t dxva_index; + } references[VA_DECODER_SURFACE_COUNT]; + bool long_slice_info; }; static void close_va_display(struct va_context *ctx) @@ -113,16 +125,22 @@ static NTSTATUS open_va_display(UINT64 handle, struct va_context *ctx) ERR("Failed to load function %s.\n", #f); \ goto fail; \ } + LOAD_FUNCPTR(vaBeginPicture); + LOAD_FUNCPTR(vaCreateBuffer); LOAD_FUNCPTR(vaCreateConfig); LOAD_FUNCPTR(vaCreateContext); LOAD_FUNCPTR(vaCreateSurfaces); + LOAD_FUNCPTR(vaDestroyBuffer); LOAD_FUNCPTR(vaDestroyConfig); LOAD_FUNCPTR(vaDestroyContext); LOAD_FUNCPTR(vaDestroySurfaces); + LOAD_FUNCPTR(vaEndPicture); LOAD_FUNCPTR(vaExportSurfaceHandle); LOAD_FUNCPTR(vaInitialize); LOAD_FUNCPTR(vaMaxNumProfiles); LOAD_FUNCPTR(vaQueryConfigProfiles); + LOAD_FUNCPTR(vaRenderPicture); + LOAD_FUNCPTR(vaSyncSurface); LOAD_FUNCPTR(vaTerminate); #undef LOAD_FUNCPTR @@ -270,8 +288,8 @@ static NTSTATUS va_decoder_create_vk(void *args) goto fail; } - if ((status = ctx->vaCreateContext(ctx->display, decoder->config, params->desc.width, params->desc.height, - VA_PROGRESSIVE, decoder->surfaces, ARRAY_SIZE(decoder->surfaces), &decoder->context)) != VA_STATUS_SUCCESS) + if ((status = ctx->vaCreateContext(ctx->display, decoder->config, params->width, params->height, VA_PROGRESSIVE, + decoder->surfaces, ARRAY_SIZE(decoder->surfaces), &decoder->context)) != VA_STATUS_SUCCESS) { ERR("Failed to create context, error %#x.\n", status); ctx->vaDestroySurfaces(ctx->display, decoder->surfaces, VA_DECODER_SURFACE_COUNT); @@ -382,6 +400,8 @@ static NTSTATUS va_decoder_create_vk(void *args) params->surfaces[i].image = image; } + decoder->long_slice_info = params->desc.long_slice_info; + TRACE("Created VA decoder %p.\n", decoder); params->decoder = (uintptr_t)decoder; @@ -409,11 +429,234 @@ static NTSTATUS va_decoder_destroy_vk(void *args) return S_OK; } +static bool find_reference_slot(struct va_decoder *decoder, uint8_t dxva_index, unsigned int *idx) +{ + for (unsigned int i = 0; i < VA_DECODER_SURFACE_COUNT; ++i) + { + if (decoder->references[i].valid && decoder->references[i].dxva_index == dxva_index) + { + *idx = i; + return true; + } + } + + ERR("Reference index %u was never written.\n", dxva_index); + return false; +} + +static NTSTATUS va_decoder_decode(void *args) +{ + struct va_decoder_decode_params *params = args; + const DXVA_PicParams_H264 *dxva_params = (const void *)(uintptr_t)params->parameters; + const DXVA_Qmatrix_H264 *dxva_matrix = (const void *)(uintptr_t)params->matrix; + struct va_decoder *decoder = (struct va_decoder *)(uintptr_t)params->decoder; + VAPictureParameterBufferH264 va_params = {0}; + struct va_context *ctx = &decoder->ctx; + VAIQMatrixBufferH264 va_matrix; + unsigned int ref_count = 0; + unsigned int slice_count; + VABufferID buffers[3]; + VAStatus status; + + if (decoder->long_slice_info) + slice_count = params->slice_control_size / sizeof(DXVA_Slice_H264_Long); + else + slice_count = params->slice_control_size / sizeof(DXVA_Slice_H264_Short); + + if ((status = ctx->vaBeginPicture(ctx->display, decoder->context, + decoder->surfaces[params->output_idx])) != VA_STATUS_SUCCESS) + ERR("Failed to begin picture, error %#x.\n", status); + + for (unsigned int i = 0; i < slice_count; ++i) + { + VASliceParameterBufferH264 slice_params = {0}; + VABufferID slice_buffer; + + if (decoder->long_slice_info) + { + const DXVA_Slice_H264_Long *slices = (const void *)(uintptr_t)params->slice_control; + + /* VA doesn't want the start codes. */ + slice_params.slice_data_size = slices[i].SliceBytesInBuffer - 3; + slice_params.slice_data_offset = slices[i].BSNALunitDataLocation + 3; + slice_params.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; + + slice_params.num_ref_idx_l0_active_minus1 = slices[i].num_ref_idx_l0_active_minus1; + slice_params.num_ref_idx_l1_active_minus1 = slices[i].num_ref_idx_l1_active_minus1; + slice_params.slice_type = slices[i].slice_type; + } + else + { + const DXVA_Slice_H264_Short *slices = (const void *)(uintptr_t)params->slice_control; + + slice_params.slice_data_size = slices[i].SliceBytesInBuffer - 3; + slice_params.slice_data_offset = slices[i].BSNALunitDataLocation + 3; + slice_params.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; + + /* FIXME: We can't fill any of the other parameters. + * Mesa doesn't care about most of them, but it does care about + * these two. However, it also treats them as per-picture, and we + * have the "default" values presumably from the PPS, so provide + * them here. */ + slice_params.num_ref_idx_l0_active_minus1 = dxva_params->num_ref_idx_l0_active_minus1; + slice_params.num_ref_idx_l1_active_minus1 = dxva_params->num_ref_idx_l1_active_minus1; + } + + if ((status = ctx->vaCreateBuffer(ctx->display, decoder->context, VASliceParameterBufferType, + sizeof(slice_params), 1, &slice_params, &slice_buffer)) != VA_STATUS_SUCCESS) + ERR("Failed to create slice parameters, error %#x.\n", status); + + if ((status = ctx->vaRenderPicture(ctx->display, decoder->context, + &slice_buffer, 1)) != VA_STATUS_SUCCESS) + ERR("Failed to render slice parameters, error %#x.\n", status); + + ctx->vaDestroyBuffer(ctx->display, slice_buffer); + } + + TRACE("Decoding frame %02x/%02x/%u, RefPicFlag %#x, reference frames", + dxva_params->CurrPic.bPicEntry, dxva_params->frame_num, params->output_idx, dxva_params->RefPicFlag); + + for (unsigned int i = 0; i < ARRAY_SIZE(dxva_params->RefFrameList); ++i) + { + unsigned int field_flags = ((dxva_params->UsedForReferenceFlags >> (2 * i)) & 3u); + VAPictureH264 *va_ref = &va_params.ReferenceFrames[ref_count]; + unsigned int ref_idx; + + if (dxva_params->RefFrameList[i].bPicEntry == 0xff) + continue; + + /* NVidia's DXVA implementation apparently expects each frame to appear + * in its own references list. VA does not expect or need this. */ + if (dxva_params->RefFrameList[i].Index7Bits == dxva_params->CurrPic.Index7Bits) + continue; + + if (!find_reference_slot(decoder, dxva_params->RefFrameList[i].Index7Bits, &ref_idx)) + return E_FAIL; + + TRACE(" %02x/%02x/%u", dxva_params->RefFrameList[i].bPicEntry, dxva_params->FrameNumList[i], ref_idx); + + va_ref->picture_id = decoder->surfaces[ref_idx]; + va_ref->frame_idx = dxva_params->FrameNumList[i]; + if (dxva_params->RefFrameList[i].AssociatedFlag) + va_ref->flags = VA_PICTURE_H264_LONG_TERM_REFERENCE; + else + va_ref->flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE; + + if (field_flags == 1) + va_ref->flags |= VA_PICTURE_H264_TOP_FIELD; + else if (field_flags == 2) + va_ref->flags |= VA_PICTURE_H264_BOTTOM_FIELD; + + va_ref->TopFieldOrderCnt = dxva_params->FieldOrderCntList[i][0]; + va_ref->BottomFieldOrderCnt = dxva_params->FieldOrderCntList[i][1]; + + ++ref_count; + } + + for (unsigned int i = ref_count; i < ARRAY_SIZE(va_params.ReferenceFrames); ++i) + { + va_params.ReferenceFrames[i].flags = VA_PICTURE_H264_INVALID; + va_params.ReferenceFrames[i].picture_id = VA_INVALID_SURFACE; + } + + TRACE(".\n"); + + va_params.CurrPic.picture_id = decoder->surfaces[params->output_idx]; + va_params.CurrPic.frame_idx = dxva_params->frame_num; + /* FIXME: What on earth do we put here? For some reason DXVA supplies these + * flags for reference frames, but not for the current frame. + * Mesa doesn't care about most of these, but it does care whether the + * current frame is a bottom field. */ + va_params.CurrPic.flags = 0; + va_params.CurrPic.TopFieldOrderCnt = dxva_params->CurrFieldOrderCnt[0]; + va_params.CurrPic.BottomFieldOrderCnt = dxva_params->CurrFieldOrderCnt[1]; + + va_params.picture_width_in_mbs_minus1 = dxva_params->wFrameWidthInMbsMinus1; + va_params.picture_height_in_mbs_minus1 = dxva_params->wFrameHeightInMbsMinus1; + va_params.bit_depth_luma_minus8 = dxva_params->bit_depth_luma_minus8; + va_params.bit_depth_chroma_minus8 = dxva_params->bit_depth_chroma_minus8; + va_params.num_ref_frames = dxva_params->num_ref_frames; + va_params.seq_fields.bits.chroma_format_idc = dxva_params->chroma_format_idc; + va_params.seq_fields.bits.residual_colour_transform_flag = dxva_params->residual_colour_transform_flag; + /* We don't have this value, so we have to say it's allowed. */ + va_params.seq_fields.bits.gaps_in_frame_num_value_allowed_flag = 1; + va_params.seq_fields.bits.frame_mbs_only_flag = dxva_params->frame_mbs_only_flag; + /* We don't have mb_adaptive_frame_field_flag, but we do have MbaffFrameFlag + * which is (mb_adaptive_frame_field_flag && !field_pic_flag). + * If field_pic_flag is 1, we don't know, so we set it to 1, which is the + * less constrained option. */ + if (!dxva_params->field_pic_flag) + va_params.seq_fields.bits.mb_adaptive_frame_field_flag = dxva_params->MbaffFrameFlag; + else + va_params.seq_fields.bits.mb_adaptive_frame_field_flag = 1; + va_params.seq_fields.bits.direct_8x8_inference_flag = dxva_params->direct_8x8_inference_flag; + va_params.seq_fields.bits.MinLumaBiPredSize8x8 = dxva_params->MinLumaBipredSize8x8Flag; + va_params.seq_fields.bits.log2_max_frame_num_minus4 = dxva_params->log2_max_frame_num_minus4; + va_params.seq_fields.bits.pic_order_cnt_type = dxva_params->pic_order_cnt_type; + va_params.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = dxva_params->log2_max_pic_order_cnt_lsb_minus4; + va_params.seq_fields.bits.delta_pic_order_always_zero_flag = dxva_params->delta_pic_order_always_zero_flag; + /* The slice group fields are "va_deprecated" and GStreamer doesn't fill them. */ + va_params.pic_init_qp_minus26 = dxva_params->pic_init_qp_minus26; + va_params.pic_init_qs_minus26 = dxva_params->pic_init_qs_minus26; + va_params.chroma_qp_index_offset = dxva_params->chroma_qp_index_offset; + va_params.second_chroma_qp_index_offset = dxva_params->second_chroma_qp_index_offset; + va_params.pic_fields.bits.entropy_coding_mode_flag = dxva_params->entropy_coding_mode_flag; + va_params.pic_fields.bits.weighted_pred_flag = dxva_params->weighted_pred_flag; + va_params.pic_fields.bits.weighted_bipred_idc = dxva_params->weighted_bipred_idc; + va_params.pic_fields.bits.transform_8x8_mode_flag = dxva_params->transform_8x8_mode_flag; + va_params.pic_fields.bits.field_pic_flag = dxva_params->field_pic_flag; + va_params.pic_fields.bits.constrained_intra_pred_flag = dxva_params->constrained_intra_pred_flag; + va_params.pic_fields.bits.pic_order_present_flag = dxva_params->pic_order_present_flag; + va_params.pic_fields.bits.deblocking_filter_control_present_flag = dxva_params->deblocking_filter_control_present_flag; + va_params.pic_fields.bits.redundant_pic_cnt_present_flag = dxva_params->redundant_pic_cnt_present_flag; + /* This seems to be equivalent, although the GStreamer code is not the easiest to follow. */ + va_params.pic_fields.bits.reference_pic_flag = dxva_params->RefPicFlag; + /* This too. */ + va_params.frame_num = dxva_params->frame_num; + + decoder->references[params->output_idx].valid = true; + decoder->references[params->output_idx].dxva_index = dxva_params->CurrPic.Index7Bits; + + /* The DXVA and VA matrices are byte-compatible. */ + memcpy(&va_matrix, dxva_matrix, sizeof(*dxva_matrix)); + memset(va_matrix.va_reserved, 0, sizeof(va_matrix.va_reserved)); + + /* The parameters need to be submitted first, or Mesa fails. */ + + if ((status = ctx->vaCreateBuffer(ctx->display, decoder->context, VAPictureParameterBufferType, + sizeof(va_params), 1, &va_params, &buffers[0])) != VA_STATUS_SUCCESS) + ERR("Failed to create parameters buffer, error %#x.\n", status); + + if ((status = ctx->vaCreateBuffer(ctx->display, decoder->context, VAIQMatrixBufferType, + sizeof(va_matrix), 1, &va_matrix, &buffers[1])) != VA_STATUS_SUCCESS) + ERR("Failed to create parameters buffer, error %#x.\n", status); + + if ((status = ctx->vaCreateBuffer(ctx->display, decoder->context, VASliceDataBufferType, + params->bitstream_size, 1, (void *)(uintptr_t)params->bitstream, &buffers[2])) != VA_STATUS_SUCCESS) + ERR("Failed to create bitstream buffer, error %#x.\n", status); + + if ((status = ctx->vaRenderPicture(ctx->display, decoder->context, + buffers, ARRAY_SIZE(buffers))) != VA_STATUS_SUCCESS) + ERR("Failed to render buffers, error %#x.\n", status); + + ctx->vaDestroyBuffer(ctx->display, buffers[0]); + ctx->vaDestroyBuffer(ctx->display, buffers[1]); + ctx->vaDestroyBuffer(ctx->display, buffers[2]); + + if ((status = ctx->vaEndPicture(ctx->display, decoder->context)) != VA_STATUS_SUCCESS) + ERR("Failed to end picture, error %#x.\n", status); + + ctx->vaSyncSurface(ctx->display, decoder->surfaces[params->output_idx]); + + return S_OK; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { [unix_va_get_profiles_vk] = va_get_profiles_vk, [unix_va_decoder_create_vk] = va_decoder_create_vk, [unix_va_decoder_destroy_vk] = va_decoder_destroy_vk, + [unix_va_decoder_decode] = va_decoder_decode, }; const unixlib_entry_t __wine_unix_call_wow64_funcs[] = @@ -421,6 +664,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = [unix_va_get_profiles_vk] = va_get_profiles_vk, [unix_va_decoder_create_vk] = va_decoder_create_vk, [unix_va_decoder_destroy_vk] = va_decoder_destroy_vk, + [unix_va_decoder_decode] = va_decoder_decode, }; #endif -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11535