vdpau.c revision 848b8605
1/**************************************************************************
2 *
3 * Copyright 2013 Advanced Micro Devices, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28/*
29 * Authors:
30 *      Christian König <christian.koenig@amd.com>
31 *
32 */
33
34#include <stdbool.h>
35#include "util/hash_table.h"
36#include "context.h"
37#include "glformats.h"
38#include "set.h"
39#include "texobj.h"
40#include "teximage.h"
41#include "vdpau.h"
42
43#define MAX_TEXTURES 4
44
45struct vdp_surface
46{
47   GLenum target;
48   struct gl_texture_object *textures[MAX_TEXTURES];
49   GLenum access, state;
50   GLboolean output;
51   const GLvoid *vdpSurface;
52};
53
54void GLAPIENTRY
55_mesa_VDPAUInitNV(const GLvoid *vdpDevice, const GLvoid *getProcAddress)
56{
57   GET_CURRENT_CONTEXT(ctx);
58
59   if (!vdpDevice) {
60      _mesa_error(ctx, GL_INVALID_VALUE, "vdpDevice");
61      return;
62   }
63
64   if (!getProcAddress) {
65      _mesa_error(ctx, GL_INVALID_VALUE, "getProcAddress");
66      return;
67   }
68
69   if (ctx->vdpDevice || ctx->vdpGetProcAddress || ctx->vdpSurfaces) {
70      _mesa_error(ctx, GL_INVALID_OPERATION, "VDPAUInitNV");
71      return;
72   }
73
74   ctx->vdpDevice = vdpDevice;
75   ctx->vdpGetProcAddress = getProcAddress;
76   ctx->vdpSurfaces = _mesa_set_create(NULL, _mesa_key_pointer_equal);
77}
78
79static void
80unregister_surface(struct set_entry *entry)
81{
82   struct vdp_surface *surf = (struct vdp_surface *)entry->key;
83   GET_CURRENT_CONTEXT(ctx);
84
85   if (surf->state == GL_SURFACE_MAPPED_NV) {
86      GLintptr surfaces[] = { (GLintptr)surf };
87      _mesa_VDPAUUnmapSurfacesNV(1, surfaces);
88   }
89
90   _mesa_set_remove(ctx->vdpSurfaces, entry);
91   free(surf);
92}
93
94void GLAPIENTRY
95_mesa_VDPAUFiniNV(void)
96{
97   GET_CURRENT_CONTEXT(ctx);
98
99   if (!ctx->vdpDevice || !ctx->vdpGetProcAddress || !ctx->vdpSurfaces) {
100      _mesa_error(ctx, GL_INVALID_OPERATION, "VDPAUFiniNV");
101      return;
102   }
103
104   _mesa_set_destroy(ctx->vdpSurfaces, unregister_surface);
105
106   ctx->vdpDevice = 0;
107   ctx->vdpGetProcAddress = 0;
108   ctx->vdpSurfaces = NULL;
109}
110
111static GLintptr
112register_surface(struct gl_context *ctx, GLboolean isOutput,
113                 const GLvoid *vdpSurface, GLenum target,
114                 GLsizei numTextureNames, const GLuint *textureNames)
115{
116   struct vdp_surface *surf;
117   int i;
118
119   if (!ctx->vdpDevice || !ctx->vdpGetProcAddress || !ctx->vdpSurfaces) {
120      _mesa_error(ctx, GL_INVALID_OPERATION, "VDPAURegisterSurfaceNV");
121      return (GLintptr)NULL;
122   }
123
124   if (target != GL_TEXTURE_2D && target != GL_TEXTURE_RECTANGLE) {
125      _mesa_error(ctx, GL_INVALID_ENUM, "VDPAURegisterSurfaceNV");
126      return (GLintptr)NULL;
127   }
128
129   if (target == GL_TEXTURE_RECTANGLE && !ctx->Extensions.NV_texture_rectangle) {
130      _mesa_error(ctx, GL_INVALID_ENUM, "VDPAURegisterSurfaceNV");
131      return (GLintptr)NULL;
132   }
133
134   surf = CALLOC_STRUCT( vdp_surface );
135   if (surf == NULL) {
136      _mesa_error_no_memory("VDPAURegisterSurfaceNV");
137      return (GLintptr)NULL;
138   }
139
140   surf->vdpSurface = vdpSurface;
141   surf->target = target;
142   surf->access = GL_READ_WRITE;
143   surf->state = GL_SURFACE_REGISTERED_NV;
144   surf->output = isOutput;
145   for (i = 0; i < numTextureNames; ++i) {
146      struct gl_texture_object *tex;
147      tex  = _mesa_lookup_texture(ctx, textureNames[i]);
148      if (tex == NULL) {
149         free(surf);
150         _mesa_error(ctx, GL_INVALID_OPERATION,
151                     "VDPAURegisterSurfaceNV(texture ID not found)");
152         return (GLintptr)NULL;
153      }
154
155      _mesa_lock_texture(ctx, tex);
156
157      if (tex->Immutable) {
158         _mesa_unlock_texture(ctx, tex);
159         free(surf);
160         _mesa_error(ctx, GL_INVALID_OPERATION,
161                     "VDPAURegisterSurfaceNV(texture is immutable)");
162         return (GLintptr)NULL;
163      }
164
165      if (tex->Target == 0)
166         tex->Target = target;
167      else if (tex->Target != target) {
168         _mesa_unlock_texture(ctx, tex);
169         free(surf);
170         _mesa_error(ctx, GL_INVALID_OPERATION,
171                     "VDPAURegisterSurfaceNV(target mismatch)");
172         return (GLintptr)NULL;
173      }
174
175      /* This will disallow respecifying the storage. */
176      tex->Immutable = GL_TRUE;
177      _mesa_unlock_texture(ctx, tex);
178
179      _mesa_reference_texobj(&surf->textures[i], tex);
180   }
181
182   _mesa_set_add(ctx->vdpSurfaces, _mesa_hash_pointer(surf), surf);
183
184   return (GLintptr)surf;
185}
186
187GLintptr GLAPIENTRY
188_mesa_VDPAURegisterVideoSurfaceNV(const GLvoid *vdpSurface, GLenum target,
189                                  GLsizei numTextureNames,
190                                  const GLuint *textureNames)
191{
192   GET_CURRENT_CONTEXT(ctx);
193
194   if (numTextureNames != 4) {
195      _mesa_error(ctx, GL_INVALID_VALUE, "VDPAURegisterVideoSurfaceNV");
196      return (GLintptr)NULL;
197   }
198
199   return register_surface(ctx, false, vdpSurface, target,
200                           numTextureNames, textureNames);
201}
202
203GLintptr GLAPIENTRY
204_mesa_VDPAURegisterOutputSurfaceNV(const GLvoid *vdpSurface, GLenum target,
205                                   GLsizei numTextureNames,
206                                   const GLuint *textureNames)
207{
208   GET_CURRENT_CONTEXT(ctx);
209
210   if (numTextureNames != 1) {
211      _mesa_error(ctx, GL_INVALID_VALUE, "VDPAURegisterVideoSurfaceNV");
212      return (GLintptr)NULL;
213   }
214
215   return register_surface(ctx, true, vdpSurface, target,
216                           numTextureNames, textureNames);
217}
218
219GLboolean GLAPIENTRY
220_mesa_VDPAUIsSurfaceNV(GLintptr surface)
221{
222   struct vdp_surface *surf = (struct vdp_surface *)surface;
223   GET_CURRENT_CONTEXT(ctx);
224
225   if (!ctx->vdpDevice || !ctx->vdpGetProcAddress || !ctx->vdpSurfaces) {
226      _mesa_error(ctx, GL_INVALID_OPERATION, "VDPAUIsSurfaceNV");
227      return false;
228   }
229
230   if (!_mesa_set_search(ctx->vdpSurfaces, _mesa_hash_pointer(surf), surf)) {
231      return false;
232   }
233
234   return true;
235}
236
237void GLAPIENTRY
238_mesa_VDPAUUnregisterSurfaceNV(GLintptr surface)
239{
240   struct vdp_surface *surf = (struct vdp_surface *)surface;
241   struct set_entry *entry;
242   int i;
243   GET_CURRENT_CONTEXT(ctx);
244
245   if (!ctx->vdpDevice || !ctx->vdpGetProcAddress || !ctx->vdpSurfaces) {
246      _mesa_error(ctx, GL_INVALID_OPERATION, "VDPAUUnregisterSurfaceNV");
247      return;
248   }
249
250   /* according to the spec it's ok when this is zero */
251   if (surface == 0)
252      return;
253
254   entry = _mesa_set_search(ctx->vdpSurfaces, _mesa_hash_pointer(surf), surf);
255   if (!entry) {
256      _mesa_error(ctx, GL_INVALID_VALUE, "VDPAUUnregisterSurfaceNV");
257      return;
258   }
259
260   for (i = 0; i < MAX_TEXTURES; i++) {
261      if (surf->textures[i]) {
262         surf->textures[i]->Immutable = GL_FALSE;
263         _mesa_reference_texobj(&surf->textures[i], NULL);
264      }
265   }
266
267   _mesa_set_remove(ctx->vdpSurfaces, entry);
268   free(surf);
269}
270
271void GLAPIENTRY
272_mesa_VDPAUGetSurfaceivNV(GLintptr surface, GLenum pname, GLsizei bufSize,
273                          GLsizei *length, GLint *values)
274{
275   struct vdp_surface *surf = (struct vdp_surface *)surface;
276   GET_CURRENT_CONTEXT(ctx);
277
278   if (!ctx->vdpDevice || !ctx->vdpGetProcAddress || !ctx->vdpSurfaces) {
279      _mesa_error(ctx, GL_INVALID_OPERATION, "VDPAUGetSurfaceivNV");
280      return;
281   }
282
283   if (!_mesa_set_search(ctx->vdpSurfaces, _mesa_hash_pointer(surf), surf)) {
284      _mesa_error(ctx, GL_INVALID_VALUE, "VDPAUGetSurfaceivNV");
285      return;
286   }
287
288   if (pname != GL_SURFACE_STATE_NV) {
289      _mesa_error(ctx, GL_INVALID_ENUM, "VDPAUGetSurfaceivNV");
290      return;
291   }
292
293   if (bufSize < 1) {
294      _mesa_error(ctx, GL_INVALID_VALUE, "VDPAUGetSurfaceivNV");
295      return;
296   }
297
298   values[0] = surf->state;
299
300   if (length != NULL)
301      *length = 1;
302}
303
304void GLAPIENTRY
305_mesa_VDPAUSurfaceAccessNV(GLintptr surface, GLenum access)
306{
307   struct vdp_surface *surf = (struct vdp_surface *)surface;
308   GET_CURRENT_CONTEXT(ctx);
309
310   if (!ctx->vdpDevice || !ctx->vdpGetProcAddress || !ctx->vdpSurfaces) {
311      _mesa_error(ctx, GL_INVALID_OPERATION, "VDPAUSurfaceAccessNV");
312      return;
313   }
314
315   if (!_mesa_set_search(ctx->vdpSurfaces, _mesa_hash_pointer(surf), surf)) {
316      _mesa_error(ctx, GL_INVALID_VALUE, "VDPAUSurfaceAccessNV");
317      return;
318   }
319
320   if (access != GL_READ_ONLY && access != GL_WRITE_ONLY &&
321       access != GL_READ_WRITE) {
322
323      _mesa_error(ctx, GL_INVALID_VALUE, "VDPAUSurfaceAccessNV");
324      return;
325   }
326
327   if (surf->state == GL_SURFACE_MAPPED_NV) {
328      _mesa_error(ctx, GL_INVALID_OPERATION, "VDPAUSurfaceAccessNV");
329      return;
330   }
331
332   surf->access = access;
333}
334
335void GLAPIENTRY
336_mesa_VDPAUMapSurfacesNV(GLsizei numSurfaces, const GLintptr *surfaces)
337{
338   GET_CURRENT_CONTEXT(ctx);
339   int i;
340
341   if (!ctx->vdpDevice || !ctx->vdpGetProcAddress || !ctx->vdpSurfaces) {
342      _mesa_error(ctx, GL_INVALID_OPERATION, "VDPAUUnmapSurfacesNV");
343      return;
344   }
345
346   for (i = 0; i < numSurfaces; ++i) {
347      struct vdp_surface *surf = (struct vdp_surface *)surfaces[i];
348
349      if (!_mesa_set_search(ctx->vdpSurfaces, _mesa_hash_pointer(surf), surf)) {
350         _mesa_error(ctx, GL_INVALID_VALUE, "VDPAUSurfaceAccessNV");
351         return;
352      }
353
354      if (surf->state == GL_SURFACE_MAPPED_NV) {
355         _mesa_error(ctx, GL_INVALID_OPERATION, "VDPAUSurfaceAccessNV");
356         return;
357      }
358   }
359
360   for (i = 0; i < numSurfaces; ++i) {
361      struct vdp_surface *surf = (struct vdp_surface *)surfaces[i];
362      unsigned numTextureNames = surf->output ? 1 : 4;
363      unsigned j;
364
365      for (j = 0; j < numTextureNames; ++j) {
366         struct gl_texture_object *tex = surf->textures[j];
367         struct gl_texture_image *image;
368
369         _mesa_lock_texture(ctx, tex);
370         image = _mesa_get_tex_image(ctx, tex, surf->target, 0);
371         if (!image) {
372            _mesa_error(ctx, GL_OUT_OF_MEMORY, "VDPAUMapSurfacesNV");
373            _mesa_unlock_texture(ctx, tex);
374            return;
375         }
376
377         ctx->Driver.FreeTextureImageBuffer(ctx, image);
378
379         ctx->Driver.VDPAUMapSurface(ctx, surf->target, surf->access,
380                                     surf->output, tex, image,
381                                     surf->vdpSurface, j);
382
383         _mesa_unlock_texture(ctx, tex);
384      }
385      surf->state = GL_SURFACE_MAPPED_NV;
386   }
387}
388
389void GLAPIENTRY
390_mesa_VDPAUUnmapSurfacesNV(GLsizei numSurfaces, const GLintptr *surfaces)
391{
392   GET_CURRENT_CONTEXT(ctx);
393   int i;
394
395   if (!ctx->vdpDevice || !ctx->vdpGetProcAddress || !ctx->vdpSurfaces) {
396      _mesa_error(ctx, GL_INVALID_OPERATION, "VDPAUUnmapSurfacesNV");
397      return;
398   }
399
400   for (i = 0; i < numSurfaces; ++i) {
401      struct vdp_surface *surf = (struct vdp_surface *)surfaces[i];
402
403      if (!_mesa_set_search(ctx->vdpSurfaces, _mesa_hash_pointer(surf), surf)) {
404         _mesa_error(ctx, GL_INVALID_VALUE, "VDPAUSurfaceAccessNV");
405         return;
406      }
407
408      if (surf->state != GL_SURFACE_MAPPED_NV) {
409         _mesa_error(ctx, GL_INVALID_OPERATION, "VDPAUSurfaceAccessNV");
410         return;
411      }
412   }
413
414   for (i = 0; i < numSurfaces; ++i) {
415      struct vdp_surface *surf = (struct vdp_surface *)surfaces[i];
416      unsigned numTextureNames = surf->output ? 1 : 4;
417      unsigned j;
418
419      for (j = 0; j < numTextureNames; ++j) {
420         struct gl_texture_object *tex = surf->textures[j];
421         struct gl_texture_image *image;
422
423         _mesa_lock_texture(ctx, tex);
424
425         image = _mesa_select_tex_image(ctx, tex, surf->target, 0);
426
427         ctx->Driver.VDPAUUnmapSurface(ctx, surf->target, surf->access,
428                                       surf->output, tex, image,
429                                       surf->vdpSurface, j);
430
431         if (image)
432            ctx->Driver.FreeTextureImageBuffer(ctx, image);
433
434         _mesa_unlock_texture(ctx, tex);
435      }
436      surf->state = GL_SURFACE_REGISTERED_NV;
437   }
438}
439