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 /* PCI values are obsolete on version >= 4 of the interface */
47 if (out->version < 4) {
48 out->pci_segment_group = screen->get_param(screen, PIPE_CAP_PCI_GROUP);
49 out->pci_bus = screen->get_param(screen, PIPE_CAP_PCI_BUS);
50 out->pci_device = screen->get_param(screen, PIPE_CAP_PCI_DEVICE);
51 out->pci_function = screen->get_param(screen, PIPE_CAP_PCI_FUNCTION);
52 }
53
54 out->vendor_id = screen->get_param(screen, PIPE_CAP_VENDOR_ID);
55 out->device_id = screen->get_param(screen, PIPE_CAP_DEVICE_ID);
56
57 if (out->version > 1 && screen->interop_query_device_info)
58 out->driver_data_size = screen->interop_query_device_info(screen,
59 out->driver_data_size,
60 out->driver_data);
61
62 if (out->version >= 3 && screen->get_device_uuid)
63 screen->get_device_uuid(screen, out->device_uuid);
64
65 /* Instruct the caller that we support up-to version four of the interface */
66 out->version = MIN2(out->version, 4);
67
68 return MESA_GLINTEROP_SUCCESS;
69 }
70
71 static int
lookup_object(struct gl_context * ctx,struct mesa_glinterop_export_in * in,struct mesa_glinterop_export_out * out,struct pipe_resource ** res)72 lookup_object(struct gl_context *ctx,
73 struct mesa_glinterop_export_in *in,
74 struct mesa_glinterop_export_out *out, struct pipe_resource **res)
75 {
76 unsigned target = in->target;
77 /* Validate the target. */
78 switch (in->target) {
79 case GL_TEXTURE_BUFFER:
80 case GL_TEXTURE_1D:
81 case GL_TEXTURE_2D:
82 case GL_TEXTURE_3D:
83 case GL_TEXTURE_RECTANGLE:
84 case GL_TEXTURE_1D_ARRAY:
85 case GL_TEXTURE_2D_ARRAY:
86 case GL_TEXTURE_CUBE_MAP_ARRAY:
87 case GL_TEXTURE_CUBE_MAP:
88 case GL_TEXTURE_2D_MULTISAMPLE:
89 case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
90 case GL_TEXTURE_EXTERNAL_OES:
91 case GL_RENDERBUFFER:
92 case GL_ARRAY_BUFFER:
93 break;
94 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
95 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
96 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
97 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
98 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
99 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
100 target = GL_TEXTURE_CUBE_MAP;
101 break;
102 default:
103 return MESA_GLINTEROP_INVALID_TARGET;
104 }
105
106 /* Validate the simple case of miplevel. */
107 if ((target == GL_RENDERBUFFER || target == GL_ARRAY_BUFFER) &&
108 in->miplevel != 0)
109 return MESA_GLINTEROP_INVALID_MIP_LEVEL;
110
111 if (target == GL_ARRAY_BUFFER) {
112 /* Buffer objects.
113 *
114 * The error checking is based on the documentation of
115 * clCreateFromGLBuffer from OpenCL 2.0 SDK.
116 */
117 struct gl_buffer_object *buf = _mesa_lookup_bufferobj(ctx, in->obj);
118
119 /* From OpenCL 2.0 SDK, clCreateFromGLBuffer:
120 * "CL_INVALID_GL_OBJECT if bufobj is not a GL buffer object or is
121 * a GL buffer object but does not have an existing data store or
122 * the size of the buffer is 0."
123 */
124 if (!buf || buf->Size == 0)
125 return MESA_GLINTEROP_INVALID_OBJECT;
126
127 *res = buf->buffer;
128 /* this shouldn't happen */
129 if (!*res)
130 return MESA_GLINTEROP_INVALID_OBJECT;
131
132 if (out) {
133 out->buf_offset = 0;
134 out->buf_size = buf->Size;
135
136 buf->UsageHistory |= USAGE_DISABLE_MINMAX_CACHE;
137 }
138 } else if (target == GL_RENDERBUFFER) {
139 /* Renderbuffers.
140 *
141 * The error checking is based on the documentation of
142 * clCreateFromGLRenderbuffer from OpenCL 2.0 SDK.
143 */
144 struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, in->obj);
145
146 /* From OpenCL 2.0 SDK, clCreateFromGLRenderbuffer:
147 * "CL_INVALID_GL_OBJECT if renderbuffer is not a GL renderbuffer
148 * object or if the width or height of renderbuffer is zero."
149 */
150 if (!rb || rb->Width == 0 || rb->Height == 0)
151 return MESA_GLINTEROP_INVALID_OBJECT;
152
153 /* From OpenCL 2.0 SDK, clCreateFromGLRenderbuffer:
154 * "CL_INVALID_OPERATION if renderbuffer is a multi-sample GL
155 * renderbuffer object."
156 */
157 if (rb->NumSamples > 1)
158 return MESA_GLINTEROP_INVALID_OPERATION;
159
160 /* From OpenCL 2.0 SDK, clCreateFromGLRenderbuffer:
161 * "CL_OUT_OF_RESOURCES if there is a failure to allocate resources
162 * required by the OpenCL implementation on the device."
163 */
164 *res = rb->texture;
165 if (!*res)
166 return MESA_GLINTEROP_OUT_OF_RESOURCES;
167
168 if (out) {
169 out->internal_format = rb->InternalFormat;
170 out->view_minlevel = 0;
171 out->view_numlevels = 1;
172 out->view_minlayer = 0;
173 out->view_numlayers = 1;
174
175 if (out->version >= 2) {
176 out->width = rb->Width;
177 out->height = rb->Height;
178 out->depth = MAX2(1, rb->Depth);
179 }
180 }
181 } else {
182 /* Texture objects.
183 *
184 * The error checking is based on the documentation of
185 * clCreateFromGLTexture from OpenCL 2.0 SDK.
186 */
187 struct gl_texture_object *obj = _mesa_lookup_texture(ctx, in->obj);
188
189 if (obj)
190 _mesa_test_texobj_completeness(ctx, obj);
191
192 /* From OpenCL 2.0 SDK, clCreateFromGLTexture:
193 * "CL_INVALID_GL_OBJECT if texture is not a GL texture object whose
194 * type matches texture_target, if the specified miplevel of texture
195 * is not defined, or if the width or height of the specified
196 * miplevel is zero or if the GL texture object is incomplete."
197 */
198 if (!obj ||
199 obj->Target != target ||
200 !obj->_BaseComplete ||
201 (in->miplevel > 0 && !obj->_MipmapComplete))
202 return MESA_GLINTEROP_INVALID_OBJECT;
203
204 if (target == GL_TEXTURE_BUFFER) {
205 struct gl_buffer_object *stBuf =
206 obj->BufferObject;
207
208 /* this shouldn't happen */
209 if (!stBuf || !stBuf->buffer)
210 return MESA_GLINTEROP_INVALID_OBJECT;
211 *res = stBuf->buffer;
212
213 if (out) {
214 out->internal_format = obj->BufferObjectFormat;
215 out->buf_offset = obj->BufferOffset;
216 out->buf_size = obj->BufferSize == -1 ? obj->BufferObject->Size :
217 obj->BufferSize;
218
219 obj->BufferObject->UsageHistory |= USAGE_DISABLE_MINMAX_CACHE;
220 }
221 } else {
222 /* From OpenCL 2.0 SDK, clCreateFromGLTexture:
223 * "CL_INVALID_MIP_LEVEL if miplevel is less than the value of
224 * levelbase (for OpenGL implementations) or zero (for OpenGL ES
225 * implementations); or greater than the value of q (for both OpenGL
226 * and OpenGL ES). levelbase and q are defined for the texture in
227 * section 3.8.10 (Texture Completeness) of the OpenGL 2.1
228 * specification and section 3.7.10 of the OpenGL ES 2.0."
229 */
230 if (in->miplevel < obj->Attrib.BaseLevel || in->miplevel > obj->_MaxLevel)
231 return MESA_GLINTEROP_INVALID_MIP_LEVEL;
232
233 if (!st_finalize_texture(ctx, ctx->st->pipe, obj, 0))
234 return MESA_GLINTEROP_OUT_OF_RESOURCES;
235
236 *res = st_get_texobj_resource(obj);
237 /* Incomplete texture buffer object? This shouldn't really occur. */
238 if (!*res)
239 return MESA_GLINTEROP_INVALID_OBJECT;
240
241 if (out) {
242 out->internal_format = obj->Image[0][0]->InternalFormat;
243 out->view_minlevel = obj->Attrib.MinLevel;
244 out->view_numlevels = obj->Attrib.NumLevels;
245 out->view_minlayer = obj->Attrib.MinLayer;
246 out->view_numlayers = obj->Attrib.NumLayers;
247
248 if (out->version >= 2) {
249 const GLuint face = _mesa_tex_target_to_face(in->target);;
250 struct gl_texture_image *image = obj->Image[face][in->miplevel];
251
252 out->width = image->Width;
253 out->height = image->Height;
254 out->depth = image->Depth;
255 }
256 }
257 }
258 }
259 return MESA_GLINTEROP_SUCCESS;
260 }
261
262 int
st_interop_export_object(struct st_context * st,struct mesa_glinterop_export_in * in,struct mesa_glinterop_export_out * out)263 st_interop_export_object(struct st_context *st,
264 struct mesa_glinterop_export_in *in,
265 struct mesa_glinterop_export_out *out)
266 {
267 struct pipe_screen *screen = st->pipe->screen;
268 struct gl_context *ctx = st->ctx;
269 struct pipe_resource *res = NULL;
270 struct winsys_handle whandle;
271 unsigned usage;
272 bool success;
273 bool need_export_dmabuf = true;
274
275 /* There is no version 0, thus we do not support it */
276 if (in->version == 0 || out->version == 0)
277 return MESA_GLINTEROP_INVALID_VERSION;
278
279 /* Wait for glthread to finish to get up-to-date GL object lookups. */
280 _mesa_glthread_finish(st->ctx);
281
282 /* Validate the OpenGL object and get pipe_resource. */
283 simple_mtx_lock(&ctx->Shared->Mutex);
284
285 int ret = lookup_object(ctx, in, out, &res);
286 if (ret != MESA_GLINTEROP_SUCCESS) {
287 simple_mtx_unlock(&ctx->Shared->Mutex);
288 return ret;
289 }
290
291 /* Get the handle. */
292 switch (in->access) {
293 case MESA_GLINTEROP_ACCESS_READ_ONLY:
294 usage = 0;
295 break;
296 case MESA_GLINTEROP_ACCESS_READ_WRITE:
297 case MESA_GLINTEROP_ACCESS_WRITE_ONLY:
298 usage = PIPE_HANDLE_USAGE_SHADER_WRITE;
299 break;
300 default:
301 usage = 0;
302 }
303
304 out->out_driver_data_written = 0;
305 if (screen->interop_export_object) {
306 out->out_driver_data_written = screen->interop_export_object(screen,
307 res,
308 in->out_driver_data_size,
309 in->out_driver_data,
310 &need_export_dmabuf);
311 }
312
313 memset(&whandle, 0, sizeof(whandle));
314
315 if (need_export_dmabuf) {
316 whandle.type = WINSYS_HANDLE_TYPE_FD;
317
318 /* OpenCL requires explicit flushes. */
319 if (out->version >= 2)
320 usage |= PIPE_HANDLE_USAGE_EXPLICIT_FLUSH;
321
322 success = screen->resource_get_handle(screen, st->pipe, res, &whandle,
323 usage);
324
325 if (!success) {
326 simple_mtx_unlock(&ctx->Shared->Mutex);
327 return MESA_GLINTEROP_OUT_OF_HOST_MEMORY;
328 }
329
330 #ifndef _WIN32
331 out->dmabuf_fd = whandle.handle;
332 #else
333 out->win32_handle = whandle.handle;
334 #endif
335
336 if (out->version >= 2) {
337 out->modifier = whandle.modifier;
338 out->stride = whandle.stride;
339 }
340 }
341
342 simple_mtx_unlock(&ctx->Shared->Mutex);
343
344 if (res->target == PIPE_BUFFER)
345 out->buf_offset += whandle.offset;
346
347 /* Instruct the caller of the version of the interface we support */
348 in->version = MIN2(in->version, 2);
349 out->version = MIN2(out->version, 2);
350
351 return MESA_GLINTEROP_SUCCESS;
352 }
353
354 static int
flush_object(struct gl_context * ctx,struct mesa_glinterop_export_in * in)355 flush_object(struct gl_context *ctx,
356 struct mesa_glinterop_export_in *in)
357 {
358 struct pipe_resource *res = NULL;
359 /* There is no version 0, thus we do not support it */
360 if (in->version == 0)
361 return MESA_GLINTEROP_INVALID_VERSION;
362
363 int ret = lookup_object(ctx, in, NULL, &res);
364 if (ret != MESA_GLINTEROP_SUCCESS)
365 return ret;
366
367 ctx->pipe->flush_resource(ctx->pipe, res);
368
369 /* Instruct the caller of the version of the interface we support */
370 in->version = MIN2(in->version, 2);
371
372 return MESA_GLINTEROP_SUCCESS;
373 }
374
375 int
st_interop_flush_objects(struct st_context * st,unsigned count,struct mesa_glinterop_export_in * objects,struct mesa_glinterop_flush_out * out)376 st_interop_flush_objects(struct st_context *st,
377 unsigned count, struct mesa_glinterop_export_in *objects,
378 struct mesa_glinterop_flush_out *out)
379 {
380 struct gl_context *ctx = st->ctx;
381 bool flush_out_struct = false;
382
383 /* Wait for glthread to finish to get up-to-date GL object lookups. */
384 _mesa_glthread_finish(st->ctx);
385
386 simple_mtx_lock(&ctx->Shared->Mutex);
387
388 for (unsigned i = 0; i < count; ++i) {
389 int ret = flush_object(ctx, &objects[i]);
390
391 if (objects[i].version >= 2)
392 flush_out_struct = true;
393
394 if (ret != MESA_GLINTEROP_SUCCESS) {
395 simple_mtx_unlock(&ctx->Shared->Mutex);
396 return ret;
397 }
398 }
399
400 simple_mtx_unlock(&ctx->Shared->Mutex);
401
402 if (count > 0 && out) {
403 if (flush_out_struct) {
404 if (out->sync) {
405 *out->sync = _mesa_fence_sync(ctx, GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
406 }
407 if (out->fence_fd) {
408 struct pipe_fence_handle *fence = NULL;
409 ctx->pipe->flush(ctx->pipe, &fence, PIPE_FLUSH_FENCE_FD | PIPE_FLUSH_ASYNC);
410 *out->fence_fd = ctx->screen->fence_get_fd(ctx->screen, fence);
411 }
412 out->version = MIN2(out->version, 1);
413 } else {
414 GLsync *sync = (GLsync *)out;
415 *sync = _mesa_fence_sync(ctx, GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
416 }
417 }
418
419 return MESA_GLINTEROP_SUCCESS;
420 }
421