xref: /aosp_15_r20/external/mesa3d/src/mesa/state_tracker/st_interop.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright 2009, VMware, Inc.
3  * Copyright (C) 2010 LunarG Inc.
4  * Copyright © Microsoft Corporation
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23  * IN THE SOFTWARE.
24  */
25 
26 #include "st_interop.h"
27 #include "st_cb_texture.h"
28 #include "st_cb_flush.h"
29 #include "st_texture.h"
30 
31 #include "bufferobj.h"
32 #include "texobj.h"
33 #include "teximage.h"
34 #include "syncobj.h"
35 
36 int
st_interop_query_device_info(struct st_context * st,struct mesa_glinterop_device_info * out)37 st_interop_query_device_info(struct st_context *st,
38                              struct mesa_glinterop_device_info *out)
39 {
40    struct pipe_screen *screen = st->pipe->screen;
41 
42    /* There is no version 0, thus we do not support it */
43    if (out->version == 0)
44       return MESA_GLINTEROP_INVALID_VERSION;
45 
46    if (!screen->resource_get_handle && !screen->interop_export_object)
47       return MESA_GLINTEROP_UNSUPPORTED;
48 
49    /* PCI values are obsolete on version >= 4 of the interface */
50    if (out->version < 4) {
51       out->pci_segment_group = screen->get_param(screen, PIPE_CAP_PCI_GROUP);
52       out->pci_bus = screen->get_param(screen, PIPE_CAP_PCI_BUS);
53       out->pci_device = screen->get_param(screen, PIPE_CAP_PCI_DEVICE);
54       out->pci_function = screen->get_param(screen, PIPE_CAP_PCI_FUNCTION);
55    }
56 
57    out->vendor_id = screen->get_param(screen, PIPE_CAP_VENDOR_ID);
58    out->device_id = screen->get_param(screen, PIPE_CAP_DEVICE_ID);
59 
60    if (out->version > 1 && screen->interop_query_device_info)
61       out->driver_data_size = screen->interop_query_device_info(screen,
62                                                                 out->driver_data_size,
63                                                                 out->driver_data);
64 
65    if (out->version >= 3 && screen->get_device_uuid)
66       screen->get_device_uuid(screen, out->device_uuid);
67 
68    /* Instruct the caller that we support up-to version four of the interface */
69    out->version = MIN2(out->version, 4);
70 
71    return MESA_GLINTEROP_SUCCESS;
72 }
73 
74 static int
lookup_object(struct gl_context * ctx,struct mesa_glinterop_export_in * in,struct mesa_glinterop_export_out * out,struct pipe_resource ** res)75 lookup_object(struct gl_context *ctx,
76               struct mesa_glinterop_export_in *in,
77               struct mesa_glinterop_export_out *out, struct pipe_resource **res)
78 {
79    unsigned target = in->target;
80    /* Validate the target. */
81    switch (in->target) {
82    case GL_TEXTURE_BUFFER:
83    case GL_TEXTURE_1D:
84    case GL_TEXTURE_2D:
85    case GL_TEXTURE_3D:
86    case GL_TEXTURE_RECTANGLE:
87    case GL_TEXTURE_1D_ARRAY:
88    case GL_TEXTURE_2D_ARRAY:
89    case GL_TEXTURE_CUBE_MAP_ARRAY:
90    case GL_TEXTURE_CUBE_MAP:
91    case GL_TEXTURE_2D_MULTISAMPLE:
92    case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
93    case GL_TEXTURE_EXTERNAL_OES:
94    case GL_RENDERBUFFER:
95    case GL_ARRAY_BUFFER:
96       break;
97    case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
98    case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
99    case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
100    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
101    case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
102    case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
103       target = GL_TEXTURE_CUBE_MAP;
104       break;
105    default:
106       return MESA_GLINTEROP_INVALID_TARGET;
107    }
108 
109    /* Validate the simple case of miplevel. */
110    if ((target == GL_RENDERBUFFER || target == GL_ARRAY_BUFFER) &&
111        in->miplevel != 0)
112       return MESA_GLINTEROP_INVALID_MIP_LEVEL;
113 
114    if (target == GL_ARRAY_BUFFER) {
115       /* Buffer objects.
116       *
117       * The error checking is based on the documentation of
118       * clCreateFromGLBuffer from OpenCL 2.0 SDK.
119       */
120       struct gl_buffer_object *buf = _mesa_lookup_bufferobj(ctx, in->obj);
121 
122       /* From OpenCL 2.0 SDK, clCreateFromGLBuffer:
123       *  "CL_INVALID_GL_OBJECT if bufobj is not a GL buffer object or is
124       *   a GL buffer object but does not have an existing data store or
125       *   the size of the buffer is 0."
126       */
127       if (!buf || buf->Size == 0)
128          return MESA_GLINTEROP_INVALID_OBJECT;
129 
130       *res = buf->buffer;
131       /* this shouldn't happen */
132       if (!*res)
133          return MESA_GLINTEROP_INVALID_OBJECT;
134 
135       if (out) {
136          out->buf_offset = 0;
137          out->buf_size = buf->Size;
138 
139          buf->UsageHistory |= USAGE_DISABLE_MINMAX_CACHE;
140       }
141    } else if (target == GL_RENDERBUFFER) {
142       /* Renderbuffers.
143       *
144       * The error checking is based on the documentation of
145       * clCreateFromGLRenderbuffer from OpenCL 2.0 SDK.
146       */
147       struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, in->obj);
148 
149       /* From OpenCL 2.0 SDK, clCreateFromGLRenderbuffer:
150       *   "CL_INVALID_GL_OBJECT if renderbuffer is not a GL renderbuffer
151       *    object or if the width or height of renderbuffer is zero."
152       */
153       if (!rb || rb->Width == 0 || rb->Height == 0)
154          return MESA_GLINTEROP_INVALID_OBJECT;
155 
156       /* From OpenCL 2.0 SDK, clCreateFromGLRenderbuffer:
157       *   "CL_INVALID_OPERATION if renderbuffer is a multi-sample GL
158       *    renderbuffer object."
159       */
160       if (rb->NumSamples > 1)
161          return MESA_GLINTEROP_INVALID_OPERATION;
162 
163       /* From OpenCL 2.0 SDK, clCreateFromGLRenderbuffer:
164       *   "CL_OUT_OF_RESOURCES if there is a failure to allocate resources
165       *    required by the OpenCL implementation on the device."
166       */
167       *res = rb->texture;
168       if (!*res)
169          return MESA_GLINTEROP_OUT_OF_RESOURCES;
170 
171       if (out) {
172          out->internal_format = rb->InternalFormat;
173          out->view_minlevel = 0;
174          out->view_numlevels = 1;
175          out->view_minlayer = 0;
176          out->view_numlayers = 1;
177 
178          if (out->version >= 2) {
179            out->width = rb->Width;
180            out->height = rb->Height;
181            out->depth = MAX2(1, rb->Depth);
182          }
183       }
184    } else {
185       /* Texture objects.
186       *
187       * The error checking is based on the documentation of
188       * clCreateFromGLTexture from OpenCL 2.0 SDK.
189       */
190       struct gl_texture_object *obj = _mesa_lookup_texture(ctx, in->obj);
191 
192       if (obj)
193          _mesa_test_texobj_completeness(ctx, obj);
194 
195       /* From OpenCL 2.0 SDK, clCreateFromGLTexture:
196       *   "CL_INVALID_GL_OBJECT if texture is not a GL texture object whose
197       *    type matches texture_target, if the specified miplevel of texture
198       *    is not defined, or if the width or height of the specified
199       *    miplevel is zero or if the GL texture object is incomplete."
200       */
201       if (!obj ||
202           obj->Target != target ||
203           !obj->_BaseComplete ||
204           (in->miplevel > 0 && !obj->_MipmapComplete))
205          return MESA_GLINTEROP_INVALID_OBJECT;
206 
207       if (target == GL_TEXTURE_BUFFER) {
208          struct gl_buffer_object *stBuf =
209             obj->BufferObject;
210 
211          /* this shouldn't happen */
212          if (!stBuf || !stBuf->buffer)
213             return MESA_GLINTEROP_INVALID_OBJECT;
214          *res = stBuf->buffer;
215 
216          if (out) {
217             out->internal_format = obj->BufferObjectFormat;
218             out->buf_offset = obj->BufferOffset;
219             out->buf_size = obj->BufferSize == -1 ? obj->BufferObject->Size :
220                obj->BufferSize;
221 
222             obj->BufferObject->UsageHistory |= USAGE_DISABLE_MINMAX_CACHE;
223          }
224       } else {
225          /* From OpenCL 2.0 SDK, clCreateFromGLTexture:
226          *   "CL_INVALID_MIP_LEVEL if miplevel is less than the value of
227          *    levelbase (for OpenGL implementations) or zero (for OpenGL ES
228          *    implementations); or greater than the value of q (for both OpenGL
229          *    and OpenGL ES). levelbase and q are defined for the texture in
230          *    section 3.8.10 (Texture Completeness) of the OpenGL 2.1
231          *    specification and section 3.7.10 of the OpenGL ES 2.0."
232          */
233          if (in->miplevel < obj->Attrib.BaseLevel || in->miplevel > obj->_MaxLevel)
234             return MESA_GLINTEROP_INVALID_MIP_LEVEL;
235 
236          if (!st_finalize_texture(ctx, ctx->st->pipe, obj, 0))
237             return MESA_GLINTEROP_OUT_OF_RESOURCES;
238 
239          *res = st_get_texobj_resource(obj);
240          /* Incomplete texture buffer object? This shouldn't really occur. */
241          if (!*res)
242             return MESA_GLINTEROP_INVALID_OBJECT;
243 
244          if (out) {
245             out->internal_format = obj->Image[0][0]->InternalFormat;
246             out->view_minlevel = obj->Attrib.MinLevel;
247             out->view_numlevels = obj->Attrib.NumLevels;
248             out->view_minlayer = obj->Attrib.MinLayer;
249             out->view_numlayers = obj->Attrib.NumLayers;
250 
251             if (out->version >= 2) {
252                const GLuint face = _mesa_tex_target_to_face(in->target);;
253                struct gl_texture_image *image = obj->Image[face][in->miplevel];
254 
255                out->width = image->Width;
256                out->height = image->Height;
257                out->depth = image->Depth;
258             }
259          }
260       }
261    }
262    return MESA_GLINTEROP_SUCCESS;
263 }
264 
265 int
st_interop_export_object(struct st_context * st,struct mesa_glinterop_export_in * in,struct mesa_glinterop_export_out * out)266 st_interop_export_object(struct st_context *st,
267                          struct mesa_glinterop_export_in *in,
268                          struct mesa_glinterop_export_out *out)
269 {
270    struct pipe_screen *screen = st->pipe->screen;
271    struct gl_context *ctx = st->ctx;
272    struct pipe_resource *res = NULL;
273    struct winsys_handle whandle;
274    unsigned usage;
275    bool success;
276    bool need_export_dmabuf = true;
277 
278    /* There is no version 0, thus we do not support it */
279    if (in->version == 0 || out->version == 0)
280       return MESA_GLINTEROP_INVALID_VERSION;
281 
282    if (!screen->resource_get_handle && !screen->interop_export_object)
283       return MESA_GLINTEROP_UNSUPPORTED;
284 
285    /* Wait for glthread to finish to get up-to-date GL object lookups. */
286    _mesa_glthread_finish(st->ctx);
287 
288    /* Validate the OpenGL object and get pipe_resource. */
289    simple_mtx_lock(&ctx->Shared->Mutex);
290 
291    int ret = lookup_object(ctx, in, out, &res);
292    if (ret != MESA_GLINTEROP_SUCCESS) {
293       simple_mtx_unlock(&ctx->Shared->Mutex);
294       return ret;
295    }
296 
297    /* Get the handle. */
298    switch (in->access) {
299    case MESA_GLINTEROP_ACCESS_READ_ONLY:
300       usage = 0;
301       break;
302    case MESA_GLINTEROP_ACCESS_READ_WRITE:
303    case MESA_GLINTEROP_ACCESS_WRITE_ONLY:
304       usage = PIPE_HANDLE_USAGE_SHADER_WRITE;
305       break;
306    default:
307       usage = 0;
308    }
309 
310    out->out_driver_data_written = 0;
311    if (screen->interop_export_object) {
312       out->out_driver_data_written = screen->interop_export_object(screen,
313                                                                    res,
314                                                                    in->out_driver_data_size,
315                                                                    in->out_driver_data,
316                                                                    &need_export_dmabuf);
317    }
318 
319    memset(&whandle, 0, sizeof(whandle));
320 
321    if (need_export_dmabuf) {
322       whandle.type = WINSYS_HANDLE_TYPE_FD;
323 
324       /* OpenCL requires explicit flushes. */
325       if (out->version >= 2)
326          usage |= PIPE_HANDLE_USAGE_EXPLICIT_FLUSH;
327 
328       success = screen->resource_get_handle(screen, st->pipe, res, &whandle,
329                                             usage);
330 
331       if (!success) {
332          simple_mtx_unlock(&ctx->Shared->Mutex);
333          return MESA_GLINTEROP_OUT_OF_HOST_MEMORY;
334       }
335 
336 #ifndef _WIN32
337       out->dmabuf_fd = whandle.handle;
338 #else
339       out->win32_handle = whandle.handle;
340 #endif
341 
342       if (out->version >= 2) {
343          out->modifier = whandle.modifier;
344          out->stride = whandle.stride;
345       }
346    }
347 
348    simple_mtx_unlock(&ctx->Shared->Mutex);
349 
350    if (res->target == PIPE_BUFFER)
351       out->buf_offset += whandle.offset;
352 
353    /* Instruct the caller of the version of the interface we support */
354    in->version = MIN2(in->version, 2);
355    out->version = MIN2(out->version, 2);
356 
357    return MESA_GLINTEROP_SUCCESS;
358 }
359 
360 static int
flush_object(struct gl_context * ctx,struct mesa_glinterop_export_in * in)361 flush_object(struct gl_context *ctx,
362                         struct mesa_glinterop_export_in *in)
363 {
364    struct pipe_resource *res = NULL;
365    /* There is no version 0, thus we do not support it */
366    if (in->version == 0)
367       return MESA_GLINTEROP_INVALID_VERSION;
368 
369    int ret = lookup_object(ctx, in, NULL, &res);
370    if (ret != MESA_GLINTEROP_SUCCESS)
371       return ret;
372 
373    ctx->pipe->flush_resource(ctx->pipe, res);
374 
375    /* Instruct the caller of the version of the interface we support */
376    in->version = MIN2(in->version, 2);
377 
378    return MESA_GLINTEROP_SUCCESS;
379 }
380 
381 int
st_interop_flush_objects(struct st_context * st,unsigned count,struct mesa_glinterop_export_in * objects,struct mesa_glinterop_flush_out * out)382 st_interop_flush_objects(struct st_context *st,
383                          unsigned count, struct mesa_glinterop_export_in *objects,
384                          struct mesa_glinterop_flush_out *out)
385 {
386    struct gl_context *ctx = st->ctx;
387    bool flush_out_struct = false;
388 
389    if (!ctx->screen->resource_get_handle && !ctx->screen->interop_export_object)
390       return MESA_GLINTEROP_UNSUPPORTED;
391 
392    /* Wait for glthread to finish to get up-to-date GL object lookups. */
393    _mesa_glthread_finish(st->ctx);
394 
395    simple_mtx_lock(&ctx->Shared->Mutex);
396 
397    for (unsigned i = 0; i < count; ++i) {
398       int ret = flush_object(ctx, &objects[i]);
399 
400       if (objects[i].version >= 2)
401          flush_out_struct = true;
402 
403       if (ret != MESA_GLINTEROP_SUCCESS) {
404          simple_mtx_unlock(&ctx->Shared->Mutex);
405          return ret;
406       }
407    }
408 
409    simple_mtx_unlock(&ctx->Shared->Mutex);
410 
411    if (count > 0 && out) {
412       if (flush_out_struct) {
413          if (out->sync) {
414             *out->sync = _mesa_fence_sync(ctx, GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
415          }
416          if (out->fence_fd) {
417             struct pipe_fence_handle *fence = NULL;
418             ctx->pipe->flush(ctx->pipe, &fence, PIPE_FLUSH_FENCE_FD | PIPE_FLUSH_ASYNC);
419             *out->fence_fd = ctx->screen->fence_get_fd(ctx->screen, fence);
420          }
421          out->version = MIN2(out->version, 1);
422       } else {
423          GLsync *sync = (GLsync *)out;
424          *sync = _mesa_fence_sync(ctx, GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
425       }
426    }
427 
428    return MESA_GLINTEROP_SUCCESS;
429 }
430