https://www.jianshu.com/p/29ab1b15cd2a
纹理与索引
一文,纹理的核心步骤如下:
1.glGenTextures 生成一个纹理句柄
2.glBindTexture 绑定纹理句柄
3.glTexParameteri 设置纹理参数
4.glTexImage2D 输入到像素数据到纹理
5.glActiveTexture 激活纹理
6.glDrawElements 绘制到屏幕
7.eglSwapBuffers 交换缓冲区,显示到屏幕
frameworks
/
native
/
opengl
/
libagl
/
texture.cpp
void glGenTextures(GLsizei n, GLuint *textures)
ogles_context_t* c = ogles_context_t::get();
if (n<0) {
ogles_error(c, GL_INVALID_VALUE);
return;
c->surfaceManager->getToken(n, textures);
先获取当前线程的上下文ogles_context_t。surfaceManager是指EGLSurfaceManager指针。EGLSurfaceManager继承于TokenManager,getToken是属于TokenManager。
frameworks/native/opengl/libagl/TokenManager.cpp
status_t TokenManager::getToken(GLsizei n, GLuint *tokens)
Mutex::Autolock _l(mLock);
for (GLsizei i=0 ; i<n ; i++)
*tokens++ = mTokenizer.acquire();
return NO_ERROR;
文件:/frameworks/native/opengl/libagl/Tokenizer.cpp
uint32_t Tokenizer::acquire()
if (!mRanges.size() || mRanges[0].first) {
_insertTokenAt(0,0);
return 0;
const run_t& run = mRanges[0];
uint32_t token = run.first + run.length;
_insertTokenAt(token, 1);
return token;
实际上这里的意思就是计算token的数值。这个token其实由mRanges的vector控制。mRanges的元素其实是run_t结构体,而token则是由run_t的first和length组成。
当第一次生成一个句柄时候,会在0位置插入0,length为1,返回0.不是第一次的时候,新的token为第0个的first+length。
frameworks/native/opengl/libagl/texture.cpp
void glBindTexture(GLenum target, GLuint texture)
ogles_context_t* c = ogles_context_t::get();
...
sp<EGLTextureObject> tex;
if (texture == 0) {
tex = c->textures.defaultTexture;
} else {
tex = c->surfaceManager->texture(texture);
if (ggl_unlikely(tex == 0)) {
tex = c->surfaceManager->createTexture(texture);
if (tex == 0) {
ogles_error(c, GL_OUT_OF_MEMORY);
return;
bindTextureTmu(c, c->textures.active, texture, tex);
EGLTextureObject指一个纹理对象。如果glBindTexture为0,则是一个默认纹理对象。因此当我们关闭一个纹理的时候,就要把glBindTexture的句柄设置为0.
否则EGLSurfaceManager调用texture获取该id的EGLTextureObject,如果找不到创建一个createTexture创建一个该id对应的纹理对象。
最后调用bindTextureTmu,把texture_state_t中的active,texture(纹理句柄),找到或者生成新的纹理对象进行处理。
文件:ameworks/native/opengl/libagl/TextureObjectManager.cpp
sp<EGLTextureObject> EGLSurfaceManager::texture(GLuint name)
Mutex::Autolock _l(mLock);
const ssize_t index = mTextures.indexOfKey(name);
if (index >= 0)
return mTextures.valueAt(index);
return 0;
mTextures是一个KeyedVector。你可以暂时是做一个SparseArray即可。里面保存了当前key对应的EGLTextureObject,找不到返回0.
sp<EGLTextureObject> EGLSurfaceManager::createTexture(GLuint name)
sp<EGLTextureObject> result;
Mutex::Autolock _l(mLock);
if (mTextures.indexOfKey(name) >= 0)
return result;
result = new EGLTextureObject();
status_t err = mTextures.add(name, result);
if (err < 0)
result.clear();
return result;
此时进行了EGLTextureObject初始化并且添加到mTextures。
system/core/libpixelflinger/pixelflinger.cpp
static void ggl_activeTexture(void* con, GGLuint tmu)
GGL_CONTEXT(c, con);
if (tmu >= GGLuint(GGL_TEXTURE_UNIT_COUNT)) {
ggl_error(c, GGL_INVALID_ENUM);
return;
c->activeTMUIndex = tmu;
c->activeTMU = &(c->state.texture[tmu]);
此时c是context_t,就是很简单的把context_t中activeTMUIndex设置为当前的纹理句柄,通过句柄找到对应的纹理对象,赋值到activeTMU。
这样context _t状态管理器就拿到了活跃的纹理对象。
frameworks/native/opengl/libagl/array.cpp
void glDrawElements(
GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)
ogles_context_t* c = ogles_context_t::get();
...
switch (mode) {
case GL_POINTS:
case GL_LINE_STRIP:
case GL_LINE_LOOP:
case GL_LINES:
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
case GL_TRIANGLES:
break;
default:
ogles_error(c, GL_INVALID_ENUM);
return;
switch (type) {
case GL_UNSIGNED_BYTE:
case GL_UNSIGNED_SHORT:
c->arrays.indicesType = type;
break;
default:
ogles_error(c, GL_INVALID_ENUM);
return;
if (count == 0 || !c->arrays.vertex.enable)
return;
if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK))
return;
c->vc.clear();
validate_arrays(c, mode);
if (c->arrays.element_array_buffer) {
indices = c->arrays.element_array_buffer->data + uintptr_t(indices);
const uint32_t enables = c->rasterizer.state.enables;
if (enables & GGL_ENABLE_TMUS)
ogles_lock_textures(c);
drawElementsPrims[mode](c, count, indices);
if (enables & GGL_ENABLE_TMUS)
ogles_unlock_textures(c);
#if VC_CACHE_STATISTICS
c->vc.total = count;
c->vc.dump_stats(mode);
#endif
这个方法的核心实际上是检测array_machine_t结构体,绘制顶点,绘制三角形,检查绑定在顶点数组对象的纹理。
frameworks/native/opengl/libagl/texture.cpp
static __attribute__((noinline))
void validate_tmu(ogles_context_t* c, int i)
texture_unit_t& u(c->textures.tmu[i]);
if (u.dirty) {
u.dirty = 0;
c->rasterizer.procs.activeTexture(c, i);
c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
c->rasterizer.procs.texGeni(c, GGL_S,
GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
c->rasterizer.procs.texGeni(c, GGL_T,
GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
GGL_TEXTURE_WRAP_S, u.texture->wraps);
c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
GGL_TEXTURE_WRAP_T, u.texture->wrapt);
c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
GGL_TEXTURE_MIN_FILTER, u.texture->min_filter);
c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter);
if (!u.texture->isComplete()) {
c->rasterizer.procs.disable(c, GGL_TEXTURE_2D);
在这个过程中会获取之前放在ogles_context_t.texture_state中的数据,全部拷贝到state_t.
system/core/libpixelflinger/scanline.cpp
void scanline(context_t* c)
const uint32_t enables = c->state.enables;
const int xs = c->iterators.xl;
const int x1 = c->iterators.xr;
int xc = x1 - xs;
const int16_t* covPtr = c->state.buffers.coverage + xs;
GGLcolor r, g, b, a;
iterators_t& ci = c->iterators;
...
GGLfixed z = (xs * c->shade.dzdx) + ci.ydzdy;
GGLfixed f = (xs * c->shade.dfdx) + ci.ydfdy;
struct {
GGLfixed s, t;
} tc[GGL_TEXTURE_UNIT_COUNT];
if (enables & GGL_ENABLE_TMUS) {
for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
if (c->state.texture[i].enable) {
texture_iterators_t& ti = c->state.texture[i].iterators;
if (enables & GGL_ENABLE_W) {
tc[i].s = ti.ydsdy;
tc[i].t = ti.ydtdy;
} else {
tc[i].s = (xs * ti.dsdx) + ti.ydsdy;
tc[i].t = (xs * ti.dtdx) + ti.ydtdy;
pixel_t fragment;
pixel_t texel;
pixel_t fb;
uint32_t x = xs;
uint32_t y = c->iterators.y;
while (xc--) {
{
fragment.s[1] = fragment.s[2] =
fragment.s[3] = fragment.s[0] = 8;
fragment.c[1] = r >> (GGL_COLOR_BITS-8);
fragment.c[2] = g >> (GGL_COLOR_BITS-8);
fragment.c[3] = b >> (GGL_COLOR_BITS-8);
fragment.c[0] = a >> (GGL_COLOR_BITS-8);
if (enables & GGL_ENABLE_TMUS) {
for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; ++i) {
texture_t& tx = c->state.texture[i];
if (!tx.enable)
continue;
texture_iterators_t& ti = tx.iterators;
int32_t u, v;
if (tx.s_coord != GGL_ONE_TO_ONE) {
const int w = tx.surface.width;
u = wrapping(tc[i].s, w, tx.s_wrap);
tc[i].s += ti.dsdx;
} else {
u = (((tx.shade.is0>>16) + x)<<16) + FIXED_HALF;
if (tx.t_coord != GGL_ONE_TO_ONE) {
const int h = tx.surface.height;
v = wrapping(tc[i].t, h, tx.t_wrap);
tc[i].t += ti.dtdx;
} else {
v = (((tx.shade.it0>>16) + y)<<16) + FIXED_HALF;
if (tx.mag_filter == GGL_NEAREST &&
tx.min_filter == GGL_NEAREST)
u >>= 16;
v >>= 16;
tx.surface.read(&tx.surface, c, u, v, &texel);
} else {
const int w = tx.surface.width;
const int h = tx.surface.height;
u -= FIXED_HALF;
v -= FIXED_HALF;
int u0 = u >> 16;
int v0 = v >> 16;
int u1 = u0 + 1;
int v1 = v0 + 1;
if (tx.s_wrap == GGL_REPEAT) {
if (u0<0) u0 += w;
if (u1<0) u1 += w;
if (u0>=w) u0 -= w;
if (u1>=w) u1 -= w;
} else {
if (u0<0) u0 = 0;
if (u1<0) u1 = 0;
if (u0>=w) u0 = w-1;
if (u1>=w) u1 = w-1;
if (tx.t_wrap == GGL_REPEAT) {
if (v0<0) v0 += h;
if (v1<0) v1 += h;
if (v0>=h) v0 -= h;
if (v1>=h) v1 -= h;
} else {
if (v0<0) v0 = 0;
if (v1<0) v1 = 0;
if (v0>=h) v0 = h-1;
if (v1>=h) v1 = h-1;
pixel_t texels[4];
uint32_t mm[4];
tx.surface.read(&tx.surface, c, u0, v0, &texels[0]);
tx.surface.read(&tx.surface, c, u0, v1, &texels[1]);
tx.surface.read(&tx.surface, c, u1, v0, &texels[2]);
tx.surface.read(&tx.surface, c, u1, v1, &texels[3]);
u = (u >> 12) & 0xF;
v = (v >> 12) & 0xF;
u += u>>3;
v += v>>3;
mm[0] = (0x10 - u) * (0x10 - v);
mm[1] = (0x10 - u) * v;
mm[2] = u * (0x10 - v);
mm[3] = 0x100 - (mm[0] + mm[1] + mm[2]);
for (int j=0 ; j<4 ; j++) {
texel.s[j] = texels[0].s[j];
if (!texel.s[j]) continue;
texel.s[j] += 8;
texel.c[j] = texels[0].c[j]*mm[0] +
texels[1].c[j]*mm[1] +
texels[2].c[j]*mm[2] +
texels[3].c[j]*mm[3] ;
for (int j=0 ; j<4 ; j++) {
uint32_t& Cf = fragment.c[j];
uint32_t& Ct = texel.c[j];
uint8_t& sf = fragment.s[j];
uint8_t& st = texel.s[j];
uint32_t At = texel.c[0];
uint8_t sat = texel.s[0];
switch (tx.env) {
case GGL_REPLACE:
if (st) {
Cf = Ct;
sf = st;
break;
case GGL_MODULATE:
if (st) {
uint32_t factor = Ct + (Ct>>(st-1));
Cf = (Cf * factor) >> st;
break;
case GGL_DECAL:
if (sat) {
rescale(Cf, sf, Ct, st);
Cf += ((Ct - Cf) * (At + (At>>(sat-1)))) >> sat;
break;
case GGL_BLEND:
if (st) {
uint32_t Cc = tx.env_color[i];
if (sf>8) Cc = (Cc * ((1<<sf)-1))>>8;
else if (sf<8) Cc = (Cc - (Cc>>(8-sf)))>>(8-sf);
uint32_t factor = Ct + (Ct>>(st-1));
Cf = ((((1<<st) - factor) * Cf) + Ct*Cc)>>st;
break;
case GGL_ADD:
if (st) {
rescale(Cf, sf, Ct, st);
Cf += Ct;
break;
if (enables & GGL_ENABLE_AA) {
...
if (enables & GGL_ENABLE_ALPHA_TEST) {
...
if (c->state.buffers.depth.format) {
....
if (enables & GGL_ENABLE_FOG) {
....
if (enables & GGL_ENABLE_BLENDING) {
...
c->state.buffers.color.write(
&(c->state.buffers.color), c, x, y, &fragment);
discard:
x += 1;
if (enables & GGL_ENABLE_SMOOTH) {
r += c->shade.drdx;
g += c->shade.dgdx;
b += c->shade.dbdx;
a += c->shade.dadx;
z += c->shade.dzdx;
f += c->shade.dfdx;
1.处理纹理
2.处理片元覆盖
3.透明测试
4.深度测试
5.雾化效果
6.混合纹理
6.处理过的像素点将会调用 context_t 中state_t中的framebuffer_t中color中的write方法。这就是上一篇文章聊过的。在OpenGL es makeCurrent时候将会把Surface中存储数据段的地址和framebuffer_t中color的bit字段关联起来。
如果对这些算法感兴趣,可以看看他们对像素的操作。
我们来看看framebuffer_t中的write方法。当通过glDrawArrays的时候,会调用trianglex_validate进行校验,同时赋值函数指针:
static void pick_read_write(surface_t* s)
switch (s->format) {
case GGL_PIXEL_FORMAT_RGBA_8888: s->read = readABGR8888; break;
case GGL_PIXEL_FORMAT_RGB_565: s->read = readRGB565; break;
default: s->read = read_pixel; break;
s->write = write_pixel;
void write_pixel(const surface_t* s, context_t* c,
uint32_t x, uint32_t y, const pixel_t* pixel)
int dither = -1;
if (c->state.enables & GGL_ENABLE_DITHER) {
dither = c->ditherMatrix[ (x & GGL_DITHER_MASK) +
((y & GGL_DITHER_MASK)<<GGL_DITHER_ORDER_SHIFT) ];
const GGLFormat* f = &(c->formats[s->format]);
int32_t index = x + (s->stride * y);
uint8_t* const data = s->data + index * f->size;
uint32_t mask = 0;
uint32_t v = 0;
for (int i=0 ; i<4 ; i++) {
const int component_mask = 1 << i;
if (f->components>=GGL_LUMINANCE &&
(i==GGLFormat::GREEN || i==GGLFormat::BLUE)) {
continue;
const int l = f->c[i].l;
const int h = f->c[i].h;
if (h && (c->state.mask.color & component_mask)) {
mask |= (((1<<(h-l))-1)<<l);
uint32_t u = pixel->c[i];
int32_t pixelSize = pixel->s[i];
if (pixelSize < (h-l)) {
u = expand(u, pixelSize, h-l);
pixelSize = h-l;
v = downshift_component(v, u, pixelSize, 0, h, l, 0, 0, dither);
if ((c->state.mask.color != 0xF) ||
(c->state.enables & GGL_ENABLE_LOGIC_OP)) {
uint32_t d = 0;
switch (f->size) {
case 1: d = *data; break;
case 2: d = *(uint16_t*)data; break;
case 3: d = (data[2]<<16)|(data[1]<<8)|data[0]; break;
case 4: d = GGL_RGBA_TO_HOST(*(uint32_t*)data); break;
if (c->state.enables & GGL_ENABLE_LOGIC_OP) {
v = logic_op(c->state.logic_op.opcode, v, d);
v &= mask;
v |= (d & ~mask);
switch (f->size) {
case 1: *data = v; break;
case 2: *(uint16_t*)data = v; break;
case 3:
data[0] = v;
data[1] = v>>8;
data[2] = v>>16;
break;
case 4: *(uint32_t*)data = GGL_HOST_TO_RGBA(v); break;
在这个方法里面,如果没办法完全看懂没关系,但是能知道获取了surface_t中的data字段,这个字段刚好和Surface的bit地址绑定起来,此时写进去数据,就是写进Surface的bit字段中。
写进去了,但是怎么显示到屏幕呢?
frameworks/native/opengl/libs/EGL/eglApi.cpp
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface)
return eglSwapBuffersWithDamageKHR(dpy, surface, NULL, 0);
EGLBoolean eglSwapBuffersWithDamageKHR(EGLDisplay dpy, EGLSurface draw,
EGLint *rects, EGLint n_rects)
ATRACE_CALL();
clearError();
const egl_display_ptr dp = validate_display(dpy);
if (!dp) return EGL_FALSE;
SurfaceRef _s(dp.get(), draw);
...
std::vector<android_native_rect_t> androidRects((size_t)n_rects);
for (int r = 0; r < n_rects; ++r) {
int offset = r * 4;
int x = rects[offset];
int y = rects[offset + 1];
int width = rects[offset + 2];
int height = rects[offset + 3];
android_native_rect_t androidRect;
androidRect.left = x;
androidRect.top = y + height;
androidRect.right = x + width;
androidRect.bottom = y;
androidRects.push_back(androidRect);
native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(), androidRects.size());
if (s->cnx->egl.eglSwapBuffersWithDamageKHR) {
return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface,
rects, n_rects);
} else {
return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
能看到这里面的核心逻辑,首先拿到整个绘制的区域,接着调用native_window_set_surface_damage设置内容,最后根据OpenGL
es是否包含eglSwapBuffersWithDamageKHR方法来决定,如果包含这个优化的方法,就调用Android平台优化过的eglSwapBuffersWithDamageKHR交换缓冲区方法,否则将会调用通用eglSwapBuffers。
frameworks/native/opengl/libagl/egl.cpp
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
...
egl_surface_t* d = static_cast<egl_surface_t*>(draw);
...
d->swapBuffers();
if (d->ctx != EGL_NO_CONTEXT) {
d->bindDrawSurface((ogles_context_t*)d->ctx);
egl_context_t* c = egl_context_t::context(d->ctx);
if (c->read == draw) {
d->bindReadSurface((ogles_context_t*)d->ctx);
return EGL_TRUE;
核心方法就是调用egl_surface_t的swapBuffers。而egl_surface_t其实就是上一篇文章解析过的egl_window_surface_v2_t对象。下面这个函数就是引出整个图元状态和申请的核心逻辑。