void mp_set_pcm_codec(struct sh_stream *sh, bool sign, bool is_float, int bits, bool is_be) { // This uses libavcodec pcm codec names, e.g. "pcm_u16le". char codec[64] = "pcm_"; if (is_float) { mp_snprintf_cat(codec, sizeof(codec), "f"); } else { mp_snprintf_cat(codec, sizeof(codec), sign ? "s" : "u"); } mp_snprintf_cat(codec, sizeof(codec), "%d", bits); if (bits != 8) mp_snprintf_cat(codec, sizeof(codec), is_be ? "be" : "le"); sh->codec = talloc_strdup(sh->audio, codec); }
static void dump_decoder_info(struct lavc_ctx *s, GUID *device_guids, UINT n_guids) { struct priv *p = s->hwdec_priv; MP_VERBOSE(p, "%u decoder devices:\n", (unsigned)n_guids); for (UINT i = 0; i < n_guids; i++) { GUID *guid = &device_guids[i]; char *description = d3d_decoder_guid_to_desc(guid); D3DFORMAT *formats = NULL; UINT n_formats = 0; HRESULT hr = IDirectXVideoDecoderService_GetDecoderRenderTargets( p->decoder_service, guid, &n_formats, &formats); if (FAILED(hr)) { MP_ERR(p, "Failed to get render targets for decoder %s:%s\n", description, mp_HRESULT_to_str(hr)); } char fmts[256] = {0}; for (UINT j = 0; j < n_formats; j++) { mp_snprintf_cat(fmts, sizeof(fmts), " %s", mp_tag_str(formats[j])); } CoTaskMemFree(formats); MP_VERBOSE(p, "%s %s\n", description, fmts); } }
static void list_features(int set, struct mp_log *log, int msgl, bool invert) { char b[1024] = {0}; for (const struct feature *f = &features[0]; f->id; f++) { if (invert == !(f->id & set)) mp_snprintf_cat(b, sizeof(b), " [%s]", f->name); } mp_msg(log, msgl, "%s\n", b); }
static void dump_decoder_info(struct lavc_ctx *s, const GUID *guid) { struct priv *p = s->hwdec_priv; char fmts[256] = {0}; for (int i = 0; i < MP_ARRAY_SIZE(d3d11_formats); i++) { const struct d3d_decoded_format *format = &d3d11_formats[i]; if (d3d11_format_supported(s, guid, format)) mp_snprintf_cat(fmts, sizeof(fmts), " %s", format->name); } MP_VERBOSE(p, "%s %s\n", d3d_decoder_guid_to_desc(guid), fmts); }
static char *audio_config_to_str_buf(char *buf, size_t buf_sz, int rate, int format, struct mp_chmap channels) { char ch[128]; mp_chmap_to_str_buf(ch, sizeof(ch), &channels); char *hr_ch = mp_chmap_to_str_hr(&channels); if (strcmp(hr_ch, ch) != 0) mp_snprintf_cat(ch, sizeof(ch), " (%s)", hr_ch); snprintf(buf, buf_sz, "%dHz %s %dch %s", rate, ch, channels.num, af_fmt_to_str(format)); return buf; }
static int dxva2_create_decoder(struct lavc_ctx *s, int w, int h, enum AVCodecID codec_id, int profile) { DXVA2Context *ctx = s->hwdec_priv; struct dxva_context *dxva_ctx = s->avctx->hwaccel_context; GUID *guid_list = NULL; unsigned guid_count = 0, i, j; GUID device_guid = GUID_NULL; D3DFORMAT target_format = 0; DXVA2_VideoDesc desc = { 0 }; DXVA2_ConfigPictureDecode config; HRESULT hr; int surface_alignment; int ret; hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids(ctx->decoder_service, &guid_count, &guid_list); if (FAILED(hr)) { MP_ERR(ctx, "Failed to retrieve decoder device GUIDs\n"); goto fail; } // dump all decoder info MP_VERBOSE(ctx, "%d decoder devices:\n", (int)guid_count); for (j = 0; j < guid_count; j++) { GUID *guid = &guid_list[j]; const char *name = "<unknown>"; for (i = 0; dxva2_modes[i].guid; i++) { if (IsEqualGUID(dxva2_modes[i].guid, guid)) name = dxva2_modes[i].name; } D3DFORMAT *target_list = NULL; unsigned target_count = 0; hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, guid, &target_count, &target_list); if (FAILED(hr)) continue; char fmts[256] = {0}; for (i = 0; i < target_count; i++) mp_snprintf_cat(fmts, sizeof(fmts), " %s", mp_tag_str(target_list[i])); CoTaskMemFree(target_list); MP_VERBOSE(ctx, "%s %s %s\n", mp_GUID_to_str(guid), name, fmts); } // find a suitable decoder for (i = 0; dxva2_modes[i].guid; i++) { D3DFORMAT *target_list = NULL; unsigned target_count = 0; const dxva2_mode *mode = &dxva2_modes[i]; if (mode->codec != codec_id) continue; for (j = 0; j < guid_count; j++) { if (IsEqualGUID(mode->guid, &guid_list[j])) break; } if (j == guid_count) continue; hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, mode->guid, &target_count, &target_list); if (FAILED(hr)) { continue; } for (j = 0; j < target_count; j++) { const D3DFORMAT format = target_list[j]; if (format == MKTAG('N','V','1','2')) { target_format = format; break; } } CoTaskMemFree(target_list); if (target_format) { device_guid = *mode->guid; break; } } CoTaskMemFree(guid_list); if (IsEqualGUID(&device_guid, &GUID_NULL)) { MP_ERR(ctx, "No decoder device for codec found\n"); goto fail; } desc.SampleWidth = w; desc.SampleHeight = h; desc.Format = target_format; ret = dxva2_get_decoder_configuration(s, codec_id, &device_guid, &desc, &config); if (ret < 0) { goto fail; } /* decoding MPEG-2 requires additional alignment on some Intel GPUs, but it causes issues for H.264 on certain AMD GPUs..... */ if (codec_id == AV_CODEC_ID_MPEG2VIDEO) surface_alignment = 32; /* the HEVC DXVA2 spec asks for 128 pixel aligned surfaces to ensure all coding features have enough room to work with */ else if (codec_id == AV_CODEC_ID_HEVC) surface_alignment = 128; else surface_alignment = 16; /* 4 base work surfaces */ ctx->num_surfaces = 4 + ADDTIONAL_SURFACES; /* add surfaces based on number of possible refs */ if (codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_HEVC) ctx->num_surfaces += 16; else ctx->num_surfaces += 2; ctx->surfaces = av_mallocz(ctx->num_surfaces * sizeof(*ctx->surfaces)); ctx->surface_infos = av_mallocz(ctx->num_surfaces * sizeof(*ctx->surface_infos)); if (!ctx->surfaces || !ctx->surface_infos) { MP_ERR(ctx, "Unable to allocate surface arrays\n"); goto fail; } hr = IDirectXVideoDecoderService_CreateSurface(ctx->decoder_service, FFALIGN(w, surface_alignment), FFALIGN(h, surface_alignment), ctx->num_surfaces - 1, target_format, D3DPOOL_DEFAULT, 0, DXVA2_VideoDecoderRenderTarget, ctx->surfaces, NULL); if (FAILED(hr)) { MP_ERR(ctx, "Failed to create %d video surfaces\n", ctx->num_surfaces); goto fail; } hr = IDirectXVideoDecoderService_CreateVideoDecoder(ctx->decoder_service, &device_guid, &desc, &config, ctx->surfaces, ctx->num_surfaces, &ctx->decoder); if (FAILED(hr)) { MP_ERR(ctx, "Failed to create DXVA2 video decoder\n"); goto fail; } ctx->decoder_guid = device_guid; ctx->decoder_config = config; dxva_ctx->cfg = &ctx->decoder_config; dxva_ctx->decoder = ctx->decoder; dxva_ctx->surface = ctx->surfaces; dxva_ctx->surface_count = ctx->num_surfaces; if (IsEqualGUID(&ctx->decoder_guid, &DXVADDI_Intel_ModeH264_E)) dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO; return 0; fail: dxva2_destroy_decoder(s); return -1; }
struct ra_layout ra_renderpass_input_layout(struct ra_renderpass_input *input) { size_t el_size = ra_vartype_size(input->type); if (!el_size) return (struct ra_layout){0}; // host data is always tightly packed return (struct ra_layout) { .align = 1, .stride = el_size * input->dim_v, .size = el_size * input->dim_v * input->dim_m, }; } static struct ra_renderpass_input *dup_inputs(void *ta_parent, const struct ra_renderpass_input *inputs, int num_inputs) { struct ra_renderpass_input *res = talloc_memdup(ta_parent, (void *)inputs, num_inputs * sizeof(inputs[0])); for (int n = 0; n < num_inputs; n++) res[n].name = talloc_strdup(res, res[n].name); return res; } // Return a newly allocated deep-copy of params. struct ra_renderpass_params *ra_renderpass_params_copy(void *ta_parent, const struct ra_renderpass_params *params) { struct ra_renderpass_params *res = talloc_ptrtype(ta_parent, res); *res = *params; res->inputs = dup_inputs(res, res->inputs, res->num_inputs); res->vertex_attribs = dup_inputs(res, res->vertex_attribs, res->num_vertex_attribs); res->cached_program = bstrdup(res, res->cached_program); res->vertex_shader = talloc_strdup(res, res->vertex_shader); res->frag_shader = talloc_strdup(res, res->frag_shader); res->compute_shader = talloc_strdup(res, res->compute_shader); return res; }; struct glsl_fmt { enum ra_ctype ctype; int num_components; int component_depth[4]; const char *glsl_format; }; // List taken from the GLSL specification, sans snorm and sint formats static const struct glsl_fmt ra_glsl_fmts[] = { {RA_CTYPE_FLOAT, 1, {16}, "r16f"}, {RA_CTYPE_FLOAT, 1, {32}, "r32f"}, {RA_CTYPE_FLOAT, 2, {16, 16}, "rg16f"}, {RA_CTYPE_FLOAT, 2, {32, 32}, "rg32f"}, {RA_CTYPE_FLOAT, 4, {16, 16, 16, 16}, "rgba16f"}, {RA_CTYPE_FLOAT, 4, {32, 32, 32, 32}, "rgba32f"}, {RA_CTYPE_FLOAT, 3, {11, 11, 10}, "r11f_g11f_b10f"}, {RA_CTYPE_UNORM, 1, {8}, "r8"}, {RA_CTYPE_UNORM, 1, {16}, "r16"}, {RA_CTYPE_UNORM, 2, {8, 8}, "rg8"}, {RA_CTYPE_UNORM, 2, {16, 16}, "rg16"}, {RA_CTYPE_UNORM, 4, {8, 8, 8, 8}, "rgba8"}, {RA_CTYPE_UNORM, 4, {16, 16, 16, 16}, "rgba16"}, {RA_CTYPE_UNORM, 4, {10, 10, 10, 2}, "rgb10_a2"}, {RA_CTYPE_UINT, 1, {8}, "r8ui"}, {RA_CTYPE_UINT, 1, {16}, "r16ui"}, {RA_CTYPE_UINT, 1, {32}, "r32ui"}, {RA_CTYPE_UINT, 2, {8, 8}, "rg8ui"}, {RA_CTYPE_UINT, 2, {16, 16}, "rg16ui"}, {RA_CTYPE_UINT, 2, {32, 32}, "rg32ui"}, {RA_CTYPE_UINT, 4, {8, 8, 8, 8}, "rgba8ui"}, {RA_CTYPE_UINT, 4, {16, 16, 16, 16}, "rgba16ui"}, {RA_CTYPE_UINT, 4, {32, 32, 32, 32}, "rgba32ui"}, {RA_CTYPE_UINT, 4, {10, 10, 10, 2}, "rgb10_a2ui"}, }; const char *ra_fmt_glsl_format(const struct ra_format *fmt) { for (int n = 0; n < MP_ARRAY_SIZE(ra_glsl_fmts); n++) { const struct glsl_fmt *gfmt = &ra_glsl_fmts[n]; if (fmt->ctype != gfmt->ctype) continue; if (fmt->num_components != gfmt->num_components) continue; for (int i = 0; i < fmt->num_components; i++) { if (fmt->component_depth[i] != gfmt->component_depth[i]) goto next_fmt; } return gfmt->glsl_format; next_fmt: ; // equivalent to `continue` } return NULL; } // Return whether this is a tightly packed format with no external padding and // with the same bit size/depth in all components, and the shader returns // components in the same order as in memory. static bool ra_format_is_regular(const struct ra_format *fmt) { if (!fmt->pixel_size || !fmt->num_components || !fmt->ordered) return false; for (int n = 1; n < fmt->num_components; n++) { if (fmt->component_size[n] != fmt->component_size[0] || fmt->component_depth[n] != fmt->component_depth[0]) return false; } if (fmt->component_size[0] * fmt->num_components != fmt->pixel_size * 8) return false; return true; } // Return a regular filterable format using RA_CTYPE_UNORM. const struct ra_format *ra_find_unorm_format(struct ra *ra, int bytes_per_component, int n_components) { for (int n = 0; n < ra->num_formats; n++) { const struct ra_format *fmt = ra->formats[n]; if (fmt->ctype == RA_CTYPE_UNORM && fmt->num_components == n_components && fmt->pixel_size == bytes_per_component * n_components && fmt->component_depth[0] == bytes_per_component * 8 && fmt->linear_filter && ra_format_is_regular(fmt)) return fmt; } return NULL; } // Return a regular format using RA_CTYPE_UINT. const struct ra_format *ra_find_uint_format(struct ra *ra, int bytes_per_component, int n_components) { for (int n = 0; n < ra->num_formats; n++) { const struct ra_format *fmt = ra->formats[n]; if (fmt->ctype == RA_CTYPE_UINT && fmt->num_components == n_components && fmt->pixel_size == bytes_per_component * n_components && fmt->component_depth[0] == bytes_per_component * 8 && ra_format_is_regular(fmt)) return fmt; } return NULL; } // Find a float format of any precision that matches the C type of the same // size for upload. // May drop bits from the mantissa (such as selecting float16 even if // bytes_per_component == 32); prefers possibly faster formats first. static const struct ra_format *ra_find_float_format(struct ra *ra, int bytes_per_component, int n_components) { // Assumes ra_format are ordered by performance. // The >=16 check is to avoid catching fringe formats. for (int n = 0; n < ra->num_formats; n++) { const struct ra_format *fmt = ra->formats[n]; if (fmt->ctype == RA_CTYPE_FLOAT && fmt->num_components == n_components && fmt->pixel_size == bytes_per_component * n_components && fmt->component_depth[0] >= 16 && fmt->linear_filter && ra_format_is_regular(fmt)) return fmt; } return NULL; } // Return a filterable regular format that uses at least float16 internally, and // uses a normal C float for transfer on the CPU side. (This is just so we don't // need 32->16 bit conversion on CPU, which would be messy.) const struct ra_format *ra_find_float16_format(struct ra *ra, int n_components) { return ra_find_float_format(ra, sizeof(float), n_components); } const struct ra_format *ra_find_named_format(struct ra *ra, const char *name) { for (int n = 0; n < ra->num_formats; n++) { const struct ra_format *fmt = ra->formats[n]; if (strcmp(fmt->name, name) == 0) return fmt; } return NULL; } // Like ra_find_unorm_format(), but if no fixed point format is available, // return an unsigned integer format. static const struct ra_format *find_plane_format(struct ra *ra, int bytes, int n_channels, enum mp_component_type ctype) { switch (ctype) { case MP_COMPONENT_TYPE_UINT: { const struct ra_format *f = ra_find_unorm_format(ra, bytes, n_channels); if (f) return f; return ra_find_uint_format(ra, bytes, n_channels); } case MP_COMPONENT_TYPE_FLOAT: return ra_find_float_format(ra, bytes, n_channels); default: return NULL; } } // Put a mapping of imgfmt to texture formats into *out. Basically it selects // the correct texture formats needed to represent an imgfmt in a shader, with // textures using the same memory organization as on the CPU. // Each plane is represented by a texture, and each texture has a RGBA // component order. out->components describes the meaning of them. // May return integer formats for >8 bit formats, if the driver has no // normalized 16 bit formats. // Returns false (and *out is not touched) if no format found. bool ra_get_imgfmt_desc(struct ra *ra, int imgfmt, struct ra_imgfmt_desc *out) { struct ra_imgfmt_desc res = {0}; struct mp_regular_imgfmt regfmt; if (mp_get_regular_imgfmt(®fmt, imgfmt)) { enum ra_ctype ctype = RA_CTYPE_UNKNOWN; res.num_planes = regfmt.num_planes; res.component_bits = regfmt.component_size * 8; res.component_pad = regfmt.component_pad; for (int n = 0; n < regfmt.num_planes; n++) { struct mp_regular_imgfmt_plane *plane = ®fmt.planes[n]; res.planes[n] = find_plane_format(ra, regfmt.component_size, plane->num_components, regfmt.component_type); if (!res.planes[n]) return false; for (int i = 0; i < plane->num_components; i++) res.components[n][i] = plane->components[i]; // Dropping LSBs when shifting will lead to dropped MSBs. if (res.component_bits > res.planes[n]->component_depth[0] && res.component_pad < 0) return false; // Renderer restriction, but actually an unwanted corner case. if (ctype != RA_CTYPE_UNKNOWN && ctype != res.planes[n]->ctype) return false; ctype = res.planes[n]->ctype; } res.chroma_w = regfmt.chroma_w; res.chroma_h = regfmt.chroma_h; goto supported; } for (int n = 0; n < ra->num_formats; n++) { if (imgfmt && ra->formats[n]->special_imgfmt == imgfmt) { res = *ra->formats[n]->special_imgfmt_desc; goto supported; } } // Unsupported format return false; supported: *out = res; return true; } void ra_dump_tex_formats(struct ra *ra, int msgl) { if (!mp_msg_test(ra->log, msgl)) return; MP_MSG(ra, msgl, "Texture formats:\n"); MP_MSG(ra, msgl, " NAME COMP*TYPE SIZE DEPTH PER COMP.\n"); for (int n = 0; n < ra->num_formats; n++) { const struct ra_format *fmt = ra->formats[n]; const char *ctype = "unknown"; switch (fmt->ctype) { case RA_CTYPE_UNORM: ctype = "unorm"; break; case RA_CTYPE_UINT: ctype = "uint "; break; case RA_CTYPE_FLOAT: ctype = "float"; break; } char cl[40] = ""; for (int i = 0; i < fmt->num_components; i++) { mp_snprintf_cat(cl, sizeof(cl), "%s%d", i ? " " : "", fmt->component_size[i]); if (fmt->component_size[i] != fmt->component_depth[i]) mp_snprintf_cat(cl, sizeof(cl), "/%d", fmt->component_depth[i]); } MP_MSG(ra, msgl, " %-10s %d*%s %3dB %s %s %s {%s}\n", fmt->name, fmt->num_components, ctype, fmt->pixel_size, fmt->luminance_alpha ? "LA" : " ", fmt->linear_filter ? "LF" : " ", fmt->renderable ? "CR" : " ", cl); } MP_MSG(ra, msgl, " LA = LUMINANCE_ALPHA hack format\n"); MP_MSG(ra, msgl, " LF = linear filterable\n"); MP_MSG(ra, msgl, " CR = can be used for render targets\n"); } void ra_dump_imgfmt_desc(struct ra *ra, const struct ra_imgfmt_desc *desc, int msgl) { char pl[80] = ""; char pf[80] = ""; for (int n = 0; n < desc->num_planes; n++) { if (n > 0) { mp_snprintf_cat(pl, sizeof(pl), "/"); mp_snprintf_cat(pf, sizeof(pf), "/"); } char t[5] = {0}; for (int i = 0; i < 4; i++) t[i] = "_rgba"[desc->components[n][i]]; for (int i = 3; i > 0 && t[i] == '_'; i--) t[i] = '\0'; mp_snprintf_cat(pl, sizeof(pl), "%s", t); mp_snprintf_cat(pf, sizeof(pf), "%s", desc->planes[n]->name); } MP_MSG(ra, msgl, "%d planes %dx%d %d/%d [%s] (%s)\n", desc->num_planes, desc->chroma_w, desc->chroma_h, desc->component_bits, desc->component_pad, pf, pl); } void ra_dump_img_formats(struct ra *ra, int msgl) { if (!mp_msg_test(ra->log, msgl)) return; MP_MSG(ra, msgl, "Image formats:\n"); for (int imgfmt = IMGFMT_START; imgfmt < IMGFMT_END; imgfmt++) { const char *name = mp_imgfmt_to_name(imgfmt); if (strcmp(name, "unknown") == 0) continue; MP_MSG(ra, msgl, " %s", name); struct ra_imgfmt_desc desc; if (ra_get_imgfmt_desc(ra, imgfmt, &desc)) { MP_MSG(ra, msgl, " => "); ra_dump_imgfmt_desc(ra, &desc, msgl); } else { MP_MSG(ra, msgl, "\n"); } } }