Files
third_party_mesa3d/src/gallium/frontends/dri/dri2.c
Pierre-Eric Pelloux-Prayer 9b0ffa9ecd egl/dri2: implement createImageFromDmaBufs3
And refuse to import image with protected_content enabled.

We don't want a compositor to import an encrypted buffer in a image
without the ProtectedContent attribute enabled, because that will
lead to incorrect display.

Similarly, if the compositor thinks the image is encrypted, we fail
the import if the buffer is not.

Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/5096>
2020-11-02 10:15:47 +01:00

2408 lines
74 KiB
C

/*
* Mesa 3-D graphics library
*
* Copyright 2009, VMware, Inc.
* All Rights Reserved.
* Copyright (C) 2010 LunarG Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors:
* Keith Whitwell <keithw@vmware.com> Jakob Bornecrantz
* <wallbraker@gmail.com> Chia-I Wu <olv@lunarg.com>
*/
#include <xf86drm.h>
#include "GL/mesa_glinterop.h"
#include "util/disk_cache.h"
#include "util/u_memory.h"
#include "util/u_inlines.h"
#include "util/format/u_format.h"
#include "util/u_debug.h"
#include "frontend/drm_driver.h"
#include "state_tracker/st_cb_bufferobjects.h"
#include "state_tracker/st_cb_fbo.h"
#include "state_tracker/st_cb_texture.h"
#include "state_tracker/st_texture.h"
#include "state_tracker/st_context.h"
#include "pipe-loader/pipe_loader.h"
#include "main/bufferobj.h"
#include "main/texobj.h"
#include "dri_util.h"
#include "dri_helpers.h"
#include "dri_drawable.h"
#include "dri_query_renderer.h"
#include "drm-uapi/drm_fourcc.h"
struct dri2_buffer
{
__DRIbuffer base;
struct pipe_resource *resource;
};
static inline struct dri2_buffer *
dri2_buffer(__DRIbuffer * driBufferPriv)
{
return (struct dri2_buffer *) driBufferPriv;
}
/**
* DRI2 flush extension.
*/
static void
dri2_flush_drawable(__DRIdrawable *dPriv)
{
dri_flush(dPriv->driContextPriv, dPriv, __DRI2_FLUSH_DRAWABLE, -1);
}
static void
dri2_invalidate_drawable(__DRIdrawable *dPriv)
{
struct dri_drawable *drawable = dri_drawable(dPriv);
dri2InvalidateDrawable(dPriv);
drawable->dPriv->lastStamp = drawable->dPriv->dri2.stamp;
drawable->texture_mask = 0;
p_atomic_inc(&drawable->base.stamp);
}
static const __DRI2flushExtension dri2FlushExtension = {
.base = { __DRI2_FLUSH, 4 },
.flush = dri2_flush_drawable,
.invalidate = dri2_invalidate_drawable,
.flush_with_flags = dri_flush,
};
/**
* Retrieve __DRIbuffer from the DRI loader.
*/
static __DRIbuffer *
dri2_drawable_get_buffers(struct dri_drawable *drawable,
const enum st_attachment_type *atts,
unsigned *count)
{
__DRIdrawable *dri_drawable = drawable->dPriv;
const __DRIdri2LoaderExtension *loader = drawable->sPriv->dri2.loader;
boolean with_format;
__DRIbuffer *buffers;
int num_buffers;
unsigned attachments[10];
unsigned num_attachments, i;
assert(loader);
with_format = dri_with_format(drawable->sPriv);
num_attachments = 0;
/* for Xserver 1.6.0 (DRI2 version 1) we always need to ask for the front */
if (!with_format)
attachments[num_attachments++] = __DRI_BUFFER_FRONT_LEFT;
for (i = 0; i < *count; i++) {
enum pipe_format format;
unsigned bind;
int att, depth;
dri_drawable_get_format(drawable, atts[i], &format, &bind);
if (format == PIPE_FORMAT_NONE)
continue;
switch (atts[i]) {
case ST_ATTACHMENT_FRONT_LEFT:
/* already added */
if (!with_format)
continue;
att = __DRI_BUFFER_FRONT_LEFT;
break;
case ST_ATTACHMENT_BACK_LEFT:
att = __DRI_BUFFER_BACK_LEFT;
break;
case ST_ATTACHMENT_FRONT_RIGHT:
att = __DRI_BUFFER_FRONT_RIGHT;
break;
case ST_ATTACHMENT_BACK_RIGHT:
att = __DRI_BUFFER_BACK_RIGHT;
break;
default:
continue;
}
/*
* In this switch statement we must support all formats that
* may occur as the stvis->color_format.
*/
switch(format) {
case PIPE_FORMAT_R16G16B16A16_FLOAT:
depth = 64;
break;
case PIPE_FORMAT_R16G16B16X16_FLOAT:
depth = 48;
break;
case PIPE_FORMAT_B10G10R10A2_UNORM:
case PIPE_FORMAT_R10G10B10A2_UNORM:
case PIPE_FORMAT_BGRA8888_UNORM:
case PIPE_FORMAT_RGBA8888_UNORM:
depth = 32;
break;
case PIPE_FORMAT_R10G10B10X2_UNORM:
case PIPE_FORMAT_B10G10R10X2_UNORM:
depth = 30;
break;
case PIPE_FORMAT_BGRX8888_UNORM:
case PIPE_FORMAT_RGBX8888_UNORM:
depth = 24;
break;
case PIPE_FORMAT_B5G6R5_UNORM:
depth = 16;
break;
default:
depth = util_format_get_blocksizebits(format);
assert(!"Unexpected format in dri2_drawable_get_buffers()");
}
attachments[num_attachments++] = att;
if (with_format) {
attachments[num_attachments++] = depth;
}
}
if (with_format) {
num_attachments /= 2;
buffers = loader->getBuffersWithFormat(dri_drawable,
&dri_drawable->w, &dri_drawable->h,
attachments, num_attachments,
&num_buffers, dri_drawable->loaderPrivate);
}
else {
buffers = loader->getBuffers(dri_drawable,
&dri_drawable->w, &dri_drawable->h,
attachments, num_attachments,
&num_buffers, dri_drawable->loaderPrivate);
}
if (buffers)
*count = num_buffers;
return buffers;
}
static bool
dri_image_drawable_get_buffers(struct dri_drawable *drawable,
struct __DRIimageList *images,
const enum st_attachment_type *statts,
unsigned statts_count)
{
__DRIdrawable *dPriv = drawable->dPriv;
__DRIscreen *sPriv = drawable->sPriv;
unsigned int image_format = __DRI_IMAGE_FORMAT_NONE;
enum pipe_format pf;
uint32_t buffer_mask = 0;
unsigned i, bind;
for (i = 0; i < statts_count; i++) {
dri_drawable_get_format(drawable, statts[i], &pf, &bind);
if (pf == PIPE_FORMAT_NONE)
continue;
switch (statts[i]) {
case ST_ATTACHMENT_FRONT_LEFT:
buffer_mask |= __DRI_IMAGE_BUFFER_FRONT;
break;
case ST_ATTACHMENT_BACK_LEFT:
buffer_mask |= __DRI_IMAGE_BUFFER_BACK;
break;
default:
continue;
}
switch (pf) {
case PIPE_FORMAT_R16G16B16A16_FLOAT:
image_format = __DRI_IMAGE_FORMAT_ABGR16161616F;
break;
case PIPE_FORMAT_R16G16B16X16_FLOAT:
image_format = __DRI_IMAGE_FORMAT_XBGR16161616F;
break;
case PIPE_FORMAT_B5G5R5A1_UNORM:
image_format = __DRI_IMAGE_FORMAT_ARGB1555;
break;
case PIPE_FORMAT_B5G6R5_UNORM:
image_format = __DRI_IMAGE_FORMAT_RGB565;
break;
case PIPE_FORMAT_BGRX8888_UNORM:
image_format = __DRI_IMAGE_FORMAT_XRGB8888;
break;
case PIPE_FORMAT_BGRA8888_UNORM:
image_format = __DRI_IMAGE_FORMAT_ARGB8888;
break;
case PIPE_FORMAT_RGBX8888_UNORM:
image_format = __DRI_IMAGE_FORMAT_XBGR8888;
break;
case PIPE_FORMAT_RGBA8888_UNORM:
image_format = __DRI_IMAGE_FORMAT_ABGR8888;
break;
case PIPE_FORMAT_B10G10R10X2_UNORM:
image_format = __DRI_IMAGE_FORMAT_XRGB2101010;
break;
case PIPE_FORMAT_B10G10R10A2_UNORM:
image_format = __DRI_IMAGE_FORMAT_ARGB2101010;
break;
case PIPE_FORMAT_R10G10B10X2_UNORM:
image_format = __DRI_IMAGE_FORMAT_XBGR2101010;
break;
case PIPE_FORMAT_R10G10B10A2_UNORM:
image_format = __DRI_IMAGE_FORMAT_ABGR2101010;
break;
default:
image_format = __DRI_IMAGE_FORMAT_NONE;
break;
}
}
return (*sPriv->image.loader->getBuffers) (dPriv, image_format,
(uint32_t *) &drawable->base.stamp,
dPriv->loaderPrivate, buffer_mask,
images);
}
static __DRIbuffer *
dri2_allocate_buffer(__DRIscreen *sPriv,
unsigned attachment, unsigned format,
int width, int height)
{
struct dri_screen *screen = dri_screen(sPriv);
struct dri2_buffer *buffer;
struct pipe_resource templ;
enum pipe_format pf;
unsigned bind = 0;
struct winsys_handle whandle;
switch (attachment) {
case __DRI_BUFFER_FRONT_LEFT:
case __DRI_BUFFER_FAKE_FRONT_LEFT:
bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
break;
case __DRI_BUFFER_BACK_LEFT:
bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW;
break;
case __DRI_BUFFER_DEPTH:
case __DRI_BUFFER_DEPTH_STENCIL:
case __DRI_BUFFER_STENCIL:
bind = PIPE_BIND_DEPTH_STENCIL; /* XXX sampler? */
break;
}
/* because we get the handle and stride */
bind |= PIPE_BIND_SHARED;
switch (format) {
case 64:
pf = PIPE_FORMAT_R16G16B16A16_FLOAT;
break;
case 48:
pf = PIPE_FORMAT_R16G16B16X16_FLOAT;
break;
case 32:
pf = PIPE_FORMAT_BGRA8888_UNORM;
break;
case 30:
pf = PIPE_FORMAT_B10G10R10X2_UNORM;
break;
case 24:
pf = PIPE_FORMAT_BGRX8888_UNORM;
break;
case 16:
pf = PIPE_FORMAT_Z16_UNORM;
break;
default:
return NULL;
}
buffer = CALLOC_STRUCT(dri2_buffer);
if (!buffer)
return NULL;
memset(&templ, 0, sizeof(templ));
templ.bind = bind;
templ.format = pf;
templ.target = PIPE_TEXTURE_2D;
templ.last_level = 0;
templ.width0 = width;
templ.height0 = height;
templ.depth0 = 1;
templ.array_size = 1;
buffer->resource =
screen->base.screen->resource_create(screen->base.screen, &templ);
if (!buffer->resource) {
FREE(buffer);
return NULL;
}
memset(&whandle, 0, sizeof(whandle));
if (screen->can_share_buffer)
whandle.type = WINSYS_HANDLE_TYPE_SHARED;
else
whandle.type = WINSYS_HANDLE_TYPE_KMS;
screen->base.screen->resource_get_handle(screen->base.screen, NULL,
buffer->resource, &whandle,
PIPE_HANDLE_USAGE_EXPLICIT_FLUSH);
buffer->base.attachment = attachment;
buffer->base.name = whandle.handle;
buffer->base.cpp = util_format_get_blocksize(pf);
buffer->base.pitch = whandle.stride;
return &buffer->base;
}
static void
dri2_release_buffer(__DRIscreen *sPriv, __DRIbuffer *bPriv)
{
struct dri2_buffer *buffer = dri2_buffer(bPriv);
pipe_resource_reference(&buffer->resource, NULL);
FREE(buffer);
}
/*
* Backend functions for st_framebuffer interface.
*/
static void
dri2_allocate_textures(struct dri_context *ctx,
struct dri_drawable *drawable,
const enum st_attachment_type *statts,
unsigned statts_count)
{
__DRIscreen *sPriv = drawable->sPriv;
__DRIdrawable *dri_drawable = drawable->dPriv;
struct dri_screen *screen = dri_screen(sPriv);
struct pipe_resource templ;
boolean alloc_depthstencil = FALSE;
unsigned i, j, bind;
const __DRIimageLoaderExtension *image = sPriv->image.loader;
/* Image specific variables */
struct __DRIimageList images;
/* Dri2 specific variables */
__DRIbuffer *buffers = NULL;
struct winsys_handle whandle;
unsigned num_buffers = statts_count;
/* First get the buffers from the loader */
if (image) {
if (!dri_image_drawable_get_buffers(drawable, &images,
statts, statts_count))
return;
}
else {
buffers = dri2_drawable_get_buffers(drawable, statts, &num_buffers);
if (!buffers || (drawable->old_num == num_buffers &&
drawable->old_w == dri_drawable->w &&
drawable->old_h == dri_drawable->h &&
memcmp(drawable->old, buffers,
sizeof(__DRIbuffer) * num_buffers) == 0))
return;
}
/* Second clean useless resources*/
/* See if we need a depth-stencil buffer. */
for (i = 0; i < statts_count; i++) {
if (statts[i] == ST_ATTACHMENT_DEPTH_STENCIL) {
alloc_depthstencil = TRUE;
break;
}
}
/* Delete the resources we won't need. */
for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
/* Don't delete the depth-stencil buffer, we can reuse it. */
if (i == ST_ATTACHMENT_DEPTH_STENCIL && alloc_depthstencil)
continue;
/* Flush the texture before unreferencing, so that other clients can
* see what the driver has rendered.
*/
if (i != ST_ATTACHMENT_DEPTH_STENCIL && drawable->textures[i]) {
struct pipe_context *pipe = ctx->st->pipe;
pipe->flush_resource(pipe, drawable->textures[i]);
}
pipe_resource_reference(&drawable->textures[i], NULL);
}
if (drawable->stvis.samples > 1) {
for (i = 0; i < ST_ATTACHMENT_COUNT; i++) {
boolean del = TRUE;
/* Don't delete MSAA resources for the attachments which are enabled,
* we can reuse them. */
for (j = 0; j < statts_count; j++) {
if (i == statts[j]) {
del = FALSE;
break;
}
}
if (del) {
pipe_resource_reference(&drawable->msaa_textures[i], NULL);
}
}
}
/* Third use the buffers retrieved to fill the drawable info */
memset(&templ, 0, sizeof(templ));
templ.target = screen->target;
templ.last_level = 0;
templ.depth0 = 1;
templ.array_size = 1;
if (image) {
if (images.image_mask & __DRI_IMAGE_BUFFER_FRONT) {
struct pipe_resource **buf =
&drawable->textures[ST_ATTACHMENT_FRONT_LEFT];
struct pipe_resource *texture = images.front->texture;
dri_drawable->w = texture->width0;
dri_drawable->h = texture->height0;
pipe_resource_reference(buf, texture);
}
if (images.image_mask & __DRI_IMAGE_BUFFER_BACK) {
struct pipe_resource **buf =
&drawable->textures[ST_ATTACHMENT_BACK_LEFT];
struct pipe_resource *texture = images.back->texture;
dri_drawable->w = texture->width0;
dri_drawable->h = texture->height0;
pipe_resource_reference(buf, texture);
}
/* Note: if there is both a back and a front buffer,
* then they have the same size.
*/
templ.width0 = dri_drawable->w;
templ.height0 = dri_drawable->h;
}
else {
memset(&whandle, 0, sizeof(whandle));
/* Process DRI-provided buffers and get pipe_resources. */
for (i = 0; i < num_buffers; i++) {
__DRIbuffer *buf = &buffers[i];
enum st_attachment_type statt;
enum pipe_format format;
switch (buf->attachment) {
case __DRI_BUFFER_FRONT_LEFT:
if (!screen->auto_fake_front) {
continue; /* invalid attachment */
}
/* fallthrough */
case __DRI_BUFFER_FAKE_FRONT_LEFT:
statt = ST_ATTACHMENT_FRONT_LEFT;
break;
case __DRI_BUFFER_BACK_LEFT:
statt = ST_ATTACHMENT_BACK_LEFT;
break;
default:
continue; /* invalid attachment */
}
dri_drawable_get_format(drawable, statt, &format, &bind);
if (format == PIPE_FORMAT_NONE)
continue;
/* dri2_drawable_get_buffers has already filled dri_drawable->w
* and dri_drawable->h */
templ.width0 = dri_drawable->w;
templ.height0 = dri_drawable->h;
templ.format = format;
templ.bind = bind;
whandle.handle = buf->name;
whandle.stride = buf->pitch;
whandle.offset = 0;
whandle.format = format;
whandle.modifier = DRM_FORMAT_MOD_INVALID;
if (screen->can_share_buffer)
whandle.type = WINSYS_HANDLE_TYPE_SHARED;
else
whandle.type = WINSYS_HANDLE_TYPE_KMS;
drawable->textures[statt] =
screen->base.screen->resource_from_handle(screen->base.screen,
&templ, &whandle,
PIPE_HANDLE_USAGE_EXPLICIT_FLUSH);
assert(drawable->textures[statt]);
}
}
/* Allocate private MSAA colorbuffers. */
if (drawable->stvis.samples > 1) {
for (i = 0; i < statts_count; i++) {
enum st_attachment_type statt = statts[i];
if (statt == ST_ATTACHMENT_DEPTH_STENCIL)
continue;
if (drawable->textures[statt]) {
templ.format = drawable->textures[statt]->format;
templ.bind = drawable->textures[statt]->bind &
~(PIPE_BIND_SCANOUT | PIPE_BIND_SHARED);
templ.nr_samples = drawable->stvis.samples;
templ.nr_storage_samples = drawable->stvis.samples;
/* Try to reuse the resource.
* (the other resource parameters should be constant)
*/
if (!drawable->msaa_textures[statt] ||
drawable->msaa_textures[statt]->width0 != templ.width0 ||
drawable->msaa_textures[statt]->height0 != templ.height0) {
/* Allocate a new one. */
pipe_resource_reference(&drawable->msaa_textures[statt], NULL);
drawable->msaa_textures[statt] =
screen->base.screen->resource_create(screen->base.screen,
&templ);
assert(drawable->msaa_textures[statt]);
/* If there are any MSAA resources, we should initialize them
* such that they contain the same data as the single-sample
* resources we just got from the X server.
*
* The reason for this is that the gallium frontend (and
* therefore the app) can access the MSAA resources only.
* The single-sample resources are not exposed
* to the gallium frontend.
*
*/
dri_pipe_blit(ctx->st->pipe,
drawable->msaa_textures[statt],
drawable->textures[statt]);
}
}
else {
pipe_resource_reference(&drawable->msaa_textures[statt], NULL);
}
}
}
/* Allocate a private depth-stencil buffer. */
if (alloc_depthstencil) {
enum st_attachment_type statt = ST_ATTACHMENT_DEPTH_STENCIL;
struct pipe_resource **zsbuf;
enum pipe_format format;
unsigned bind;
dri_drawable_get_format(drawable, statt, &format, &bind);
if (format) {
templ.format = format;
templ.bind = bind & ~PIPE_BIND_SHARED;
if (drawable->stvis.samples > 1) {
templ.nr_samples = drawable->stvis.samples;
templ.nr_storage_samples = drawable->stvis.samples;
zsbuf = &drawable->msaa_textures[statt];
}
else {
templ.nr_samples = 0;
templ.nr_storage_samples = 0;
zsbuf = &drawable->textures[statt];
}
/* Try to reuse the resource.
* (the other resource parameters should be constant)
*/
if (!*zsbuf ||
(*zsbuf)->width0 != templ.width0 ||
(*zsbuf)->height0 != templ.height0) {
/* Allocate a new one. */
pipe_resource_reference(zsbuf, NULL);
*zsbuf = screen->base.screen->resource_create(screen->base.screen,
&templ);
assert(*zsbuf);
}
}
else {
pipe_resource_reference(&drawable->msaa_textures[statt], NULL);
pipe_resource_reference(&drawable->textures[statt], NULL);
}
}
/* For DRI2, we may get the same buffers again from the server.
* To prevent useless imports of gem names, drawable->old* is used
* to bypass the import if we get the same buffers. This doesn't apply
* to DRI3/Wayland, users of image.loader, since the buffer is managed
* by the client (no import), and the back buffer is going to change
* at every redraw.
*/
if (!image) {
drawable->old_num = num_buffers;
drawable->old_w = dri_drawable->w;
drawable->old_h = dri_drawable->h;
memcpy(drawable->old, buffers, sizeof(__DRIbuffer) * num_buffers);
}
}
static void
dri2_flush_frontbuffer(struct dri_context *ctx,
struct dri_drawable *drawable,
enum st_attachment_type statt)
{
__DRIdrawable *dri_drawable = drawable->dPriv;
const __DRIimageLoaderExtension *image = drawable->sPriv->image.loader;
const __DRIdri2LoaderExtension *loader = drawable->sPriv->dri2.loader;
struct pipe_context *pipe = ctx->st->pipe;
if (statt != ST_ATTACHMENT_FRONT_LEFT)
return;
if (drawable->stvis.samples > 1) {
/* Resolve the front buffer. */
dri_pipe_blit(ctx->st->pipe,
drawable->textures[ST_ATTACHMENT_FRONT_LEFT],
drawable->msaa_textures[ST_ATTACHMENT_FRONT_LEFT]);
}
if (drawable->textures[ST_ATTACHMENT_FRONT_LEFT]) {
pipe->flush_resource(pipe, drawable->textures[ST_ATTACHMENT_FRONT_LEFT]);
}
pipe->flush(pipe, NULL, 0);
if (image) {
image->flushFrontBuffer(dri_drawable, dri_drawable->loaderPrivate);
}
else if (loader->flushFrontBuffer) {
loader->flushFrontBuffer(dri_drawable, dri_drawable->loaderPrivate);
}
}
/**
* The struct dri_drawable flush_swapbuffers callback
*/
static void
dri2_flush_swapbuffers(struct dri_context *ctx,
struct dri_drawable *drawable)
{
__DRIdrawable *dri_drawable = drawable->dPriv;
const __DRIimageLoaderExtension *image = drawable->sPriv->image.loader;
if (image && image->base.version >= 3 && image->flushSwapBuffers) {
image->flushSwapBuffers(dri_drawable, dri_drawable->loaderPrivate);
}
}
static void
dri2_update_tex_buffer(struct dri_drawable *drawable,
struct dri_context *ctx,
struct pipe_resource *res)
{
/* no-op */
}
static const struct dri2_format_mapping r8_g8b8_mapping = {
DRM_FORMAT_NV12,
__DRI_IMAGE_FORMAT_NONE,
__DRI_IMAGE_COMPONENTS_Y_UV,
PIPE_FORMAT_R8_G8B8_420_UNORM,
2,
{ { 0, 0, 0, __DRI_IMAGE_FORMAT_R8, 1 },
{ 1, 1, 1, __DRI_IMAGE_FORMAT_GR88, 2 } }
};
static __DRIimage *
dri2_create_image_from_winsys(__DRIscreen *_screen,
int width, int height, const struct dri2_format_mapping *map,
int num_handles, struct winsys_handle *whandle,
bool is_protected_content,
void *loaderPrivate)
{
struct dri_screen *screen = dri_screen(_screen);
struct pipe_screen *pscreen = screen->base.screen;
__DRIimage *img;
struct pipe_resource templ;
unsigned tex_usage = 0;
int i;
bool use_lowered = false;
const unsigned format_planes = util_format_get_num_planes(map->pipe_format);
if (pscreen->is_format_supported(pscreen, map->pipe_format, screen->target, 0, 0,
PIPE_BIND_RENDER_TARGET))
tex_usage |= PIPE_BIND_RENDER_TARGET;
if (pscreen->is_format_supported(pscreen, map->pipe_format, screen->target, 0, 0,
PIPE_BIND_SAMPLER_VIEW))
tex_usage |= PIPE_BIND_SAMPLER_VIEW;
/* For NV12, see if we have support for sampling r8_b8g8 */
if (!tex_usage && map->pipe_format == PIPE_FORMAT_NV12 &&
pscreen->is_format_supported(pscreen, PIPE_FORMAT_R8_G8B8_420_UNORM,
screen->target, 0, 0, PIPE_BIND_SAMPLER_VIEW)) {
map = &r8_g8b8_mapping;
tex_usage |= PIPE_BIND_SAMPLER_VIEW;
}
if (!tex_usage && util_format_is_yuv(map->pipe_format)) {
/* YUV format sampling can be emulated by the GL gallium frontend by
* using multiple samplers of varying formats.
* If no tex_usage is set and we detect a YUV format,
* test for support of all planes' sampler formats and
* add sampler view usage.
*/
use_lowered = true;
if (dri2_yuv_dma_buf_supported(screen, map))
tex_usage |= PIPE_BIND_SAMPLER_VIEW;
}
if (!tex_usage)
return NULL;
img = CALLOC_STRUCT(__DRIimageRec);
if (!img)
return NULL;
memset(&templ, 0, sizeof(templ));
templ.bind = tex_usage;
templ.target = screen->target;
templ.last_level = 0;
templ.depth0 = 1;
templ.array_size = 1;
for (i = num_handles - 1; i >= format_planes; i--) {
struct pipe_resource *tex;
templ.next = img->texture;
tex = pscreen->resource_from_handle(pscreen, &templ, &whandle[i],
PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE);
if (!tex) {
pipe_resource_reference(&img->texture, NULL);
FREE(img);
return NULL;
}
img->texture = tex;
}
for (i = (use_lowered ? map->nplanes : format_planes) - 1; i >= 0; i--) {
struct pipe_resource *tex;
templ.next = img->texture;
templ.width0 = width >> map->planes[i].width_shift;
templ.height0 = height >> map->planes[i].height_shift;
if (use_lowered)
templ.format = dri2_get_pipe_format_for_dri_format(map->planes[i].dri_format);
else
templ.format = map->pipe_format;
assert(templ.format != PIPE_FORMAT_NONE);
tex = pscreen->resource_from_handle(pscreen,
&templ, &whandle[use_lowered ? map->planes[i].buffer_index : i],
PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE);
if (!tex) {
pipe_resource_reference(&img->texture, NULL);
FREE(img);
return NULL;
}
/* Reject image creation if there's an inconsistency between
* content protection status of tex and img.
*/
if ((tex->bind & PIPE_BIND_PROTECTED) != is_protected_content) {
pipe_resource_reference(&img->texture, NULL);
pipe_resource_reference(&tex, NULL);
FREE(img);
return NULL;
}
img->texture = tex;
}
img->level = 0;
img->layer = 0;
img->use = 0;
img->loader_private = loaderPrivate;
return img;
}
static __DRIimage *
dri2_create_image_from_name(__DRIscreen *_screen,
int width, int height, int format,
int name, int pitch, void *loaderPrivate)
{
const struct dri2_format_mapping *map = dri2_get_mapping_by_format(format);
struct winsys_handle whandle;
__DRIimage *img;
if (!map)
return NULL;
memset(&whandle, 0, sizeof(whandle));
whandle.type = WINSYS_HANDLE_TYPE_SHARED;
whandle.handle = name;
whandle.format = map->pipe_format;
whandle.modifier = DRM_FORMAT_MOD_INVALID;
whandle.stride = pitch * util_format_get_blocksize(map->pipe_format);
img = dri2_create_image_from_winsys(_screen, width, height, map,
1, &whandle, false, loaderPrivate);
if (!img)
return NULL;
img->dri_components = map->dri_components;
img->dri_fourcc = map->dri_fourcc;
img->dri_format = map->dri_format;
return img;
}
static unsigned
dri2_get_modifier_num_planes(uint64_t modifier, int fourcc)
{
const struct dri2_format_mapping *map = dri2_get_mapping_by_fourcc(fourcc);
if (!map)
return 0;
switch (modifier) {
case I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS:
case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS:
case I915_FORMAT_MOD_Y_TILED_CCS:
return 2 * util_format_get_num_planes(map->pipe_format);
case DRM_FORMAT_MOD_ARM_16X16_BLOCK_U_INTERLEAVED:
case DRM_FORMAT_MOD_ARM_AFBC(
AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
AFBC_FORMAT_MOD_SPARSE |
AFBC_FORMAT_MOD_YTR):
case DRM_FORMAT_MOD_ARM_AFBC(
AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
AFBC_FORMAT_MOD_SPARSE):
case DRM_FORMAT_MOD_BROADCOM_UIF:
case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED:
case DRM_FORMAT_MOD_LINEAR:
/* DRM_FORMAT_MOD_NONE is the same as LINEAR */
case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_EIGHT_GOB:
case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_FOUR_GOB:
case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_ONE_GOB:
case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_SIXTEEN_GOB:
case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB:
case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_TWO_GOB:
case DRM_FORMAT_MOD_QCOM_COMPRESSED:
case DRM_FORMAT_MOD_VIVANTE_SPLIT_SUPER_TILED:
case DRM_FORMAT_MOD_VIVANTE_SPLIT_TILED:
case DRM_FORMAT_MOD_VIVANTE_SUPER_TILED:
case DRM_FORMAT_MOD_VIVANTE_TILED:
/* FD_FORMAT_MOD_QCOM_TILED is not in drm_fourcc.h */
case I915_FORMAT_MOD_X_TILED:
case I915_FORMAT_MOD_Y_TILED:
case DRM_FORMAT_MOD_INVALID:
return util_format_get_num_planes(map->pipe_format);
default:
return 0;
}
}
static __DRIimage *
dri2_create_image_from_fd(__DRIscreen *_screen,
int width, int height, int fourcc,
uint64_t modifier, int *fds, int num_fds,
int *strides, int *offsets, bool protected_content,
unsigned *error, void *loaderPrivate)
{
struct winsys_handle whandles[4];
const struct dri2_format_mapping *map = dri2_get_mapping_by_fourcc(fourcc);
__DRIimage *img = NULL;
unsigned err = __DRI_IMAGE_ERROR_SUCCESS;
int i;
const int expected_num_fds = dri2_get_modifier_num_planes(modifier, fourcc);
if (!map || expected_num_fds == 0) {
err = __DRI_IMAGE_ERROR_BAD_MATCH;
goto exit;
}
if (num_fds != expected_num_fds) {
err = __DRI_IMAGE_ERROR_BAD_MATCH;
goto exit;
}
memset(whandles, 0, sizeof(whandles));
for (i = 0; i < num_fds; i++) {
if (fds[i] < 0) {
err = __DRI_IMAGE_ERROR_BAD_ALLOC;
goto exit;
}
whandles[i].type = WINSYS_HANDLE_TYPE_FD;
whandles[i].handle = (unsigned)fds[i];
whandles[i].stride = (unsigned)strides[i];
whandles[i].offset = (unsigned)offsets[i];
whandles[i].format = map->pipe_format;
whandles[i].modifier = modifier;
whandles[i].plane = i;
}
img = dri2_create_image_from_winsys(_screen, width, height, map,
num_fds, whandles, protected_content,
loaderPrivate);
if(img == NULL) {
err = __DRI_IMAGE_ERROR_BAD_ALLOC;
goto exit;
}
img->dri_components = map->dri_components;
img->dri_fourcc = fourcc;
img->dri_format = map->dri_format;
img->imported_dmabuf = TRUE;
exit:
if (error)
*error = err;
return img;
}
static __DRIimage *
dri2_create_image_common(__DRIscreen *_screen,
int width, int height,
int format, unsigned int use,
const uint64_t *modifiers,
const unsigned count,
void *loaderPrivate)
{
const struct dri2_format_mapping *map = dri2_get_mapping_by_format(format);
struct dri_screen *screen = dri_screen(_screen);
struct pipe_screen *pscreen = screen->base.screen;
__DRIimage *img;
struct pipe_resource templ;
unsigned tex_usage = 0;
if (!map)
return NULL;
if (pscreen->is_format_supported(pscreen, map->pipe_format, screen->target,
0, 0, PIPE_BIND_RENDER_TARGET))
tex_usage |= PIPE_BIND_RENDER_TARGET;
if (pscreen->is_format_supported(pscreen, map->pipe_format, screen->target,
0, 0, PIPE_BIND_SAMPLER_VIEW))
tex_usage |= PIPE_BIND_SAMPLER_VIEW;
if (!tex_usage)
return NULL;
if (use & __DRI_IMAGE_USE_SCANOUT)
tex_usage |= PIPE_BIND_SCANOUT;
if (use & __DRI_IMAGE_USE_SHARE)
tex_usage |= PIPE_BIND_SHARED;
if (use & __DRI_IMAGE_USE_LINEAR)
tex_usage |= PIPE_BIND_LINEAR;
if (use & __DRI_IMAGE_USE_CURSOR) {
if (width != 64 || height != 64)
return NULL;
tex_usage |= PIPE_BIND_CURSOR;
}
if (use & __DRI_IMAGE_USE_PROTECTED)
tex_usage |= PIPE_BIND_PROTECTED;
img = CALLOC_STRUCT(__DRIimageRec);
if (!img)
return NULL;
memset(&templ, 0, sizeof(templ));
templ.bind = tex_usage;
templ.format = map->pipe_format;
templ.target = PIPE_TEXTURE_2D;
templ.last_level = 0;
templ.width0 = width;
templ.height0 = height;
templ.depth0 = 1;
templ.array_size = 1;
if (modifiers)
img->texture =
screen->base.screen
->resource_create_with_modifiers(screen->base.screen,
&templ,
modifiers,
count);
else
img->texture =
screen->base.screen->resource_create(screen->base.screen, &templ);
if (!img->texture) {
FREE(img);
return NULL;
}
img->level = 0;
img->layer = 0;
img->dri_format = format;
img->dri_fourcc = map->dri_fourcc;
img->dri_components = 0;
img->use = use;
img->loader_private = loaderPrivate;
return img;
}
static __DRIimage *
dri2_create_image(__DRIscreen *_screen,
int width, int height, int format,
unsigned int use, void *loaderPrivate)
{
return dri2_create_image_common(_screen, width, height, format, use,
NULL /* modifiers */, 0 /* count */,
loaderPrivate);
}
static __DRIimage *
dri2_create_image_with_modifiers(__DRIscreen *dri_screen,
int width, int height, int format,
const uint64_t *modifiers,
const unsigned count,
void *loaderPrivate)
{
return dri2_create_image_common(dri_screen, width, height, format,
__DRI_IMAGE_USE_SHARE, modifiers, count,
loaderPrivate);
}
static bool
dri2_query_image_common(__DRIimage *image, int attrib, int *value)
{
switch (attrib) {
case __DRI_IMAGE_ATTRIB_FORMAT:
*value = image->dri_format;
return true;
case __DRI_IMAGE_ATTRIB_WIDTH:
*value = image->texture->width0;
return true;
case __DRI_IMAGE_ATTRIB_HEIGHT:
*value = image->texture->height0;
return true;
case __DRI_IMAGE_ATTRIB_COMPONENTS:
if (image->dri_components == 0)
return false;
*value = image->dri_components;
return true;
case __DRI_IMAGE_ATTRIB_FOURCC:
if (image->dri_fourcc) {
*value = image->dri_fourcc;
} else {
const struct dri2_format_mapping *map;
map = dri2_get_mapping_by_format(image->dri_format);
if (!map)
return false;
*value = map->dri_fourcc;
}
return true;
default:
return false;
}
}
static bool
dri2_query_image_by_resource_handle(__DRIimage *image, int attrib, int *value)
{
struct pipe_screen *pscreen = image->texture->screen;
struct winsys_handle whandle;
struct pipe_resource *tex;
unsigned usage;
memset(&whandle, 0, sizeof(whandle));
whandle.plane = image->plane;
int i;
switch (attrib) {
case __DRI_IMAGE_ATTRIB_STRIDE:
case __DRI_IMAGE_ATTRIB_OFFSET:
case __DRI_IMAGE_ATTRIB_HANDLE:
whandle.type = WINSYS_HANDLE_TYPE_KMS;
break;
case __DRI_IMAGE_ATTRIB_NAME:
whandle.type = WINSYS_HANDLE_TYPE_SHARED;
break;
case __DRI_IMAGE_ATTRIB_FD:
whandle.type = WINSYS_HANDLE_TYPE_FD;
break;
case __DRI_IMAGE_ATTRIB_NUM_PLANES:
for (i = 0, tex = image->texture; tex; tex = tex->next)
i++;
*value = i;
return true;
case __DRI_IMAGE_ATTRIB_MODIFIER_UPPER:
case __DRI_IMAGE_ATTRIB_MODIFIER_LOWER:
whandle.type = WINSYS_HANDLE_TYPE_KMS;
whandle.modifier = DRM_FORMAT_MOD_INVALID;
break;
default:
return false;
}
usage = PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE;
if (image->use & __DRI_IMAGE_USE_BACKBUFFER)
usage |= PIPE_HANDLE_USAGE_EXPLICIT_FLUSH;
if (!pscreen->resource_get_handle(pscreen, NULL, image->texture,
&whandle, usage))
return false;
switch (attrib) {
case __DRI_IMAGE_ATTRIB_STRIDE:
*value = whandle.stride;
return true;
case __DRI_IMAGE_ATTRIB_OFFSET:
*value = whandle.offset;
return true;
case __DRI_IMAGE_ATTRIB_HANDLE:
case __DRI_IMAGE_ATTRIB_NAME:
case __DRI_IMAGE_ATTRIB_FD:
*value = whandle.handle;
return true;
case __DRI_IMAGE_ATTRIB_MODIFIER_UPPER:
if (whandle.modifier == DRM_FORMAT_MOD_INVALID)
return false;
*value = (whandle.modifier >> 32) & 0xffffffff;
return true;
case __DRI_IMAGE_ATTRIB_MODIFIER_LOWER:
if (whandle.modifier == DRM_FORMAT_MOD_INVALID)
return false;
*value = whandle.modifier & 0xffffffff;
return true;
default:
return false;
}
}
static bool
dri2_resource_get_param(__DRIimage *image, enum pipe_resource_param param,
unsigned handle_usage, uint64_t *value)
{
struct pipe_screen *pscreen = image->texture->screen;
if (!pscreen->resource_get_param)
return false;
return pscreen->resource_get_param(pscreen, NULL, image->texture,
image->plane, 0, param, handle_usage,
value);
}
static bool
dri2_query_image_by_resource_param(__DRIimage *image, int attrib, int *value)
{
enum pipe_resource_param param;
uint64_t res_param;
unsigned handle_usage;
if (!image->texture->screen->resource_get_param)
return false;
switch (attrib) {
case __DRI_IMAGE_ATTRIB_STRIDE:
param = PIPE_RESOURCE_PARAM_STRIDE;
break;
case __DRI_IMAGE_ATTRIB_OFFSET:
param = PIPE_RESOURCE_PARAM_OFFSET;
break;
case __DRI_IMAGE_ATTRIB_NUM_PLANES:
param = PIPE_RESOURCE_PARAM_NPLANES;
break;
case __DRI_IMAGE_ATTRIB_MODIFIER_UPPER:
case __DRI_IMAGE_ATTRIB_MODIFIER_LOWER:
param = PIPE_RESOURCE_PARAM_MODIFIER;
break;
case __DRI_IMAGE_ATTRIB_HANDLE:
param = PIPE_RESOURCE_PARAM_HANDLE_TYPE_KMS;
break;
case __DRI_IMAGE_ATTRIB_NAME:
param = PIPE_RESOURCE_PARAM_HANDLE_TYPE_SHARED;
break;
case __DRI_IMAGE_ATTRIB_FD:
param = PIPE_RESOURCE_PARAM_HANDLE_TYPE_FD;
break;
default:
return false;
}
handle_usage = PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE;
if (image->use & __DRI_IMAGE_USE_BACKBUFFER)
handle_usage |= PIPE_HANDLE_USAGE_EXPLICIT_FLUSH;
if (!dri2_resource_get_param(image, param, handle_usage, &res_param))
return false;
switch (attrib) {
case __DRI_IMAGE_ATTRIB_STRIDE:
case __DRI_IMAGE_ATTRIB_OFFSET:
case __DRI_IMAGE_ATTRIB_NUM_PLANES:
if (res_param > INT_MAX)
return false;
*value = (int)res_param;
return true;
case __DRI_IMAGE_ATTRIB_HANDLE:
case __DRI_IMAGE_ATTRIB_NAME:
case __DRI_IMAGE_ATTRIB_FD:
if (res_param > UINT_MAX)
return false;
*value = (int)res_param;
return true;
case __DRI_IMAGE_ATTRIB_MODIFIER_UPPER:
if (res_param == DRM_FORMAT_MOD_INVALID)
return false;
*value = (res_param >> 32) & 0xffffffff;
return true;
case __DRI_IMAGE_ATTRIB_MODIFIER_LOWER:
if (res_param == DRM_FORMAT_MOD_INVALID)
return false;
*value = res_param & 0xffffffff;
return true;
default:
return false;
}
}
static GLboolean
dri2_query_image(__DRIimage *image, int attrib, int *value)
{
if (dri2_query_image_common(image, attrib, value))
return GL_TRUE;
else if (dri2_query_image_by_resource_param(image, attrib, value))
return GL_TRUE;
else if (dri2_query_image_by_resource_handle(image, attrib, value))
return GL_TRUE;
else
return GL_FALSE;
}
static __DRIimage *
dri2_dup_image(__DRIimage *image, void *loaderPrivate)
{
__DRIimage *img;
img = CALLOC_STRUCT(__DRIimageRec);
if (!img)
return NULL;
img->texture = NULL;
pipe_resource_reference(&img->texture, image->texture);
img->level = image->level;
img->layer = image->layer;
img->dri_format = image->dri_format;
/* This should be 0 for sub images, but dup is also used for base images. */
img->dri_components = image->dri_components;
img->loader_private = loaderPrivate;
return img;
}
static GLboolean
dri2_validate_usage(__DRIimage *image, unsigned int use)
{
if (!image || !image->texture)
return false;
struct pipe_screen *screen = image->texture->screen;
if (!screen->check_resource_capability)
return true;
/* We don't want to check these:
* __DRI_IMAGE_USE_SHARE (all images are shareable)
* __DRI_IMAGE_USE_BACKBUFFER (all images support this)
*/
unsigned bind = 0;
if (use & __DRI_IMAGE_USE_SCANOUT)
bind |= PIPE_BIND_SCANOUT;
if (use & __DRI_IMAGE_USE_LINEAR)
bind |= PIPE_BIND_LINEAR;
if (use & __DRI_IMAGE_USE_CURSOR)
bind |= PIPE_BIND_CURSOR;
if (!bind)
return true;
return screen->check_resource_capability(screen, image->texture, bind);
}
static __DRIimage *
dri2_from_names(__DRIscreen *screen, int width, int height, int format,
int *names, int num_names, int *strides, int *offsets,
void *loaderPrivate)
{
const struct dri2_format_mapping *map = dri2_get_mapping_by_format(format);
__DRIimage *img;
struct winsys_handle whandle;
if (!map)
return NULL;
if (num_names != 1)
return NULL;
memset(&whandle, 0, sizeof(whandle));
whandle.type = WINSYS_HANDLE_TYPE_SHARED;
whandle.handle = names[0];
whandle.stride = strides[0];
whandle.offset = offsets[0];
whandle.format = map->pipe_format;
whandle.modifier = DRM_FORMAT_MOD_INVALID;
img = dri2_create_image_from_winsys(screen, width, height, map,
1, &whandle, false, loaderPrivate);
if (img == NULL)
return NULL;
img->dri_components = map->dri_components;
img->dri_fourcc = map->dri_fourcc;
img->dri_format = map->pipe_format;
return img;
}
static __DRIimage *
dri2_from_planar(__DRIimage *image, int plane, void *loaderPrivate)
{
__DRIimage *img;
struct pipe_resource *tex = image->texture;
int i;
if (plane < 0) {
return NULL;
} else if (plane > 0) {
uint64_t planes;
if (dri2_resource_get_param(image, PIPE_RESOURCE_PARAM_NPLANES, 0,
&planes) &&
plane >= planes) {
return NULL;
}
}
if (tex->next) {
for (i = 0; i < plane; i++) {
tex = tex->next;
if (!tex)
return NULL;
}
}
if (image->dri_components == 0) {
uint64_t modifier;
if (!dri2_resource_get_param(image, PIPE_RESOURCE_PARAM_MODIFIER, 0,
&modifier) ||
modifier == DRM_FORMAT_MOD_INVALID) {
return NULL;
}
}
img = dri2_dup_image(image, loaderPrivate);
if (img == NULL)
return NULL;
pipe_resource_reference(&img->texture, tex);
if (img->texture->screen->resource_changed)
img->texture->screen->resource_changed(img->texture->screen,
img->texture);
/* set this to 0 for sub images. */
img->dri_components = 0;
img->plane = plane;
return img;
}
static __DRIimage *
dri2_from_fds(__DRIscreen *screen, int width, int height, int fourcc,
int *fds, int num_fds, int *strides, int *offsets,
void *loaderPrivate)
{
return dri2_create_image_from_fd(screen, width, height, fourcc,
DRM_FORMAT_MOD_INVALID, fds, num_fds,
strides, offsets, false, NULL, loaderPrivate);
}
static boolean
dri2_query_dma_buf_modifiers(__DRIscreen *_screen, int fourcc, int max,
uint64_t *modifiers, unsigned int *external_only,
int *count)
{
struct dri_screen *screen = dri_screen(_screen);
struct pipe_screen *pscreen = screen->base.screen;
const struct dri2_format_mapping *map = dri2_get_mapping_by_fourcc(fourcc);
enum pipe_format format;
if (!map)
return false;
format = map->pipe_format;
if (pscreen->is_format_supported(pscreen, format, screen->target, 0, 0,
PIPE_BIND_RENDER_TARGET) ||
pscreen->is_format_supported(pscreen, format, screen->target, 0, 0,
PIPE_BIND_SAMPLER_VIEW) ||
dri2_yuv_dma_buf_supported(screen, map)) {
if (pscreen->query_dmabuf_modifiers != NULL)
pscreen->query_dmabuf_modifiers(pscreen, format, max, modifiers,
external_only, count);
else
*count = 0;
return true;
}
return false;
}
static boolean
dri2_query_dma_buf_format_modifier_attribs(__DRIscreen *_screen,
uint32_t fourcc, uint64_t modifier,
int attrib, uint64_t *value)
{
struct dri_screen *screen = dri_screen(_screen);
struct pipe_screen *pscreen = screen->base.screen;
if (!pscreen->query_dmabuf_modifiers)
return false;
switch (attrib) {
case __DRI_IMAGE_FORMAT_MODIFIER_ATTRIB_PLANE_COUNT: {
uint64_t mod_planes = dri2_get_modifier_num_planes(modifier, fourcc);
if (mod_planes > 0)
*value = mod_planes;
return mod_planes > 0;
}
default:
return false;
}
}
static __DRIimage *
dri2_from_dma_bufs(__DRIscreen *screen,
int width, int height, int fourcc,
int *fds, int num_fds,
int *strides, int *offsets,
enum __DRIYUVColorSpace yuv_color_space,
enum __DRISampleRange sample_range,
enum __DRIChromaSiting horizontal_siting,
enum __DRIChromaSiting vertical_siting,
unsigned *error,
void *loaderPrivate)
{
__DRIimage *img;
img = dri2_create_image_from_fd(screen, width, height, fourcc,
DRM_FORMAT_MOD_INVALID, fds, num_fds,
strides, offsets, false, error, loaderPrivate);
if (img == NULL)
return NULL;
img->yuv_color_space = yuv_color_space;
img->sample_range = sample_range;
img->horizontal_siting = horizontal_siting;
img->vertical_siting = vertical_siting;
*error = __DRI_IMAGE_ERROR_SUCCESS;
return img;
}
static __DRIimage *
dri2_from_dma_bufs2(__DRIscreen *screen,
int width, int height, int fourcc,
uint64_t modifier, int *fds, int num_fds,
int *strides, int *offsets,
enum __DRIYUVColorSpace yuv_color_space,
enum __DRISampleRange sample_range,
enum __DRIChromaSiting horizontal_siting,
enum __DRIChromaSiting vertical_siting,
unsigned *error,
void *loaderPrivate)
{
__DRIimage *img;
img = dri2_create_image_from_fd(screen, width, height, fourcc,
modifier, fds, num_fds, strides, offsets,
false, error, loaderPrivate);
if (img == NULL)
return NULL;
img->yuv_color_space = yuv_color_space;
img->sample_range = sample_range;
img->horizontal_siting = horizontal_siting;
img->vertical_siting = vertical_siting;
*error = __DRI_IMAGE_ERROR_SUCCESS;
return img;
}
static __DRIimage *
dri2_from_dma_bufs3(__DRIscreen *screen,
int width, int height, int fourcc,
uint64_t modifier, int *fds, int num_fds,
int *strides, int *offsets,
enum __DRIYUVColorSpace yuv_color_space,
enum __DRISampleRange sample_range,
enum __DRIChromaSiting horizontal_siting,
enum __DRIChromaSiting vertical_siting,
uint32_t flags,
unsigned *error,
void *loaderPrivate)
{
__DRIimage *img;
img = dri2_create_image_from_fd(screen, width, height, fourcc,
modifier, fds, num_fds, strides, offsets,
flags & __DRI_IMAGE_PROTECTED_CONTENT_FLAG,
error, loaderPrivate);
if (img == NULL)
return NULL;
img->yuv_color_space = yuv_color_space;
img->sample_range = sample_range;
img->horizontal_siting = horizontal_siting;
img->vertical_siting = vertical_siting;
*error = __DRI_IMAGE_ERROR_SUCCESS;
return img;
}
static void
dri2_blit_image(__DRIcontext *context, __DRIimage *dst, __DRIimage *src,
int dstx0, int dsty0, int dstwidth, int dstheight,
int srcx0, int srcy0, int srcwidth, int srcheight,
int flush_flag)
{
struct dri_context *ctx = dri_context(context);
struct pipe_context *pipe = ctx->st->pipe;
struct pipe_screen *screen;
struct pipe_fence_handle *fence;
struct pipe_blit_info blit;
if (!dst || !src)
return;
memset(&blit, 0, sizeof(blit));
blit.dst.resource = dst->texture;
blit.dst.box.x = dstx0;
blit.dst.box.y = dsty0;
blit.dst.box.width = dstwidth;
blit.dst.box.height = dstheight;
blit.dst.box.depth = 1;
blit.dst.format = dst->texture->format;
blit.src.resource = src->texture;
blit.src.box.x = srcx0;
blit.src.box.y = srcy0;
blit.src.box.width = srcwidth;
blit.src.box.height = srcheight;
blit.src.box.depth = 1;
blit.src.format = src->texture->format;
blit.mask = PIPE_MASK_RGBA;
blit.filter = PIPE_TEX_FILTER_NEAREST;
pipe->blit(pipe, &blit);
if (flush_flag == __BLIT_FLAG_FLUSH) {
pipe->flush_resource(pipe, dst->texture);
ctx->st->flush(ctx->st, 0, NULL, NULL, NULL);
} else if (flush_flag == __BLIT_FLAG_FINISH) {
screen = dri_screen(ctx->sPriv)->base.screen;
pipe->flush_resource(pipe, dst->texture);
ctx->st->flush(ctx->st, 0, &fence, NULL, NULL);
(void) screen->fence_finish(screen, NULL, fence, PIPE_TIMEOUT_INFINITE);
screen->fence_reference(screen, &fence, NULL);
}
}
static void *
dri2_map_image(__DRIcontext *context, __DRIimage *image,
int x0, int y0, int width, int height,
unsigned int flags, int *stride, void **data)
{
struct dri_context *ctx = dri_context(context);
struct pipe_context *pipe = ctx->st->pipe;
enum pipe_map_flags pipe_access = 0;
struct pipe_transfer *trans;
void *map;
if (!image || !data || *data)
return NULL;
unsigned plane = image->plane;
if (plane >= dri2_get_mapping_by_format(image->dri_format)->nplanes)
return NULL;
struct pipe_resource *resource = image->texture;
while (plane--)
resource = resource->next;
if (flags & __DRI_IMAGE_TRANSFER_READ)
pipe_access |= PIPE_MAP_READ;
if (flags & __DRI_IMAGE_TRANSFER_WRITE)
pipe_access |= PIPE_MAP_WRITE;
map = pipe_transfer_map(pipe, resource, 0, 0, pipe_access, x0, y0,
width, height, &trans);
if (map) {
*data = trans;
*stride = trans->stride;
}
return map;
}
static void
dri2_unmap_image(__DRIcontext *context, __DRIimage *image, void *data)
{
struct dri_context *ctx = dri_context(context);
struct pipe_context *pipe = ctx->st->pipe;
pipe_transfer_unmap(pipe, (struct pipe_transfer *)data);
}
static int
dri2_get_capabilities(__DRIscreen *_screen)
{
struct dri_screen *screen = dri_screen(_screen);
return (screen->can_share_buffer ? __DRI_IMAGE_CAP_GLOBAL_NAMES : 0);
}
/* The extension is modified during runtime if DRI_PRIME is detected */
static __DRIimageExtension dri2ImageExtension = {
.base = { __DRI_IMAGE, 18 },
.createImageFromName = dri2_create_image_from_name,
.createImageFromRenderbuffer = dri2_create_image_from_renderbuffer,
.destroyImage = dri2_destroy_image,
.createImage = dri2_create_image,
.queryImage = dri2_query_image,
.dupImage = dri2_dup_image,
.validateUsage = dri2_validate_usage,
.createImageFromNames = dri2_from_names,
.fromPlanar = dri2_from_planar,
.createImageFromTexture = dri2_create_from_texture,
.createImageFromFds = NULL,
.createImageFromDmaBufs = NULL,
.blitImage = dri2_blit_image,
.getCapabilities = dri2_get_capabilities,
.mapImage = dri2_map_image,
.unmapImage = dri2_unmap_image,
.createImageWithModifiers = NULL,
.createImageFromDmaBufs2 = NULL,
.createImageFromDmaBufs3 = NULL,
.queryDmaBufFormats = NULL,
.queryDmaBufModifiers = NULL,
.queryDmaBufFormatModifierAttribs = NULL,
.createImageFromRenderbuffer2 = dri2_create_image_from_renderbuffer2,
};
static const __DRIrobustnessExtension dri2Robustness = {
.base = { __DRI2_ROBUSTNESS, 1 }
};
static int
dri2_interop_query_device_info(__DRIcontext *_ctx,
struct mesa_glinterop_device_info *out)
{
struct pipe_screen *screen = dri_context(_ctx)->st->pipe->screen;
/* There is no version 0, thus we do not support it */
if (out->version == 0)
return MESA_GLINTEROP_INVALID_VERSION;
out->pci_segment_group = screen->get_param(screen, PIPE_CAP_PCI_GROUP);
out->pci_bus = screen->get_param(screen, PIPE_CAP_PCI_BUS);
out->pci_device = screen->get_param(screen, PIPE_CAP_PCI_DEVICE);
out->pci_function = screen->get_param(screen, PIPE_CAP_PCI_FUNCTION);
out->vendor_id = screen->get_param(screen, PIPE_CAP_VENDOR_ID);
out->device_id = screen->get_param(screen, PIPE_CAP_DEVICE_ID);
/* Instruct the caller that we support up-to version one of the interface */
out->version = 1;
return MESA_GLINTEROP_SUCCESS;
}
static int
dri2_interop_export_object(__DRIcontext *_ctx,
struct mesa_glinterop_export_in *in,
struct mesa_glinterop_export_out *out)
{
struct st_context_iface *st = dri_context(_ctx)->st;
struct pipe_screen *screen = st->pipe->screen;
struct gl_context *ctx = ((struct st_context *)st)->ctx;
struct pipe_resource *res = NULL;
struct winsys_handle whandle;
unsigned target, usage;
boolean success;
/* There is no version 0, thus we do not support it */
if (in->version == 0 || out->version == 0)
return MESA_GLINTEROP_INVALID_VERSION;
/* Validate the target. */
switch (in->target) {
case GL_TEXTURE_BUFFER:
case GL_TEXTURE_1D:
case GL_TEXTURE_2D:
case GL_TEXTURE_3D:
case GL_TEXTURE_RECTANGLE:
case GL_TEXTURE_1D_ARRAY:
case GL_TEXTURE_2D_ARRAY:
case GL_TEXTURE_CUBE_MAP_ARRAY:
case GL_TEXTURE_CUBE_MAP:
case GL_TEXTURE_2D_MULTISAMPLE:
case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
case GL_TEXTURE_EXTERNAL_OES:
case GL_RENDERBUFFER:
case GL_ARRAY_BUFFER:
target = in->target;
break;
case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
target = GL_TEXTURE_CUBE_MAP;
break;
default:
return MESA_GLINTEROP_INVALID_TARGET;
}
/* Validate the simple case of miplevel. */
if ((target == GL_RENDERBUFFER || target == GL_ARRAY_BUFFER) &&
in->miplevel != 0)
return MESA_GLINTEROP_INVALID_MIP_LEVEL;
/* Validate the OpenGL object and get pipe_resource. */
simple_mtx_lock(&ctx->Shared->Mutex);
if (target == GL_ARRAY_BUFFER) {
/* Buffer objects.
*
* The error checking is based on the documentation of
* clCreateFromGLBuffer from OpenCL 2.0 SDK.
*/
struct gl_buffer_object *buf = _mesa_lookup_bufferobj(ctx, in->obj);
/* From OpenCL 2.0 SDK, clCreateFromGLBuffer:
* "CL_INVALID_GL_OBJECT if bufobj is not a GL buffer object or is
* a GL buffer object but does not have an existing data store or
* the size of the buffer is 0."
*/
if (!buf || buf->Size == 0) {
simple_mtx_unlock(&ctx->Shared->Mutex);
return MESA_GLINTEROP_INVALID_OBJECT;
}
res = st_buffer_object(buf)->buffer;
if (!res) {
/* this shouldn't happen */
simple_mtx_unlock(&ctx->Shared->Mutex);
return MESA_GLINTEROP_INVALID_OBJECT;
}
out->buf_offset = 0;
out->buf_size = buf->Size;
buf->UsageHistory |= USAGE_DISABLE_MINMAX_CACHE;
} else if (target == GL_RENDERBUFFER) {
/* Renderbuffers.
*
* The error checking is based on the documentation of
* clCreateFromGLRenderbuffer from OpenCL 2.0 SDK.
*/
struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, in->obj);
/* From OpenCL 2.0 SDK, clCreateFromGLRenderbuffer:
* "CL_INVALID_GL_OBJECT if renderbuffer is not a GL renderbuffer
* object or if the width or height of renderbuffer is zero."
*/
if (!rb || rb->Width == 0 || rb->Height == 0) {
simple_mtx_unlock(&ctx->Shared->Mutex);
return MESA_GLINTEROP_INVALID_OBJECT;
}
/* From OpenCL 2.0 SDK, clCreateFromGLRenderbuffer:
* "CL_INVALID_OPERATION if renderbuffer is a multi-sample GL
* renderbuffer object."
*/
if (rb->NumSamples > 1) {
simple_mtx_unlock(&ctx->Shared->Mutex);
return MESA_GLINTEROP_INVALID_OPERATION;
}
/* From OpenCL 2.0 SDK, clCreateFromGLRenderbuffer:
* "CL_OUT_OF_RESOURCES if there is a failure to allocate resources
* required by the OpenCL implementation on the device."
*/
res = st_renderbuffer(rb)->texture;
if (!res) {
simple_mtx_unlock(&ctx->Shared->Mutex);
return MESA_GLINTEROP_OUT_OF_RESOURCES;
}
out->internal_format = rb->InternalFormat;
out->view_minlevel = 0;
out->view_numlevels = 1;
out->view_minlayer = 0;
out->view_numlayers = 1;
} else {
/* Texture objects.
*
* The error checking is based on the documentation of
* clCreateFromGLTexture from OpenCL 2.0 SDK.
*/
struct gl_texture_object *obj = _mesa_lookup_texture(ctx, in->obj);
if (obj)
_mesa_test_texobj_completeness(ctx, obj);
/* From OpenCL 2.0 SDK, clCreateFromGLTexture:
* "CL_INVALID_GL_OBJECT if texture is not a GL texture object whose
* type matches texture_target, if the specified miplevel of texture
* is not defined, or if the width or height of the specified
* miplevel is zero or if the GL texture object is incomplete."
*/
if (!obj ||
obj->Target != target ||
!obj->_BaseComplete ||
(in->miplevel > 0 && !obj->_MipmapComplete)) {
simple_mtx_unlock(&ctx->Shared->Mutex);
return MESA_GLINTEROP_INVALID_OBJECT;
}
if (target == GL_TEXTURE_BUFFER) {
struct st_buffer_object *stBuf =
st_buffer_object(obj->BufferObject);
if (!stBuf || !stBuf->buffer) {
/* this shouldn't happen */
simple_mtx_unlock(&ctx->Shared->Mutex);
return MESA_GLINTEROP_INVALID_OBJECT;
}
res = stBuf->buffer;
out->internal_format = obj->BufferObjectFormat;
out->buf_offset = obj->BufferOffset;
out->buf_size = obj->BufferSize == -1 ? obj->BufferObject->Size :
obj->BufferSize;
obj->BufferObject->UsageHistory |= USAGE_DISABLE_MINMAX_CACHE;
} else {
/* From OpenCL 2.0 SDK, clCreateFromGLTexture:
* "CL_INVALID_MIP_LEVEL if miplevel is less than the value of
* levelbase (for OpenGL implementations) or zero (for OpenGL ES
* implementations); or greater than the value of q (for both OpenGL
* and OpenGL ES). levelbase and q are defined for the texture in
* section 3.8.10 (Texture Completeness) of the OpenGL 2.1
* specification and section 3.7.10 of the OpenGL ES 2.0."
*/
if (in->miplevel < obj->BaseLevel || in->miplevel > obj->_MaxLevel) {
simple_mtx_unlock(&ctx->Shared->Mutex);
return MESA_GLINTEROP_INVALID_MIP_LEVEL;
}
if (!st_finalize_texture(ctx, st->pipe, obj, 0)) {
simple_mtx_unlock(&ctx->Shared->Mutex);
return MESA_GLINTEROP_OUT_OF_RESOURCES;
}
res = st_get_texobj_resource(obj);
if (!res) {
/* Incomplete texture buffer object? This shouldn't really occur. */
simple_mtx_unlock(&ctx->Shared->Mutex);
return MESA_GLINTEROP_INVALID_OBJECT;
}
out->internal_format = obj->Image[0][0]->InternalFormat;
out->view_minlevel = obj->MinLevel;
out->view_numlevels = obj->NumLevels;
out->view_minlayer = obj->MinLayer;
out->view_numlayers = obj->NumLayers;
}
}
/* Get the handle. */
switch (in->access) {
case MESA_GLINTEROP_ACCESS_READ_ONLY:
usage = 0;
break;
case MESA_GLINTEROP_ACCESS_READ_WRITE:
case MESA_GLINTEROP_ACCESS_WRITE_ONLY:
usage = PIPE_HANDLE_USAGE_SHADER_WRITE;
break;
default:
usage = 0;
}
memset(&whandle, 0, sizeof(whandle));
whandle.type = WINSYS_HANDLE_TYPE_FD;
success = screen->resource_get_handle(screen, st->pipe, res, &whandle,
usage);
simple_mtx_unlock(&ctx->Shared->Mutex);
if (!success)
return MESA_GLINTEROP_OUT_OF_HOST_MEMORY;
out->dmabuf_fd = whandle.handle;
out->out_driver_data_written = 0;
if (res->target == PIPE_BUFFER)
out->buf_offset += whandle.offset;
/* Instruct the caller that we support up-to version one of the interface */
in->version = 1;
out->version = 1;
return MESA_GLINTEROP_SUCCESS;
}
static const __DRI2interopExtension dri2InteropExtension = {
.base = { __DRI2_INTEROP, 1 },
.query_device_info = dri2_interop_query_device_info,
.export_object = dri2_interop_export_object
};
/**
* \brief the DRI2bufferDamageExtension set_damage_region method
*/
static void
dri2_set_damage_region(__DRIdrawable *dPriv, unsigned int nrects, int *rects)
{
struct dri_drawable *drawable = dri_drawable(dPriv);
struct pipe_box *boxes = NULL;
if (nrects) {
boxes = CALLOC(nrects, sizeof(*boxes));
assert(boxes);
for (unsigned int i = 0; i < nrects; i++) {
int *rect = &rects[i * 4];
u_box_2d(rect[0], rect[1], rect[2], rect[3], &boxes[i]);
}
}
FREE(drawable->damage_rects);
drawable->damage_rects = boxes;
drawable->num_damage_rects = nrects;
/* Only apply the damage region if the BACK_LEFT texture is up-to-date. */
if (drawable->texture_stamp == drawable->dPriv->lastStamp &&
(drawable->texture_mask & (1 << ST_ATTACHMENT_BACK_LEFT))) {
struct pipe_screen *screen = drawable->screen->base.screen;
struct pipe_resource *resource;
if (drawable->stvis.samples > 1)
resource = drawable->msaa_textures[ST_ATTACHMENT_BACK_LEFT];
else
resource = drawable->textures[ST_ATTACHMENT_BACK_LEFT];
screen->set_damage_region(screen, resource,
drawable->num_damage_rects,
drawable->damage_rects);
}
}
static __DRI2bufferDamageExtension dri2BufferDamageExtension = {
.base = { __DRI2_BUFFER_DAMAGE, 1 },
};
/**
* \brief the DRI2ConfigQueryExtension configQueryb method
*/
static int
dri2GalliumConfigQueryb(__DRIscreen *sPriv, const char *var,
unsigned char *val)
{
struct dri_screen *screen = dri_screen(sPriv);
if (!driCheckOption(&screen->dev->option_cache, var, DRI_BOOL))
return dri2ConfigQueryExtension.configQueryb(sPriv, var, val);
*val = driQueryOptionb(&screen->dev->option_cache, var);
return 0;
}
/**
* \brief the DRI2ConfigQueryExtension configQueryi method
*/
static int
dri2GalliumConfigQueryi(__DRIscreen *sPriv, const char *var, int *val)
{
struct dri_screen *screen = dri_screen(sPriv);
if (!driCheckOption(&screen->dev->option_cache, var, DRI_INT) &&
!driCheckOption(&screen->dev->option_cache, var, DRI_ENUM))
return dri2ConfigQueryExtension.configQueryi(sPriv, var, val);
*val = driQueryOptioni(&screen->dev->option_cache, var);
return 0;
}
/**
* \brief the DRI2ConfigQueryExtension configQueryf method
*/
static int
dri2GalliumConfigQueryf(__DRIscreen *sPriv, const char *var, float *val)
{
struct dri_screen *screen = dri_screen(sPriv);
if (!driCheckOption(&screen->dev->option_cache, var, DRI_FLOAT))
return dri2ConfigQueryExtension.configQueryf(sPriv, var, val);
*val = driQueryOptionf(&screen->dev->option_cache, var);
return 0;
}
/**
* \brief the DRI2ConfigQueryExtension configQuerys method
*/
static int
dri2GalliumConfigQuerys(__DRIscreen *sPriv, const char *var, char **val)
{
struct dri_screen *screen = dri_screen(sPriv);
if (!driCheckOption(&screen->dev->option_cache, var, DRI_STRING))
return dri2ConfigQueryExtension.configQuerys(sPriv, var, val);
*val = driQueryOptionstr(&screen->dev->option_cache, var);
return 0;
}
/**
* \brief the DRI2ConfigQueryExtension struct.
*
* We first query the driver option cache. Then the dri2 option cache.
*/
static const __DRI2configQueryExtension dri2GalliumConfigQueryExtension = {
.base = { __DRI2_CONFIG_QUERY, 2 },
.configQueryb = dri2GalliumConfigQueryb,
.configQueryi = dri2GalliumConfigQueryi,
.configQueryf = dri2GalliumConfigQueryf,
.configQuerys = dri2GalliumConfigQuerys,
};
/**
* \brief the DRI2blobExtension set_cache_funcs method
*/
static void
set_blob_cache_funcs(__DRIscreen *sPriv, __DRIblobCacheSet set,
__DRIblobCacheGet get)
{
struct dri_screen *screen = dri_screen(sPriv);
struct pipe_screen *pscreen = screen->base.screen;
if (!pscreen->get_disk_shader_cache)
return;
struct disk_cache *cache = pscreen->get_disk_shader_cache(pscreen);
if (!cache)
return;
disk_cache_set_callbacks(cache, set, get);
}
static const __DRI2blobExtension driBlobExtension = {
.base = { __DRI2_BLOB, 1 },
.set_cache_funcs = set_blob_cache_funcs
};
/*
* Backend function init_screen.
*/
static const __DRIextension *dri_screen_extensions[] = {
&driTexBufferExtension.base,
&dri2FlushExtension.base,
&dri2ImageExtension.base,
&dri2RendererQueryExtension.base,
&dri2GalliumConfigQueryExtension.base,
&dri2ThrottleExtension.base,
&dri2FenceExtension.base,
&dri2BufferDamageExtension.base,
&dri2InteropExtension.base,
&dri2NoErrorExtension.base,
&driBlobExtension.base,
NULL
};
static const __DRIextension *dri_robust_screen_extensions[] = {
&driTexBufferExtension.base,
&dri2FlushExtension.base,
&dri2ImageExtension.base,
&dri2RendererQueryExtension.base,
&dri2GalliumConfigQueryExtension.base,
&dri2ThrottleExtension.base,
&dri2FenceExtension.base,
&dri2InteropExtension.base,
&dri2BufferDamageExtension.base,
&dri2Robustness.base,
&dri2NoErrorExtension.base,
&driBlobExtension.base,
NULL
};
/**
* This is the driver specific part of the createNewScreen entry point.
*
* Returns the struct gl_config supported by this driver.
*/
static const __DRIconfig **
dri2_init_screen(__DRIscreen * sPriv)
{
const __DRIconfig **configs;
struct dri_screen *screen;
struct pipe_screen *pscreen = NULL;
screen = CALLOC_STRUCT(dri_screen);
if (!screen)
return NULL;
screen->sPriv = sPriv;
screen->fd = sPriv->fd;
(void) mtx_init(&screen->opencl_func_mutex, mtx_plain);
sPriv->driverPrivate = (void *)screen;
if (pipe_loader_drm_probe_fd(&screen->dev, screen->fd)) {
dri_init_options(screen);
pscreen = pipe_loader_create_screen(screen->dev);
}
if (!pscreen)
goto release_pipe;
screen->throttle = pscreen->get_param(pscreen, PIPE_CAP_THROTTLE);
if (pscreen->resource_create_with_modifiers)
dri2ImageExtension.createImageWithModifiers =
dri2_create_image_with_modifiers;
if (pscreen->get_param(pscreen, PIPE_CAP_DMABUF)) {
uint64_t cap;
if (drmGetCap(sPriv->fd, DRM_CAP_PRIME, &cap) == 0 &&
(cap & DRM_PRIME_CAP_IMPORT)) {
dri2ImageExtension.createImageFromFds = dri2_from_fds;
dri2ImageExtension.createImageFromDmaBufs = dri2_from_dma_bufs;
dri2ImageExtension.createImageFromDmaBufs2 = dri2_from_dma_bufs2;
dri2ImageExtension.createImageFromDmaBufs3 = dri2_from_dma_bufs3;
dri2ImageExtension.queryDmaBufFormats = dri2_query_dma_buf_formats;
dri2ImageExtension.queryDmaBufModifiers =
dri2_query_dma_buf_modifiers;
dri2ImageExtension.queryDmaBufFormatModifierAttribs =
dri2_query_dma_buf_format_modifier_attribs;
}
}
if (pscreen->set_damage_region)
dri2BufferDamageExtension.set_damage_region = dri2_set_damage_region;
if (pscreen->get_param(pscreen, PIPE_CAP_DEVICE_RESET_STATUS_QUERY)) {
sPriv->extensions = dri_robust_screen_extensions;
screen->has_reset_status_query = true;
}
else
sPriv->extensions = dri_screen_extensions;
configs = dri_init_screen_helper(screen, pscreen);
if (!configs)
goto destroy_screen;
screen->can_share_buffer = true;
screen->auto_fake_front = dri_with_format(sPriv);
screen->broken_invalidate = !sPriv->dri2.useInvalidate;
screen->lookup_egl_image = dri2_lookup_egl_image;
return configs;
destroy_screen:
dri_destroy_screen_helper(screen);
release_pipe:
if (screen->dev)
pipe_loader_release(&screen->dev, 1);
FREE(screen);
return NULL;
}
/**
* This is the driver specific part of the createNewScreen entry point.
*
* Returns the struct gl_config supported by this driver.
*/
static const __DRIconfig **
dri_kms_init_screen(__DRIscreen * sPriv)
{
#if defined(GALLIUM_SOFTPIPE)
const __DRIconfig **configs;
struct dri_screen *screen;
struct pipe_screen *pscreen = NULL;
uint64_t cap;
screen = CALLOC_STRUCT(dri_screen);
if (!screen)
return NULL;
screen->sPriv = sPriv;
screen->fd = sPriv->fd;
sPriv->driverPrivate = (void *)screen;
if (pipe_loader_sw_probe_kms(&screen->dev, screen->fd)) {
dri_init_options(screen);
pscreen = pipe_loader_create_screen(screen->dev);
}
if (!pscreen)
goto release_pipe;
if (pscreen->resource_create_with_modifiers)
dri2ImageExtension.createImageWithModifiers =
dri2_create_image_with_modifiers;
if (drmGetCap(sPriv->fd, DRM_CAP_PRIME, &cap) == 0 &&
(cap & DRM_PRIME_CAP_IMPORT)) {
dri2ImageExtension.createImageFromFds = dri2_from_fds;
dri2ImageExtension.createImageFromDmaBufs = dri2_from_dma_bufs;
dri2ImageExtension.createImageFromDmaBufs2 = dri2_from_dma_bufs2;
dri2ImageExtension.createImageFromDmaBufs3 = dri2_from_dma_bufs3;
dri2ImageExtension.queryDmaBufFormats = dri2_query_dma_buf_formats;
dri2ImageExtension.queryDmaBufModifiers = dri2_query_dma_buf_modifiers;
}
sPriv->extensions = dri_screen_extensions;
configs = dri_init_screen_helper(screen, pscreen);
if (!configs)
goto destroy_screen;
screen->can_share_buffer = false;
screen->auto_fake_front = dri_with_format(sPriv);
screen->broken_invalidate = !sPriv->dri2.useInvalidate;
screen->lookup_egl_image = dri2_lookup_egl_image;
return configs;
destroy_screen:
dri_destroy_screen_helper(screen);
release_pipe:
if (screen->dev)
pipe_loader_release(&screen->dev, 1);
FREE(screen);
#endif // GALLIUM_SOFTPIPE
return NULL;
}
static boolean
dri2_create_buffer(__DRIscreen * sPriv,
__DRIdrawable * dPriv,
const struct gl_config * visual, boolean isPixmap)
{
struct dri_drawable *drawable = NULL;
if (!dri_create_buffer(sPriv, dPriv, visual, isPixmap))
return FALSE;
drawable = dPriv->driverPrivate;
drawable->allocate_textures = dri2_allocate_textures;
drawable->flush_frontbuffer = dri2_flush_frontbuffer;
drawable->update_tex_buffer = dri2_update_tex_buffer;
drawable->flush_swapbuffers = dri2_flush_swapbuffers;
return TRUE;
}
/**
* DRI driver virtual function table.
*
* DRI versions differ in their implementation of init_screen and swap_buffers.
*/
const struct __DriverAPIRec galliumdrm_driver_api = {
.InitScreen = dri2_init_screen,
.DestroyScreen = dri_destroy_screen,
.CreateContext = dri_create_context,
.DestroyContext = dri_destroy_context,
.CreateBuffer = dri2_create_buffer,
.DestroyBuffer = dri_destroy_buffer,
.MakeCurrent = dri_make_current,
.UnbindContext = dri_unbind_context,
.AllocateBuffer = dri2_allocate_buffer,
.ReleaseBuffer = dri2_release_buffer,
};
/**
* DRI driver virtual function table.
*
* KMS/DRM version of the DriverAPI above sporting a different InitScreen
* hook. The latter is used to explicitly initialise the kms_swrast driver
* rather than selecting the approapriate driver as suggested by the loader.
*/
const struct __DriverAPIRec dri_kms_driver_api = {
.InitScreen = dri_kms_init_screen,
.DestroyScreen = dri_destroy_screen,
.CreateContext = dri_create_context,
.DestroyContext = dri_destroy_context,
.CreateBuffer = dri2_create_buffer,
.DestroyBuffer = dri_destroy_buffer,
.MakeCurrent = dri_make_current,
.UnbindContext = dri_unbind_context,
.AllocateBuffer = dri2_allocate_buffer,
.ReleaseBuffer = dri2_release_buffer,
};
/* This is the table of extensions that the loader will dlsym() for. */
const __DRIextension *galliumdrm_driver_extensions[] = {
&driCoreExtension.base,
&driImageDriverExtension.base,
&driDRI2Extension.base,
&gallium_config_options.base,
NULL
};
/* vim: set sw=3 ts=8 sts=3 expandtab: */