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