1 /*****************************************************************************
2 * converter_vdpau.c: OpenGL VDPAU opaque converter
3 *****************************************************************************
4 * Copyright (C) 2017 VLC authors, VideoLAN and VideoLabs
5 *
6 * Author: Victorien Le Couviour--Tuffet <victorien.lecouviour.tuffet@gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <assert.h>
28
29 #include <GL/gl.h>
30 #include <GL/glext.h>
31
32 #include <vlc_common.h>
33 #include <vlc_vout_window.h>
34 #include <vlc_xlib.h>
35
36 #include "../../hw/vdpau/vlc_vdpau.h"
37 #include "internal.h"
38
39 #define INTEROP_CALL(fct, ...) \
40 _##fct(__VA_ARGS__); \
41 { \
42 GLenum ret = tc->vt->GetError(); \
43 if (ret != GL_NO_ERROR) \
44 { \
45 msg_Err(tc->gl, #fct " failed: 0x%x\n", ret); \
46 return VLC_EGENERIC; \
47 } \
48 }
49
50 struct priv
51 {
52 vdp_t *vdp;
53 VdpDevice vdp_device;
54 };
55
56 static PFNGLVDPAUINITNVPROC _glVDPAUInitNV;
57 static PFNGLVDPAUFININVPROC _glVDPAUFiniNV;
58 static PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC _glVDPAURegisterOutputSurfaceNV;
59 static PFNGLVDPAUISSURFACENVPROC _glVDPAUIsSurfaceNV;
60 static PFNGLVDPAUUNREGISTERSURFACENVPROC _glVDPAUUnregisterSurfaceNV;
61 static PFNGLVDPAUGETSURFACEIVNVPROC _glVDPAUGetSurfaceivNV;
62 static PFNGLVDPAUSURFACEACCESSNVPROC _glVDPAUSurfaceAccessNV;
63 static PFNGLVDPAUMAPSURFACESNVPROC _glVDPAUMapSurfacesNV;
64 static PFNGLVDPAUUNMAPSURFACESNVPROC _glVDPAUUnmapSurfacesNV;
65
66 static void
pool_pic_destroy_cb(picture_t * pic)67 pool_pic_destroy_cb(picture_t *pic)
68 {
69 vdp_output_surface_destroy(pic->p_sys->vdp, pic->p_sys->surface);
70 vdp_release_x11(pic->p_sys->vdp);
71 free(pic->p_sys);
72 free(pic);
73 }
74
75 static picture_pool_t *
tc_vdpau_gl_get_pool(opengl_tex_converter_t const * tc,unsigned int requested_count)76 tc_vdpau_gl_get_pool(opengl_tex_converter_t const *tc,
77 unsigned int requested_count)
78 {
79 struct priv *priv = tc->priv;
80 picture_t *pics[requested_count];
81
82 unsigned int i;
83 for (i = 0; i < requested_count; ++i)
84 {
85 VdpOutputSurface surface;
86
87 VdpStatus st;
88 if ((st = vdp_output_surface_create(priv->vdp, priv->vdp_device,
89 VDP_RGBA_FORMAT_B8G8R8A8,
90 tc->fmt.i_visible_width,
91 tc->fmt.i_visible_height,
92 &surface)) != VDP_STATUS_OK)
93 goto error;
94
95 picture_sys_t *picsys = calloc(1, sizeof(*picsys));
96 if (!picsys)
97 goto error;
98
99 picsys->vdp = vdp_hold_x11(priv->vdp, NULL);
100 picsys->device = priv->vdp_device;
101 picsys->surface = surface;
102
103 picture_resource_t rsc = { .p_sys = picsys,
104 .pf_destroy = pool_pic_destroy_cb };
105
106 pics[i] = picture_NewFromResource(&tc->fmt, &rsc);
107 if (!pics[i])
108 goto error;
109 }
110
111 picture_pool_t *pool = picture_pool_New(requested_count, pics);
112 if (!pool)
113 goto error;
114
115 return pool;
116
117 error:
118 while (i--)
119 picture_Release(pics[i]);
120 return NULL;
121 }
122
123 static int
tc_vdpau_gl_update(opengl_tex_converter_t const * tc,GLuint textures[],GLsizei const tex_widths[],GLsizei const tex_heights[],picture_t * pic,size_t const plane_offsets[])124 tc_vdpau_gl_update(opengl_tex_converter_t const *tc, GLuint textures[],
125 GLsizei const tex_widths[], GLsizei const tex_heights[],
126 picture_t *pic, size_t const plane_offsets[])
127 {
128 VLC_UNUSED(tex_widths);
129 VLC_UNUSED(tex_heights);
130 VLC_UNUSED(plane_offsets);
131
132 GLvdpauSurfaceNV *p_gl_nv_surface =
133 (GLvdpauSurfaceNV *)&pic->p_sys->gl_nv_surface;
134
135 if (*p_gl_nv_surface)
136 {
137 assert(_glVDPAUIsSurfaceNV(*p_gl_nv_surface) == GL_TRUE);
138
139 GLint state;
140 GLsizei num_val;
141 INTEROP_CALL(glVDPAUGetSurfaceivNV, *p_gl_nv_surface,
142 GL_SURFACE_STATE_NV, 1, &num_val, &state);
143 assert(num_val == 1); assert(state == GL_SURFACE_MAPPED_NV);
144
145 INTEROP_CALL(glVDPAUUnmapSurfacesNV, 1, p_gl_nv_surface);
146 INTEROP_CALL(glVDPAUUnregisterSurfaceNV, *p_gl_nv_surface);
147 }
148
149 *p_gl_nv_surface =
150 INTEROP_CALL(glVDPAURegisterOutputSurfaceNV,
151 (void *)(size_t)pic->p_sys->surface,
152 GL_TEXTURE_2D, tc->tex_count, textures);
153 INTEROP_CALL(glVDPAUSurfaceAccessNV, *p_gl_nv_surface, GL_READ_ONLY);
154 INTEROP_CALL(glVDPAUMapSurfacesNV, 1, p_gl_nv_surface);
155
156 return VLC_SUCCESS;
157 }
158
159 static void
Close(vlc_object_t * obj)160 Close(vlc_object_t *obj)
161 {
162 opengl_tex_converter_t *tc = (void *)obj;
163 _glVDPAUFiniNV(); assert(tc->vt->GetError() == GL_NO_ERROR);
164 vdp_release_x11(((struct priv *)tc->priv)->vdp);
165 free(tc->priv);
166 }
167
168 static int
Open(vlc_object_t * obj)169 Open(vlc_object_t *obj)
170 {
171 opengl_tex_converter_t *tc = (void *) obj;
172 if ((tc->fmt.i_chroma != VLC_CODEC_VDPAU_VIDEO_420 &&
173 tc->fmt.i_chroma != VLC_CODEC_VDPAU_VIDEO_422 &&
174 tc->fmt.i_chroma != VLC_CODEC_VDPAU_VIDEO_444) ||
175 !HasExtension(tc->glexts, "GL_NV_vdpau_interop") ||
176 tc->gl->surface->type != VOUT_WINDOW_TYPE_XID)
177 return VLC_EGENERIC;
178
179 tc->fmt.i_chroma = VLC_CODEC_VDPAU_OUTPUT;
180
181 if (!vlc_xlib_init(VLC_OBJECT(tc->gl)))
182 return VLC_EGENERIC;
183
184 struct priv *priv = calloc(1, sizeof(*priv));
185 if (!priv)
186 return VLC_EGENERIC;
187 tc->priv = priv;
188
189 if (vdp_get_x11(tc->gl->surface->display.x11, -1,
190 &priv->vdp, &priv->vdp_device) != VDP_STATUS_OK)
191 {
192 free(priv);
193 return VLC_EGENERIC;
194 }
195
196 void *vdp_gpa;
197 if (vdp_get_proc_address(priv->vdp, priv->vdp_device,
198 VDP_FUNC_ID_GET_PROC_ADDRESS, &vdp_gpa)
199 != VDP_STATUS_OK)
200 {
201 vdp_release_x11(priv->vdp);
202 free(priv);
203 return VLC_EGENERIC;
204 }
205
206 #define SAFE_GPA(fct) \
207 _##fct = vlc_gl_GetProcAddress(tc->gl, #fct); \
208 if (!_##fct) \
209 { \
210 vdp_release_x11(priv->vdp); \
211 free(priv); \
212 return VLC_EGENERIC; \
213 }
214 SAFE_GPA(glVDPAUInitNV);
215 SAFE_GPA(glVDPAUFiniNV);
216 SAFE_GPA(glVDPAURegisterOutputSurfaceNV);
217 SAFE_GPA(glVDPAUIsSurfaceNV);
218 SAFE_GPA(glVDPAUUnregisterSurfaceNV);
219 SAFE_GPA(glVDPAUGetSurfaceivNV);
220 SAFE_GPA(glVDPAUSurfaceAccessNV);
221 SAFE_GPA(glVDPAUMapSurfacesNV);
222 SAFE_GPA(glVDPAUUnmapSurfacesNV);
223 #undef SAFE_GPA
224
225 INTEROP_CALL(glVDPAUInitNV, (void *)(size_t)priv->vdp_device, vdp_gpa);
226
227 tc->fshader = opengl_fragment_shader_init(tc, GL_TEXTURE_2D,
228 VLC_CODEC_RGB32,
229 COLOR_SPACE_UNDEF);
230 if (!tc->fshader)
231 {
232 Close(obj);
233 return VLC_EGENERIC;
234 }
235
236 tc->pf_get_pool = tc_vdpau_gl_get_pool;
237 tc->pf_update = tc_vdpau_gl_update;
238
239 return VLC_SUCCESS;
240 }
241
242 vlc_module_begin ()
243 set_description("VDPAU OpenGL surface converter")
244 set_capability("glconv", 2)
245 set_callbacks(Open, Close)
246 set_category(CAT_VIDEO)
247 set_subcategory(SUBCAT_VIDEO_VOUT)
248 add_shortcut("vdpau")
249 vlc_module_end ()
250