GLES Texture
概念
GLES的Texture有几个概念:
- texture unit,应用显卡中的硬件单元,用于对纹理进入采样和写入
- texture name, 一个整数,代码一个context内的唯一的纹理。用glGenTextures获得。
- texture object, 与name一一对应的。用glBindTexture获得
- texture image, 一个texture object中可以有多个image,一个image 代码2D纹理的一个mipmap level,或是Cube Map纹理中一个面的一个mipmap level。
texture unit
texture unit通过glActiveTexture激活,成为当前上下文的默认texture:
/**
* Sampler-related subset of a texture unit, like current texture objects.
*/
struct gl_texture_unit
{
GLfloat LodBias; /**< for biasing mipmap levels */
/** Texture targets that have a non-default texture bound */
GLbitfield _BoundTextures;
/** Current sampler object (GL_ARB_sampler_objects) */
struct gl_sampler_object *Sampler;
/** Current texture object pointers */
struct gl_texture_object *CurrentTex[NUM_TEXTURE_TARGETS];
/** Points to highest priority, complete and enabled texture object */
struct gl_texture_object *_Current;
};
struct gl_texture_unit Unit[MAX_COMBINED_TEXTURE_IMAGE_UNITS];
struct gl_fixedfunc_texture_unit FixedFuncUnit[MAX_TEXTURE_COORD_UNITS];
一个上下文中有MAX_COMBINED_TEXTURE_IMAGE_UNITS个texture_unit。这是一个显卡常量。
texture name
入口在_mesa_GenTextures:
first = _mesa_HashFindFreeKeyBlock(ctx->Shared->TexObjects, n);
/* Allocate new, empty texture objects */
for (i = 0; i < n; i++) {
struct gl_texture_object *texObj;
GLuint name = first + i;
texObj = ctx->Driver.NewTextureObject(ctx, name, target);
if (!texObj) {
_mesa_HashUnlockMutex(ctx->Shared->TexObjects);
_mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", caller);
return;
}
/* insert into hash table */
_mesa_HashInsertLocked(ctx->Shared->TexObjects, texObj->Name, texObj);
textures[i] = name;
}
分配一个数字,然后为这个数字分配了一个空的gl_texture_object。这个mesa的实现。 在swiftshader的实现中,仅分配了一个数字。
texture object
入口在_mesa_BindTexture:
在gl_texture_unit中有几个gl_texture_object指针,在glBindTexture调用中,一次调用,就是为其中一个指针赋值。 绑定的过程中会对旧的texture_object减少引用计数,对新的增加引用计数。旧的如果引用计数为0就会被释放。
/* If the refcount on the previously bound texture is decremented to
* zero, it'll be deleted here.
*/
_mesa_reference_texobj(&texUnit->CurrentTex[targetIndex], texObj);
texture_object中有一个二维数组,第一维是faces, 第二维的mipmap levels,存的是gl_texture_image指针。
/**
* Texture object state. Contains the array of mipmap images, border color,
* wrap modes, filter modes, and shadow/texcompare state.
*/
struct gl_texture_object
{
simple_mtx_t Mutex; /**< for thread safety */
GLint RefCount; /**< reference count */
GLuint Name; /**< the user-visible texture object ID */
GLenum16 Target; /**< GL_TEXTURE_1D, GL_TEXTURE_2D, etc. */
GLenum16 DepthMode; /**< GL_ARB_depth_texture */
GLchar *Label; /**< GL_KHR_debug */
struct gl_sampler_object Sampler;
gl_texture_index TargetIndex; /**< The gl_texture_unit::CurrentTex index.
...
GLboolean Immutable; /**< GL_ARB_texture_storage */
...
/** Actual texture images, indexed by [cube face] and [mipmap level] */
struct gl_texture_image *Image[MAX_FACES][MAX_TEXTURE_LEVELS];
};
texture image
入口在_mesa_TexImage2D或_mesa_TexStorage2D:
前者分配一个image,放在某target的某个mipmap level上。并且可以填充或不填充数据。 后者分配一组image,直接把某target的所有mipmap level弄完整。后者创建是immutable image。 immutable image的意义不是纹理不可写,而是这个纹理不可以resize和替换backing store。
/**
* Texture image state. Drivers will typically create a subclass of this
* with extra fields for memory buffers, etc.
*/
struct gl_texture_image
{
GLint InternalFormat; /**< Internal format as given by the user */
GLenum16 _BaseFormat; /**< Either GL_RGB, GL_RGBA, GL_ALPHA,
* GL_LUMINANCE, GL_LUMINANCE_ALPHA,
* GL_INTENSITY, GL_DEPTH_COMPONENT or
* GL_DEPTH_STENCIL_EXT only. Used for
* choosing TexEnv arithmetic.
*/
mesa_format TexFormat; /**< The actual texture memory format */
GLuint Border; /**< 0 or 1 */
GLuint Width; /**< = 2^WidthLog2 + 2*Border */
GLuint Height; /**< = 2^HeightLog2 + 2*Border */
GLuint Depth; /**< = 2^DepthLog2 + 2*Border */
GLuint Width2; /**< = Width - 2*Border */
GLuint Height2; /**< = Height - 2*Border */
GLuint Depth2; /**< = Depth - 2*Border */
GLuint WidthLog2; /**< = log2(Width2) */
GLuint HeightLog2; /**< = log2(Height2) */
GLuint DepthLog2; /**< = log2(Depth2) */
GLuint MaxNumLevels; /**< = maximum possible number of mipmap
levels, computed from the dimensions */
struct gl_texture_object *TexObject; /**< Pointer back to parent object */
GLuint Level; /**< Which mipmap level am I? */
/** Cube map face: index into gl_texture_object::Image[] array */
GLuint Face;
/** GL_ARB_texture_multisample */
GLuint NumSamples; /**< Sample count, or 0 for non-multisample */
GLboolean FixedSampleLocations; /**< Same sample locations for all pixels? */
};
mutable image
_mesa_TexImage2D分配的是mutable image,比较关键的gl_texture_image与gl_texture_object对应的代码在这里:
/**
* Like _mesa_select_tex_image() but if the image doesn't exist, allocate
* it and install it. Only return NULL if passed a bad parameter or run
* out of memory.
*/
struct gl_texture_image *
_mesa_get_tex_image(struct gl_context *ctx, struct gl_texture_object *texObj,
GLenum target, GLint level)
{
struct gl_texture_image *texImage;
if (!texObj)
return NULL;
texImage = _mesa_select_tex_image(texObj, target, level);
if (!texImage) {
texImage = ctx->Driver.NewTextureImage(ctx);
if (!texImage) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "texture image allocation");
return NULL;
}
set_tex_image(texObj, target, level, texImage);
}
return texImage;
}
struct gl_texture_image *
_mesa_select_tex_image(const struct gl_texture_object *texObj,
GLenum target, GLint level)
{
const GLuint face = _mesa_tex_target_to_face(target);
return texObj->Image[face][level];
}
static void
set_tex_image(struct gl_texture_object *tObj,
GLenum target, GLint level,
struct gl_texture_image *texImage)
{
const GLuint face = _mesa_tex_target_to_face(target);
tObj->Image[face][level] = texImage;
/* Set the 'back' pointer */
texImage->TexObject = tObj;
texImage->Level = level;
texImage->Face = face;
}
分配了gl_texture_image,并且关联给gl_texture_object某face某level之后,调用vendor driver的TexImage来分配内存:
static void
st_TexImage(struct gl_context * ctx, GLuint dims,
struct gl_texture_image *texImage,
GLenum format, GLenum type, const void *pixels,
const struct gl_pixelstore_attrib *unpack)
{
assert(dims == 1 || dims == 2 || dims == 3);
prep_teximage(ctx, texImage, format, type);
if (texImage->Width == 0 || texImage->Height == 0 || texImage->Depth == 0)
return;
/* allocate storage for texture data */
if (!ctx->Driver.AllocTextureImageBuffer(ctx, texImage)) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage%uD", dims);
return;
}
st_TexSubImage(ctx, dims, texImage, 0, 0, 0,
texImage->Width, texImage->Height, texImage->Depth,
format, type, pixels, unpack);
}
/**
* Called via ctx->Driver.AllocTextureImageBuffer().
* If the texture object/buffer already has space for the indicated image,
* we're done. Otherwise, allocate memory for the new texture image.
*/
static GLboolean
st_AllocTextureImageBuffer(struct gl_context *ctx,
struct gl_texture_image *texImage)
{
struct st_context *st = st_context(ctx);
struct st_texture_image *stImage = st_texture_image(texImage);
struct st_texture_object *stObj = st_texture_object(texImage->TexObject);
...
if (stObj->pt &&
st_texture_match_image(st, stObj->pt, texImage)) {
/* The image will live in the object's mipmap memory */
pipe_resource_reference(&stImage->pt, stObj->pt);
assert(stImage->pt);
return GL_TRUE;
}
else {
...
stImage->pt = st_texture_create(st,
gl_target_to_pipe(stObj->base.Target),
format,
0, /* lastLevel */
ptWidth,
ptHeight,
ptDepth,
ptLayers, 0,
bindings);
return stImage->pt != NULL;
}
}
st_texture_image
st_texture_create返回的是pipe_resource指针,填入了st_texture_image的pt成员里。pipe_resource就是代码buffer或image的backing store。
/**
* Subclass of gl_texure_image.
*/
struct st_texture_image
{
struct gl_texture_image base;
/* If stImage->pt != NULL, image data is stored here.
* Else there is no image data.
*/
struct pipe_resource *pt;
...
};
pipe_resource
st_texture_create函数主要调用screen->resource_create来创建pipe_resource的一个子类, pipe_resource代表一个buffer或image,它只描述,不具体真实的backing store,真实的backing store在子类里:
/**
* Allocate a new pipe_resource object
* width0, height0, depth0 are the dimensions of the level 0 image
* (the highest resolution). last_level indicates how many mipmap levels
* to allocate storage for. For non-mipmapped textures, this will be zero.
*/
struct pipe_resource *
st_texture_create(struct st_context *st,
enum pipe_texture_target target,
enum pipe_format format,
GLuint last_level,
GLuint width0,
GLuint height0,
GLuint depth0,
GLuint layers,
GLuint nr_samples,
GLuint bind)
{
struct pipe_resource pt, *newtex;
struct pipe_screen *screen = st->pipe->screen;
...
memset(&pt, 0, sizeof(pt));
pt.target = target;
pt.format = format;
pt.last_level = last_level;
pt.width0 = width0;
pt.height0 = height0;
pt.depth0 = depth0;
pt.array_size = layers;
pt.usage = PIPE_USAGE_DEFAULT;
pt.bind = bind;
/* only set this for OpenGL textures, not renderbuffers */
pt.flags = PIPE_RESOURCE_FLAG_TEXTURING_MORE_LIKELY;
pt.nr_samples = nr_samples;
pt.nr_storage_samples = nr_samples;
newtex = screen->resource_create(screen, &pt); //返回回子类
return newtex;
}
fd resource
screen->resource_create在adreno中就是fd_resource_create函数, 这个函数经过一系列调用,最后通过创建了一个pipe_resource的子类对象,fd_resource:
struct fd_resource {
struct pipe_resource base;
struct fd_bo *bo;
enum pipe_format internal_format;
struct fdl_layout layout;
...
/* Sequence # incremented each time bo changes: */
uint16_t seqno;
...
bool lrz_valid : 1;
enum fd_lrz_direction lrz_direction : 2;
uint16_t lrz_width; // for lrz clear, does this differ from lrz_pitch?
uint16_t lrz_height;
uint16_t lrz_pitch;
struct fd_bo *lrz;
};
fd_resource的bo指向的就是一个buffer object,这个bo的创建也不直观,代码里是先设置fd_resource的各种属性,然后调用realloc_bo来分配空间的:
static void
realloc_bo(struct fd_resource *rsc, uint32_t size)
{
...
rsc->bo = fd_bo_new(screen->dev, size, flags, "%ux%ux%u@%u:%x",
prsc->width0, prsc->height0, prsc->depth0, rsc->layout.cpp, prsc->bind);
...
/* Zero out the UBWC area on allocation. This fixes intermittent failures
* with UBWC, which I suspect are due to the HW having a hard time
* interpreting arbitrary values populating the flags buffer when the BO
* was recycled through the bo cache (instead of fresh allocations from
* the kernel, which are zeroed). sleep(1) in this spot didn't work
* around the issue, but any memset value seems to.
*/
if (rsc->layout.ubwc) {
void *buf = fd_bo_map(rsc->bo);
}
...
rsc->seqno = p_atomic_inc_return(&screen->rsc_seqno);
}
fd_bo
struct fd_bo {
struct fd_device *dev;
uint32_t size;
uint32_t handle;
uint32_t name;
int32_t refcnt;
uint32_t flags; /* flags like FD_RELOC_DUMP to use for relocs to this BO */
uint64_t iova;
void *map;
const struct fd_bo_funcs *funcs;
enum {
NO_CACHE = 0,
BO_CACHE = 1,
RING_CACHE = 2,
} bo_reuse;
struct list_head list; /* bucket-list entry */
time_t free_time; /* time when added to bucket-list */
};
bo就是buffer object,它的创建分两步,一是申请显存并得到句柄,二是获取iova填充数据结构, 申请显存调的是drm接口:
/* allocate a buffer handle: */
int msm_bo_new_handle(struct fd_device *dev,
uint32_t size, uint32_t flags, uint32_t *handle)
{
struct drm_msm_gem_new req = {
.size = size,
.flags = MSM_BO_WC, // TODO figure out proper flags..
};
...
ret = drmCommandWriteRead(dev->fd, DRM_MSM_GEM_NEW,
&req, sizeof(req));
...
*handle = req.handle;
return 0;
}
/* allocate a new buffer object, call w/ table_lock held */
static struct fd_bo * bo_from_handle(struct fd_device *dev,
uint32_t size, uint32_t handle)
{
auto bo = dev->funcs->bo_from_handle(dev, size, handle); // msm_bo_from_handle
if (!bo) {
drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
return NULL;
}
bo->size = size;
bo->handle = handle;
bo->iova = bo->funcs->iova(bo);
/* add ourself into the handle table: */
_mesa_hash_table_insert(dev->handle_table, &bo->handle, bo);
return bo;
}
immutable image
immutable image的分配是在st_AllocTextureStorage(其他显卡可以重写这个函数),它只分配一个image, 给所有的faces和level用。 分配到的是pipe_resource,存在st_texture_object的pt指针下面, st_texture_object是gl_texture_object的子类:
/**
* Subclass of gl_texure_object.
*/
struct st_texture_object
{
struct gl_texture_object base; /* The "parent" object */
...
struct pipe_resource *pt;
...
};
void st_texture_storage()
{
...
stObj->pt = st_texture_create(st,
gl_target_to_pipe(texObj->Target),
fmt,
levels - 1,
ptWidth,
ptHeight,
ptDepth,
ptLayers, num_samples,
bindings);
...
}
st_texture_create之后的实现参考上一节,都是一样的。
eglImage vs gl_texture_image
在Android中经常需要渲染到AHardwareBuffer。其中的原理是什么呢?
用法
做法一
// Try creating a 32x32 AHardwareBuffer and attaching it to a multiview
// framebuffer, with various formats and depths.
AHardwareBuffer_Desc desc = {};
desc.width = 32;
desc.height = 32;
desc.usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
const int layers[] = {2, 4};
const int formats[] = {
AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM,
AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
// Do not test AHARDWAREBUFFER_FORMAT_BLOB, it isn't color-renderable.
};
const int samples[] = {1, 2, 4};
for (int nsamples : samples) {
for (auto nlayers : layers) {
for (auto format : formats) {
desc.layers = nlayers;
desc.format = format;
testEglImageArray(env, desc, nsamples);
}
}
}
static void testEglImageArray(JNIEnv* env, AHardwareBuffer_Desc desc,
int nsamples) {
AHardwareBuffer* hwbuffer = nullptr;
int error = AHardwareBuffer_allocate(&desc, &hwbuffer);
// Create EGLClientBuffer from the AHardwareBuffer.
EGLClientBuffer native_buffer = eglGetNativeClientBufferANDROID(hwbuffer);
// Create EGLImage from EGLClientBuffer.
EGLint attrs[] = {EGL_NONE};
EGLImageKHR image =
eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID, native_buffer, attrs);
// Create OpenGL texture from the EGLImage.
GLuint texid;
glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D_ARRAY, texid);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D_ARRAY, image);
// Create FBO and add multiview attachment.
GLuint fboid;
glGenFramebuffers(1, &fboid);
glBindFramebuffer(GL_FRAMEBUFFER, fboid);
const GLint miplevel = 0;
const GLint base_view = 0;
const GLint num_views = desc.layers;
if (nsamples == 1) {
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
texid, miplevel, base_view, num_views);
} else {
glFramebufferTextureMultisampleMultiviewOVR(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texid, miplevel, nsamples,
base_view, num_views);
}
glCheckFramebufferStatus(GL_FRAMEBUFFER);
//do some render
glDeleteTextures(1, &texid);
glDeleteFramebuffers(1, &fboid);
AHardwareBuffer_release(hwbuffer);
做法二
也可以通过eglCreateNativeClientBufferANDROID获取EGLClientBuffer.
EGLint attrs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT,10,
EGL_RED_SIZE,8,
EGL_GREEN_SIZE,8,
EGL_BLUE_SIZE 8,
EGL_ALPHA_SIZE,8,
EGL_NATIVE_BUFFER_USAGE_ANDROID,EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID,
EGL_NONE };
EGLClientBuffer native_buffer = eglCreateNativeClientBufferANDROID(attrs);
原理
在前面的章节我们知道texture的一个image,在底层的backing store是一个通过drm获取的一个buffer object。
那么eglImage与gl_texture_image是不是一一对应的关系呢?
eglCreateImageKHR的实现
src/egl/drivers/dri2/platform_android.c:1343: .create_image = droid_create_image_khr,
具体到native buffer的实现在dri2_create_image_android_native_buffer
:
static _EGLImage *
dri2_create_image_android_native_buffer(_EGLDisplay *disp,
_EGLContext *ctx,
struct ANativeWindowBuffer *buf)
{
if (ctx != NULL) {
/* From the EGL_ANDROID_image_native_buffer spec:
*
* * If <target> is EGL_NATIVE_BUFFER_ANDROID and <ctx> is not
* EGL_NO_CONTEXT, the error EGL_BAD_CONTEXT is generated.
*/
_eglError(EGL_BAD_CONTEXT, "eglCreateEGLImageKHR: for "
"EGL_NATIVE_BUFFER_ANDROID, the context must be "
"EGL_NO_CONTEXT");
return NULL;
}
if (!buf || buf->common.magic != ANDROID_NATIVE_BUFFER_MAGIC ||
buf->common.version != sizeof(*buf)) {
_eglError(EGL_BAD_PARAMETER, "eglCreateEGLImageKHR");
return NULL;
}
__DRIimage *dri_image =
droid_create_image_from_native_buffer(disp, buf, buf);
#ifdef HAVE_DRM_GRALLOC
if (dri_image == NULL)
dri_image = droid_create_image_from_name(disp, buf, buf);
#endif
if (dri_image) {
#if ANDROID_API_LEVEL >= 26
AHardwareBuffer_acquire(ANativeWindowBuffer_getHardwareBuffer(buf));
#endif
return dri2_create_image_from_dri(disp, dri_image);
}
return NULL;
}
从ahardwarebuffer创建dri_image最终调到了这里:
static __DRIimage *
droid_create_image_from_buffer_info(struct dri2_egl_display *dri2_dpy,
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ struct buffer_info *buf_info,
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ void *priv)
{
unsigned error;
if (dri2_dpy->image->base.version >= 15 &&
¦ dri2_dpy->image->createImageFromDmaBufs2 != NULL) {
¦ return dri2_dpy->image->createImageFromDmaBufs2(
¦ ¦dri2_dpy->dri_screen, buf_info->width, buf_info->height,
¦ ¦buf_info->drm_fourcc, buf_info->modifier, buf_info->fds,
¦ ¦buf_info->num_planes, buf_info->pitches, buf_info->offsets,
¦ ¦buf_info->yuv_color_space, buf_info->sample_range,
¦ ¦buf_info->horizontal_siting, buf_info->vertical_siting, &error,
¦ ¦priv);
}
return dri2_dpy->image->createImageFromDmaBufs(
¦ dri2_dpy->dri_screen, buf_info->width, buf_info->height,
¦ buf_info->drm_fourcc, buf_info->fds, buf_info->num_planes,
¦ buf_info->pitches, buf_info->offsets, buf_info->yuv_color_space,
¦ buf_info->sample_range, buf_info->horizontal_siting,
¦ buf_info->vertical_siting, &error, priv);
}
两个函数指针的具体实现分别为:dri2_from_dma_bufs
和 dri2_from_dma_bufs2
,这两个函数都是调用dri2_create_image_from_fd
: 这个函数从fd创建了一个DRIImage,fd是放在了一个pipe_resource里面(与gl_texture_image同)。DRIImage的数据结构(alias of DRIImageRec):
struct __DRIimageRec {
struct pipe_resource *texture; // 这里是一个链表,对ahardwarebuffer可能带来的多个fd,用pipe_resource->next串起来。
unsigned level;
unsigned layer;
uint32_t dri_format;
uint32_t dri_fourcc;
uint32_t dri_components;
unsigned use;
unsigned plane;
void *loader_private;
boolean imported_dmabuf;
/**
¦* Provided by EGL_EXT_image_dma_buf_import.
¦*/
enum __DRIYUVColorSpace yuv_color_space;
enum __DRISampleRange sample_range;
enum __DRIChromaSiting horizontal_siting;
enum __DRIChromaSiting vertical_siting;
/* DRI loader screen */
__DRIscreen *sPriv;
};
DRIImage的texture是通过对每个fd调用tex = pscreen->resource_from_handle并prepend实现的,resource_from_handle针对高通的实现是 fd_resource_from_handle
, 实际上创建的是pipe_resource的子类fd_resource。
从这里可以看到eglImage和gl_texture_object或gl_texture_image都不一样,它代表一串,几个pipe_resource。
glEGLImageTargetTexture2DOES
它的实现是__mesa_EGLImageTargetTexture2DOES, 该函数主要逻辑:
texImage = _mesa_get_tex_image(ctx, texObj, target, 0); //最后一个参数是level,就是只用texture的level 0
if (!texImage) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", caller);
} else {
st_FreeTextureImageBuffer(ctx, texImage); // 释放到gl_texture_image的backing store
texObj->External = GL_TRUE;
if (tex_storage) {
st_egl_image_target_tex_storage(ctx, target, texObj, texImage,
image);
} else {
st_egl_image_target_texture_2d(ctx, target, texObj, texImage,
image);
}
_mesa_dirty_texobj(ctx, texObj);
}
上面代码中调用的st_egl_image_target_tex_storage和st_egl_image_target_texture_2d的实现基本是相同的:
void
st_egl_image_target_tex_storage(struct gl_context *ctx, GLenum target,
¦ ¦ ¦ ¦ ¦ ¦ ¦ struct gl_texture_object *texObj,
¦ ¦ ¦ ¦ ¦ ¦ ¦ struct gl_texture_image *texImage,
¦ ¦ ¦ ¦ ¦ ¦ ¦ GLeglImageOES image_handle)
{
struct st_egl_image stimg;
bool native_supported;
if (!st_get_egl_image(ctx, image_handle, PIPE_BIND_SAMPLER_VIEW,
¦ ¦ ¦ ¦ ¦ ¦"glEGLImageTargetTexture2D", &stimg,
¦ ¦ ¦ ¦ ¦ ¦&native_supported))
¦ return;
st_bind_egl_image(ctx, texObj, texImage, &stimg, true, native_supported);
pipe_resource_reference(&stimg.texture, NULL);
}
函数的逻辑就是取到st_egl_image然后后gl_texture_object绑定,即调用st_bind_egl_iamge。 st_egl_image不是DRIImage的子类,但它们的pipe_resource *texture指向相同。
st_bind_egl_image主要做的就是把stimg->texture填到texObj->pt和texImage->pt上。
pipe_resource_reference(&texObj->pt, stimg->texture); //pipe_resource_reference实际是将第一个参数指向的内容释放,然后再第二个参数指针的内容赋值给第一个参数
st_texture_release_all_sampler_views(st, texObj);
pipe_resource_reference(&texImage->pt, texObj->pt);
经过这一步之后是这样的:
class gl_texture_object {
pipe_resource * pt;
gl_texture_image *Image[faces][levels];
}
class gl_texture_image {
}
class st_texture_image {
pipe_resource * pt;
}
st_texture_image "extends"--|> gl_texture_image
gl_texture_object "Image[0][0]" -> gl_texture_image
class st_egl_image {
pipe_resource * texture;
}
object fd_resource_linked_list {
pipe_resource * next;
}
Object texObj<<st_texture_object>>
texObj "pt"..> fd_resource_linked_list
Object texImg<<st_texture_image>>
texImg "pt"..> fd_resource_linked_list
Object stImg<<st_egl_image>>
stImg "texture"..> fd_resource_linked_list
示例代码
glActiveTexture(GL_TEXTURE0); //激活texture unit
unsigned int textureID;
glGenTextures(1, &textureID); // 分配一个名字,就是整数
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID); // 创建texture object
int width, height, nrChannels;
unsigned char *data;
for(unsigned int i = 0; i < textures_faces.size(); i++)
{
data = stbi_load(textures_faces[i].c_str(), &width, &height, &nrChannels, 0);
glTexImage2D(
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data
); // 创建mutable image并填充数据
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
DRM vs GBM
drm提供了一组ioctl来管理buffer的创建、释放、map, 送显等: https://gitlab.freedesktop.org/mesa/drm/-/blob/main/include/drm/drm.h
gbm是一个通用的buffer管理接口,api比较易用,它有多种backend,典型的就是drm。
drm api
https://gitlab.freedesktop.org/mesa/drm/-/blob/main/include/drm/drm.h
gbm api
/**
* \file gbmint.h
* \brief Internal implementation details of gbm
*/
/**
* The device used for the memory allocation.
*
* The members of this structure should be not accessed directly
*/
struct gbm_device {
/* Hack to make a gbm_device detectable by its first element. */
struct gbm_device *(*dummy)(int);
int fd;
const char *name;
unsigned int refcount;
struct stat stat;
void (*destroy)(struct gbm_device *gbm);
int (*is_format_supported)(struct gbm_device *gbm,
uint32_t format,
uint32_t usage);
int (*get_format_modifier_plane_count)(struct gbm_device *device,
uint32_t format,
uint64_t modifier);
struct gbm_bo *(*bo_create)(struct gbm_device *gbm,
uint32_t width, uint32_t height,
uint32_t format,
uint32_t usage,
const uint64_t *modifiers,
const unsigned int count);
struct gbm_bo *(*bo_import)(struct gbm_device *gbm, uint32_t type,
void *buffer, uint32_t usage);
void *(*bo_map)(struct gbm_bo *bo,
uint32_t x, uint32_t y,
uint32_t width, uint32_t height,
uint32_t flags, uint32_t *stride,
void **map_data);
void (*bo_unmap)(struct gbm_bo *bo, void *map_data);
int (*bo_write)(struct gbm_bo *bo, const void *buf, size_t data);
int (*bo_get_fd)(struct gbm_bo *bo);
int (*bo_get_planes)(struct gbm_bo *bo);
union gbm_bo_handle (*bo_get_handle)(struct gbm_bo *bo, int plane);
uint32_t (*bo_get_stride)(struct gbm_bo *bo, int plane);
uint32_t (*bo_get_offset)(struct gbm_bo *bo, int plane);
uint64_t (*bo_get_modifier)(struct gbm_bo *bo);
void (*bo_destroy)(struct gbm_bo *bo);
struct gbm_surface *(*surface_create)(struct gbm_device *gbm,
uint32_t width, uint32_t height,
uint32_t format, uint32_t flags,
const uint64_t *modifiers,
const unsigned count);
struct gbm_bo *(*surface_lock_front_buffer)(struct gbm_surface *surface);
void (*surface_release_buffer)(struct gbm_surface *surface,
struct gbm_bo *bo);
int (*surface_has_free_buffers)(struct gbm_surface *surface);
void (*surface_destroy)(struct gbm_surface *surface);
};
通过GBM离屏渲染
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES3/gl31.h>
#include <assert.h>
#include <fcntl.h>
#include <gbm.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
/* a dummy compute shader that does nothing */
#define COMPUTE_SHADER_SRC " \
#version 310 es\n \
\
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; \
\
void main(void) { \
/* awesome compute code here */ \
} \
"
int32_t
main (int32_t argc, char* argv[])
{
bool res;
int32_t fd = open ("/dev/dri/renderD128", O_RDWR);
assert (fd > 0);
struct gbm_device *gbm = gbm_create_device (fd);
assert (gbm != NULL);
/* setup EGL from the GBM device */
EGLDisplay egl_dpy = eglGetPlatformDisplay (EGL_PLATFORM_GBM_MESA, gbm, NULL);
assert (egl_dpy != NULL);
res = eglInitialize (egl_dpy, NULL, NULL);
assert (res);
const char *egl_extension_st = eglQueryString (egl_dpy, EGL_EXTENSIONS);
assert (strstr (egl_extension_st, "EGL_KHR_create_context") != NULL);
assert (strstr (egl_extension_st, "EGL_KHR_surfaceless_context") != NULL);
static const EGLint config_attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
EGL_NONE
};
EGLConfig cfg;
EGLint count;
res = eglChooseConfig (egl_dpy, config_attribs, &cfg, 1, &count);
assert (res);
res = eglBindAPI (EGL_OPENGL_ES_API);
assert (res);
static const EGLint attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
};
EGLContext core_ctx = eglCreateContext (egl_dpy,
cfg,
EGL_NO_CONTEXT,
attribs);
assert (core_ctx != EGL_NO_CONTEXT);
res = eglMakeCurrent (egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, core_ctx);
assert (res);
/* print some compute limits (not strictly necessary) */
GLint work_group_count[3] = {0};
for (unsigned i = 0; i < 3; i++)
glGetIntegeri_v (GL_MAX_COMPUTE_WORK_GROUP_COUNT,
i,
&work_group_count[i]);
printf ("GL_MAX_COMPUTE_WORK_GROUP_COUNT: %d, %d, %d\n",
work_group_count[0],
work_group_count[1],
work_group_count[2]);
GLint work_group_size[3] = {0};
for (unsigned i = 0; i < 3; i++)
glGetIntegeri_v (GL_MAX_COMPUTE_WORK_GROUP_SIZE, i, &work_group_size[i]);
printf ("GL_MAX_COMPUTE_WORK_GROUP_SIZE: %d, %d, %d\n",
work_group_size[0],
work_group_size[1],
work_group_size[2]);
GLint max_invocations;
glGetIntegerv (GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, &max_invocations);
printf ("GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS: %d\n", max_invocations);
GLint mem_size;
glGetIntegerv (GL_MAX_COMPUTE_SHARED_MEMORY_SIZE, &mem_size);
printf ("GL_MAX_COMPUTE_SHARED_MEMORY_SIZE: %d\n", mem_size);
/* setup a compute shader */
GLuint compute_shader = glCreateShader (GL_COMPUTE_SHADER);
assert (glGetError () == GL_NO_ERROR);
const char *shader_source = COMPUTE_SHADER_SRC;
glShaderSource (compute_shader, 1, &shader_source, NULL);
assert (glGetError () == GL_NO_ERROR);
glCompileShader (compute_shader);
assert (glGetError () == GL_NO_ERROR);
GLuint shader_program = glCreateProgram ();
glAttachShader (shader_program, compute_shader);
assert (glGetError () == GL_NO_ERROR);
glLinkProgram (shader_program);
assert (glGetError () == GL_NO_ERROR);
glDeleteShader (compute_shader);
glUseProgram (shader_program);
assert (glGetError () == GL_NO_ERROR);
/* dispatch computation */
glDispatchCompute (1, 1, 1);
assert (glGetError () == GL_NO_ERROR);
printf ("Compute shader dispatched and finished successfully\n");
/* free stuff */
glDeleteProgram (shader_program);
eglDestroyContext (egl_dpy, core_ctx);
eglTerminate (egl_dpy);
gbm_device_destroy (gbm);
close (fd);
return 0;
}