Android 重學系列 渲染圖層-OpenGL es上的封裝(下)

前言

經(jīng)過上一篇對OpenGL es的環(huán)境搭建,了解幾個關鍵的數(shù)據(jù)結構,本文將會解析軟件模擬紋理的繪制流程。

先擺一張,OpenGL es上下文的數(shù)據(jù)結構:


OpenGL 上下文結構.png

在閱讀本文時候,我們需要時刻記住這個圖。

如果問題,可以來本文討論http://www.itdecent.cn/p/29ab1b15cd2a

正文

回去看看我之前寫的OpenGL 的紋理與索引一文,紋理的核心步驟如下:

  • 1.glGenTextures 生成一個紋理句柄
  • 2.glBindTexture 綁定紋理句柄
  • 3.glTexParameteri 設置紋理參數(shù)
  • 4.glTexImage2D 輸入到像素數(shù)據(jù)到紋理
  • 5.glActiveTexture 激活紋理
  • 6.glDrawElements 繪制到屏幕
  • 7.eglSwapBuffers 交換緩沖區(qū),顯示到屏幕

glGenTextures

文件:/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;
    }
    // generate unique (shared) texture names
    c->surfaceManager->getToken(n, textures);
}

先獲取當前線程的上下文ogles_context_t。surfaceManager是指EGLSurfaceManager指針。EGLSurfaceManager繼承于TokenManager,getToken是屬于TokenManager。

TokenManager getToken

文件:/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;
    }
    
    // just extend the first run
    const run_t& run = mRanges[0];
    uint32_t token = run.first + run.length;
    _insertTokenAt(token, 1);
    return token;
}

實際上這里的意思就是計算token的數(shù)值。這個token其實由mRanges的vector控制。mRanges的元素其實是run_t結構體,而token則是由run_t的first和length組成。

當?shù)谝淮紊梢粋€句柄時候,會在0位置插入0,length為1,返回0.不是第一次的時候,新的token為第0個的first+length。

glBindTexture

文件:/frameworks/native/opengl/libagl/texture.cpp

void glBindTexture(GLenum target, GLuint texture)
{
    ogles_context_t* c = ogles_context_t::get();
...

    // Bind or create a texture
    sp<EGLTextureObject> tex;
    if (texture == 0) {
        // 0 is our local texture object
        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調(diào)用texture獲取該id的EGLTextureObject,如果找不到創(chuàng)建一個createTexture創(chuàng)建一個該id對應的紋理對象。

最后調(diào)用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; // already exists!

    result = new EGLTextureObject();

    status_t err = mTextures.add(name, result);
    if (err < 0)
        result.clear();

    return result;
}

此時進行了EGLTextureObject初始化并且添加到mTextures。

EGLTextureObject 初始化

class EGLTextureObject : public LightRefBase<EGLTextureObject>
{
public:
                    EGLTextureObject();
                   ~EGLTextureObject();

    status_t    setSurface(GGLSurface const* s);
    status_t    setImage(ANativeWindowBuffer* buffer);
    void        setImageBits(void* vaddr) { surface.data = (GGLubyte*)vaddr; }

    status_t            reallocate(GLint level,
                            int w, int h, int s,
                            int format, int compressedFormat, int bpr);
    inline  size_t      size() const { return mSize; }
    const GGLSurface&   mip(int lod) const;
    GGLSurface&         editMip(int lod);
    bool                hasMipmaps() const { return mMipmaps!=0; }
    bool                isComplete() const { return mIsComplete; }
    void                copyParameters(const sp<EGLTextureObject>& old);

private:
        status_t        allocateMipmaps();
            void        freeMipmaps();
            void        init();
    size_t              mSize;
    GGLSurface          *mMipmaps;
    int                 mNumExtraLod;
    bool                mIsComplete;

public:
    GGLSurface          surface;
    GLenum              wraps;
    GLenum              wrapt;
    GLenum              min_filter;
    GLenum              mag_filter;
    GLenum              internalformat;
    GLint               crop_rect[4];
    GLint               generate_mipmap;
    GLint               direct;
    ANativeWindowBuffer* buffer;
};

能看到里面很多熟悉的結構體。GGLSurface;s,t軸等參數(shù)。

EGLTextureObject::EGLTextureObject()
    : mSize(0)
{
    init();
}
void EGLTextureObject::init()
{
    memset(&surface, 0, sizeof(surface));
    surface.version = sizeof(surface);
    mMipmaps = 0;
    mNumExtraLod = 0;
    mIsComplete = false;
    wraps = GL_REPEAT;
    wrapt = GL_REPEAT;
    min_filter = GL_LINEAR;
    mag_filter = GL_LINEAR;
    internalformat = 0;
    memset(crop_rect, 0, sizeof(crop_rect));
    generate_mipmap = GL_FALSE;
    direct = GL_FALSE;
    buffer = 0;
}

在這里初始化EGLTextureObject中的參數(shù)。s和t默認是重復,同時像素是線性過濾器,ANativeWindowBuffer初始化為0.

bindTextureTmu 更新紋理狀態(tài)機

    bindTextureTmu(c, c->textures.active, texture, tex);
void bindTextureTmu(
    ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex)
{
    if (tex.get() == c->textures.tmu[tmu].texture)
        return;

    // free the reference to the previously bound object
    texture_unit_t& u(c->textures.tmu[tmu]);
    if (u.texture)
        u.texture->decStrong(c);

    // bind this texture to the current active texture unit
    // and add a reference to this texture object
    u.texture = tex.get();
    u.texture->incStrong(c);
    u.name = texture;
    invalidate_texture(c, tmu);
}

tmu就是texture_state_t中active的句柄其實就是texture_state_t 中 tmu數(shù)組中的index。如果此時綁定的紋理和當前已經(jīng)激活的紋理是同一個index,則直接返回。

否則,把EGLTextureObject 添加到active句柄(index)的位置。也就是說texture_unit_t其實對應就是EGLTextureObject。這樣ogles_context_t上下文的活躍紋理就是新的紋理對象,其中texture_unit_t的name會記錄當前我們設置的句柄值。

設置參數(shù)就跳過,我們直接看核心函數(shù)glTexImage2D。

glTexImage2D 加載圖像數(shù)據(jù)

一般用法如下:

 glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,width,height,0,GL_RGBA,GL_UNSIGNED_BYTE,data);
void glTexImage2D(
        GLenum target, GLint level, GLint internalformat,
        GLsizei width, GLsizei height, GLint border,
        GLenum format, GLenum type, const GLvoid *pixels)
{
    ogles_context_t* c = ogles_context_t::get();
//target只能是GL_TEXTURE_2D
    if (target != GL_TEXTURE_2D) {
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }
...

    int32_t size = 0;
    GGLSurface* surface = 0;
    int error = createTextureSurface(c, &surface, &size,
            level, format, type, width, height);
...

    if (pixels) {
        const int32_t formatIdx = convertGLPixelFormat(format, type);
        const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
        const int32_t align = c->textures.unpackAlignment-1;
        const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
        const int32_t stride = bpr / pixelFormat.size;

        GGLSurface userSurface;
        userSurface.version = sizeof(userSurface);
        userSurface.width  = width;
        userSurface.height = height;
        userSurface.stride = stride;
        userSurface.format = formatIdx;
        userSurface.compressedFormat = 0;
        userSurface.data = (GLubyte*)pixels;

        int err = copyPixels(c, *surface, 0, 0, userSurface, 0, 0, width, height);
        if (err) {
            ogles_error(c, err);
            return;
        }
        generateMipmap(c, level);
    }
}
  • 1.target在OpenGL es只支持GL_TEXTURE_2D
  • 2.createTextureSurface 創(chuàng)建一個紋理臨時圖層
  • 3.傳進來的像素指針數(shù)組不為空,調(diào)用copyPixels拷貝。
  • 4.generateMipmap 處理狀態(tài)機

createTextureSurface 獲取一個紋理臨時圖層

int createTextureSurface(ogles_context_t* c,
        GGLSurface** outSurface, int32_t* outSize, GLint level,
        GLenum format, GLenum type, GLsizei width, GLsizei height,
        GLenum compressedFormat = 0)
{
    // convert the pixelformat to one we can handle
    const int32_t formatIdx = convertGLPixelFormat(format, type);
    if (formatIdx == 0) { // we don't know what to do with this
        return GL_INVALID_OPERATION;
    }

    // figure out the size we need as well as the stride
    const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
    const int32_t align = c->textures.unpackAlignment-1;
    const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
    const size_t size = bpr * height;
    const int32_t stride = bpr / pixelFormat.size;

    if (level > 0) {
        const int active = c->textures.active;
        EGLTextureObject* tex = c->textures.tmu[active].texture;
        status_t err = tex->reallocate(level,
                width, height, stride, formatIdx, compressedFormat, bpr);
        if (err != NO_ERROR)
            return GL_OUT_OF_MEMORY;
        GGLSurface& surface = tex->editMip(level);
        *outSurface = &surface;
        *outSize = size;
        return 0;
    }

    sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
    status_t err = tex->reallocate(level,
            width, height, stride, formatIdx, compressedFormat, bpr);
    if (err != NO_ERROR)
        return GL_OUT_OF_MEMORY;

    tex->internalformat = format;
    *outSurface = &tex->surface;
    *outSize = size;
    return 0;
}

這里面level一般是0.不為0說明指定多級漸遠紋理的級別。則從當前正在活躍textures找到EGLTextureObject。這里我們先按照0的情況處理。

level為0則調(diào)用getAndBindActiveTextureObject查找EGLTextureObject,接著調(diào)用reallocate,重新為EGLTextureObject設置寬高,讀取數(shù)據(jù)數(shù)據(jù)的步數(shù)。最后獲取EGLTextureObject中的GGLSurface 。

getAndBindActiveTextureObject

static __attribute__((noinline))
sp<EGLTextureObject> getAndBindActiveTextureObject(ogles_context_t* c)
{
    sp<EGLTextureObject> tex;
    const int active = c->textures.active;
    const GLuint name = c->textures.tmu[active].name;

    // free the reference to the previously bound object
    texture_unit_t& u(c->textures.tmu[active]);
    if (u.texture)
        u.texture->decStrong(c);

    if (name == 0) {
        tex = c->textures.defaultTexture;
        for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
            if (c->textures.tmu[i].texture == tex.get())
                invalidate_texture(c, i);
        }
    } else {
        // get a new texture object for that name
        tex = c->surfaceManager->replaceTexture(name);
    }

    u.texture = tex.get();
    u.texture->incStrong(c);
    u.name = name;
    invalidate_texture(c, active);
    return tex;
}

在glBindTexture一步中,把活躍紋理已經(jīng)設置新的紋理對象。這個方法通過active的位置找到tmu中name對象。如果name是0,則說明是默認的紋理對象。否則將會調(diào)用EGLSurfaceManager的replaceTexture。

EGLSurfaceManager replaceTexture

sp<EGLTextureObject> EGLSurfaceManager::replaceTexture(GLuint name)
{
    sp<EGLTextureObject> tex;
    Mutex::Autolock _l(mLock);
    const ssize_t index = mTextures.indexOfKey(name);
    if (index >= 0) {
        const sp<EGLTextureObject>& old = mTextures.valueAt(index);
        const uint32_t refs = old->getStrongCount();
        if (ggl_likely(refs == 1)) {
            // we're the only owner
            tex = old;
        } else {
            // keep the texture's parameters
            tex = new EGLTextureObject();
            tex->copyParameters(old);
            mTextures.removeItemsAt(index);
            mTextures.add(name, tex);
        }
    }
    return tex;
}

此時會從name(也就是我們在glBindTexture時候的target)嘗試找到之前通過createTexture存在mTextures的紋理對象。

此時會檢測一下,紋理對象中強引用計數(shù),如果又有一個引用則不變。如果引用不為1,則會把原來的移除掉重新生成一個新的查到原來的位置。這里面目的只是為了保證引用計數(shù)能夠正常工作。

所以getAndBindActiveTextureObject其實就會返回之前設置好的active中的新紋理對象EGLTextureObject。

EGLTextureObject reallocate

status_t EGLTextureObject::reallocate(
        GLint level, int w, int h, int s,
        int format, int compressedFormat, int bpr)
{
    const size_t size = h * bpr;
    if (level == 0)
    {
        if (size!=mSize || !surface.data) {
            if (mSize && surface.data) {
                free(surface.data);
            }
            surface.data = (GGLubyte*)malloc(size);
            if (!surface.data) {
                mSize = 0;
                mIsComplete = false;
                return NO_MEMORY;
            }
            mSize = size;
        }
        surface.version = sizeof(GGLSurface);
        surface.width  = w;
        surface.height = h;
        surface.stride = s;
        surface.format = format;
        surface.compressedFormat = compressedFormat;
        if (mMipmaps)
            freeMipmaps();
        mIsComplete = true;
    }
    else
    {
...
    }
    return NO_ERROR;
}

其實這里面的工作就是釋放了GGLSurface中的數(shù)據(jù)內(nèi)存,重新申請一套內(nèi)存,并且設置當前紋理相關的數(shù)據(jù)進去。而GGLSurface這個結構體也很簡單:

typedef struct {
    GGLsizei    version;    // always set to sizeof(GGLSurface)
    GGLuint     width;      // width in pixels
    GGLuint     height;     // height in pixels
    GGLint      stride;     // stride in pixels
    GGLubyte*   data;       // pointer to the bits
    GGLubyte    format;     // pixel format
    GGLubyte    rfu[3];     // must be zero
    // these values are dependent on the used format
    union {
        GGLint  compressedFormat;
        GGLint  vstride;
    };
    void*       reserved;
} GGLSurface;

至此createTextureSurface就可以通過活躍的紋理,拿到紋理對象中臨時的繪制圖層。

copyPixels 拷貝像素數(shù)據(jù)到紋理圖層中

        const int32_t formatIdx = convertGLPixelFormat(format, type);
        const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
        const int32_t align = c->textures.unpackAlignment-1;
        const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
        const int32_t stride = bpr / pixelFormat.size;

        GGLSurface userSurface;
        userSurface.version = sizeof(userSurface);
        userSurface.width  = width;
        userSurface.height = height;
        userSurface.stride = stride;
        userSurface.format = formatIdx;
        userSurface.compressedFormat = 0;
        userSurface.data = (GLubyte*)pixels;

        int err = copyPixels(c, *surface, 0, 0, userSurface, 0, 0, width, height);
static __attribute__((noinline))
int copyPixels(
        ogles_context_t* c,
        const GGLSurface& dst,
        GLint xoffset, GLint yoffset,
        const GGLSurface& src,
        GLint x, GLint y, GLsizei w, GLsizei h)
{
    if ((dst.format == src.format) &&
        (dst.stride == src.stride) &&
        (dst.width == src.width) &&
        (dst.height == src.height) &&
        (dst.stride > 0) &&
        ((x|y) == 0) &&
        ((xoffset|yoffset) == 0))
    {
        // this is a common case...
        const GGLFormat& pixelFormat(c->rasterizer.formats[src.format]);
        const size_t size = src.height * src.stride * pixelFormat.size;
        memcpy(dst.data, src.data, size);
        return 0;
    }

    // use pixel-flinger to handle all the conversions
    GGLContext* ggl = getRasterizer(c);
    if (!ggl) {
        // the only reason this would fail is because we ran out of memory
        return GL_OUT_OF_MEMORY;
    }

    ggl->colorBuffer(ggl, &dst);
    ggl->bindTexture(ggl, &src);
    ggl->texCoord2i(ggl, x-xoffset, y-yoffset);
    ggl->recti(ggl, xoffset, yoffset, xoffset+w, yoffset+h);
    return 0;
}

首先看到此時會進行userface臨時紋理圖層和紋理圖層比較規(guī)格。如果一致,則獲取當前的像素顏色規(guī)格,找到更加精確的surface大小,把glTexImage2D中的pixel像素的數(shù)據(jù)拷貝到紋理的的GGLSurface中。

這是絕大部分情況,也是當前的邏輯。當然遇到兩個規(guī)格不同的surface需要拷貝數(shù)據(jù),則需要把邏輯交給GGLContext處理。這里暫時不考慮。因為userface的規(guī)格參數(shù)是拷貝紋理的GGLSurface的。

generateMipmap

void generateMipmap(ogles_context_t* c, GLint level)
{
    if (level == 0) {
        const int active = c->textures.active;
        EGLTextureObject* tex = c->textures.tmu[active].texture;
        if (tex->generate_mipmap) {
            if (buildAPyramid(c, tex) != NO_ERROR) {
                ogles_error(c, GL_OUT_OF_MEMORY);
                return;
            }
        }
    }
}

generate_mipmap 此時為false,將不會走進buildAPyramid。

glActiveTexture 激活紋理

void glActiveTexture(GLenum texture)
{
    ogles_context_t* c = ogles_context_t::get();
    if (uint32_t(texture-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) {
        ogles_error(c, GL_INVALID_ENUM);
        return;
    }
    c->textures.active = texture - GL_TEXTURE0;
    c->rasterizer.procs.activeTexture(c, c->textures.active);
}

關鍵是c->rasterizer.procs.activeTexture。rasterizer是指context_t,而procs則是GGLContext。

GGLContext activeTexture

文件:/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狀態(tài)管理器就拿到了活躍的紋理對象。

glDrawElements 繪制到屏幕

文件:/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; // all triangles are culled

    // clear the vertex-cache
    c->vc.clear();
    validate_arrays(c, mode);

    // if indices are in a buffer object, the pointer is treated as an
    // offset in that buffer.
    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結構體,繪制頂點,繪制三角形,檢查綁定在頂點數(shù)組對象的紋理。

校驗拷貝olgs_context_t數(shù)據(jù)到state_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);

        // disable this texture unit if it's not complete
        if (!u.texture->isComplete()) {
            c->rasterizer.procs.disable(c, GGL_TEXTURE_2D);
        }
    }
}

在這個過程中會獲取之前放在ogles_context_t.texture_state中的數(shù)據(jù),全部拷貝到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;

    // All iterated values are sampled at the pixel center

    // reset iterators for that scanline...
    GGLcolor r, g, b, a;
    iterators_t& ci = c->iterators;
...

    // z iterators are 1.31
    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--) {
    
        { // just a scope

        // read color (convert to 8 bits by keeping only the integer part)
        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);

        // texturing
        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;

                // s-coordinate
                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;
                }

                // t-coordinate
                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;
                }

                // read texture
                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] ;
                    }
                }

                // Texture environnement...
                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;
                    }
                }
            }
        }
    
        // coverage application
        if (enables & GGL_ENABLE_AA) {
...
        }
        
        // alpha-test  透明測試
        if (enables & GGL_ENABLE_ALPHA_TEST) {
          ...
        }
        
        // depth test 深度測試
        if (c->state.buffers.depth.format) {
           ....
        }

        // 霧化效果
        if (enables & GGL_ENABLE_FOG) {
           ....
        }

        //混合
        if (enables & GGL_ENABLE_BLENDING) {
            ...
        }

        // write,調(diào)用framebuffer_t的寫方法
        c->state.buffers.color.write(
                &(c->state.buffers.color), c, x, y, &fragment);
        }

discard:
        // iterate...
        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.處理過的像素點將會調(diào)用 context_t 中state_t中的framebuffer_t中color中的write方法。這就是上一篇文章聊過的。在OpenGL es makeCurrent時候將會把Surface中存儲數(shù)據(jù)段的地址和framebuffer_t中color的bit字段關聯(lián)起來。

如果對這些算法感興趣,可以看看他們對像素的操作。

我們來看看framebuffer_t中的write方法。當通過glDrawArrays的時候,會調(diào)用trianglex_validate進行校驗,同時賦值函數(shù)指針:

static void pick_read_write(surface_t* s)
{
    // Choose best reader/writers.
    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)) {
            // destinations L formats don't have G or B
            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地址綁定起來,此時寫進去數(shù)據(jù),就是寫進Surface的bit字段中。

寫進去了,但是怎么顯示到屏幕呢?

eglSwapBuffers 交換緩沖區(qū),顯示到屏幕

文件:/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);
    }
}

能看到這里面的核心邏輯,首先拿到整個繪制的區(qū)域,接著調(diào)用native_window_set_surface_damage設置內(nèi)容,最后根據(jù)OpenGL
es是否包含eglSwapBuffersWithDamageKHR方法來決定,如果包含這個優(yōu)化的方法,就調(diào)用Android平臺優(yōu)化過的eglSwapBuffersWithDamageKHR交換緩沖區(qū)方法,否則將會調(diào)用通用eglSwapBuffers。

OpenGL es eglSwapBuffers

文件:/frameworks/native/opengl/libagl/egl.cpp

 EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
{
...

    egl_surface_t* d = static_cast<egl_surface_t*>(draw);
...

    // post the surface
    d->swapBuffers();

    // if it's bound to a context, update the buffer
    if (d->ctx != EGL_NO_CONTEXT) {
        d->bindDrawSurface((ogles_context_t*)d->ctx);
        // if this surface is also the read surface of the context
        // it is bound to, make sure to update the read buffer as well.
        // The EGL spec is a little unclear about this.
        egl_context_t* c = egl_context_t::context(d->ctx);
        if (c->read == draw) {
            d->bindReadSurface((ogles_context_t*)d->ctx);
        }
    }

    return EGL_TRUE;
}

核心方法就是調(diào)用egl_surface_t的swapBuffers。而egl_surface_t其實就是上一篇文章解析過的egl_window_surface_v2_t對象。下面這個函數(shù)就是引出整個圖元狀態(tài)和申請的核心邏輯。

egl_window_surface_v2_t swapBuffers

EGLBoolean egl_window_surface_v2_t::swapBuffers()
{
...

    if (previousBuffer) {
        previousBuffer->common.decRef(&previousBuffer->common); 
        previousBuffer = 0;
    }
    
    unlock(buffer);
    previousBuffer = buffer;
    nativeWindow->queueBuffer(nativeWindow, buffer, -1);
    buffer = 0;

    // dequeue a new buffer
    int fenceFd = -1;
    if (nativeWindow->dequeueBuffer(nativeWindow, &buffer, &fenceFd) == NO_ERROR) {
        sp<Fence> fence(new Fence(fenceFd));
        if (fence->wait(Fence::TIMEOUT_NEVER)) {
            nativeWindow->cancelBuffer(nativeWindow, buffer, fenceFd);
            return setError(EGL_BAD_ALLOC, EGL_FALSE);
        }

        // reallocate the depth-buffer if needed
        if ((width != buffer->width) || (height != buffer->height)) {
            // TODO: we probably should reset the swap rect here
            // if the window size has changed
            width = buffer->width;
            height = buffer->height;
            if (depth.data) {
                free(depth.data);
                depth.width   = width;
                depth.height  = height;
                depth.stride  = buffer->stride;
                uint64_t allocSize = static_cast<uint64_t>(depth.stride) *
                        static_cast<uint64_t>(depth.height) * 2;
                if (depth.stride < 0 || depth.height > INT_MAX ||
                        allocSize > UINT32_MAX) {
                    setError(EGL_BAD_ALLOC, EGL_FALSE);
                    return EGL_FALSE;
                }
                depth.data    = (GGLubyte*)malloc(allocSize);
                if (depth.data == 0) {
                    setError(EGL_BAD_ALLOC, EGL_FALSE);
                    return EGL_FALSE;
                }
            }
        }

        // keep a reference on the buffer
        buffer->common.incRef(&buffer->common);

        // finally pin the buffer down
        if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN |
                GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
...
        }
    } else {
        return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE);
    }

    return EGL_TRUE;
}

在這個過程中做了兩個十分重要的事情:

  • 1.調(diào)用了nativeWindow->queueBuffer 把之前通過egl_window_surface_v2_t申請出來的圖元進行queue進入相關的參數(shù)生成一個個BufferItem插入到緩沖隊列中,等待消費
  • 2.調(diào)用了nativeWindow->dequeueBuffer 把buffer設置進去并且根據(jù)圖元生產(chǎn)者那邊返回來的slotId(也就是從freeSlot和freebuffer中找到的空閑GraphicBuffer對應緩沖隊列的index)返回,也會按照需求新生成一個新的GraphicBuffer。

一般來說,都是先調(diào)用dequeue生產(chǎn)一個新的GraphicBuffer,之后調(diào)用queueBuffer把當前的圖元對應的BufferItem放到緩沖隊列。那么這里面的意思很簡單,就是swapBuffers繪制上一幀數(shù)的內(nèi)容,接著生成新的一幀,繼續(xù)在OpenGL es中繪制。

這里能第一次看到fence同步柵的存在。其實原因很簡單,一般來說CPU不知道GPU什么時候會完成,需要一個同步柵去通過GPU和CPU。

Android平臺 OpenGL es的優(yōu)化

能看到其實在紋理過程中,涉及到了不少臨時圖層,如果遇到紋理十分大時候,臨時圖層來來去去的拷貝十分影響速度。因此在Android中使用了直接紋理機制。

其核心的思想是,不需要臨時圖層,Android系統(tǒng)直接提供ANativeBuffer,你們繪制在上面就好了。

使用方法如下:

//生成一個直接紋理緩沖
EGLImageKHR eglSrcImage = eglCreateImageKHR(eglDisplay,
EGL_NO_CONTEXT,EGL_NATIVE_BUFFER_ANDROID,(EGLClientBuffer)&sSrcBuffer,0);
//生成
glGenTextures(1,&texID);
//綁定
glBindTexture(GL_TEXTURE_2D,texID);
//存儲數(shù)據(jù)
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,eglSrcImage);

我們需要生成一個EGLImage對象,承載像素數(shù)據(jù),記者直接操作EGLImage。其中EGLClientBuffer這個參數(shù)就是ANativeWindowBuffer。

eglCreateImageKHR

EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
        EGLClientBuffer buffer, const EGLint *attrib_list)
{
    clearError();

    const egl_display_ptr dp = validate_display(dpy);
    if (!dp) return EGL_NO_IMAGE_KHR;

    ContextRef _c(dp.get(), ctx);
    egl_context_t * const c = _c.get();

    std::vector<EGLint> strippedAttribList;
    for (const EGLint *attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) {
        if (attr[0] == EGL_GL_COLORSPACE_KHR &&
            dp->haveExtension("EGL_EXT_image_gl_colorspace")) {
            if (attr[1] != EGL_GL_COLORSPACE_LINEAR_KHR &&
                attr[1] != EGL_GL_COLORSPACE_SRGB_KHR) {
                continue;
            }
        }
        strippedAttribList.push_back(attr[0]);
        strippedAttribList.push_back(attr[1]);
    }
    strippedAttribList.push_back(EGL_NONE);

    EGLImageKHR result = EGL_NO_IMAGE_KHR;
    egl_connection_t* const cnx = &gEGLImpl;
    if (cnx->dso && cnx->egl.eglCreateImageKHR) {
        result = cnx->egl.eglCreateImageKHR(
                dp->disp.dpy,
                c ? c->context : EGL_NO_CONTEXT,
                target, buffer, strippedAttribList.data());
    }
    return result;
}

核心方法很簡單就是獲取屏幕中的配置參數(shù),調(diào)用了OpenGL es的eglCreateImageKHR。

OpenGL es的eglCreateImageKHR

EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
        EGLClientBuffer buffer, const EGLint* /*attrib_list*/)
{
    if (egl_display_t::is_valid(dpy) == EGL_FALSE) {
        return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE_KHR);
    }
    if (ctx != EGL_NO_CONTEXT) {
        return setError(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR);
    }
    if (target != EGL_NATIVE_BUFFER_ANDROID) {
        return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
    }

    ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)buffer;

    if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC)
        return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);

    if (native_buffer->common.version != sizeof(ANativeWindowBuffer))
        return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);

    switch (native_buffer->format) {
        case HAL_PIXEL_FORMAT_RGBA_8888:
        case HAL_PIXEL_FORMAT_RGBX_8888:
        case HAL_PIXEL_FORMAT_RGB_888:
        case HAL_PIXEL_FORMAT_RGB_565:
        case HAL_PIXEL_FORMAT_BGRA_8888:
            break;
        default:
            return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
    }

    native_buffer->common.incRef(&native_buffer->common);
    return (EGLImageKHR)native_buffer;
}

在這里面完全沒有做什么工作,就是檢測了像素規(guī)格,把ANativeWindowBuffer強轉為EGLImageKHR返回。

glEGLImageTargetTexture2DOES

void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
{
    ogles_context_t* c = ogles_context_t::get();
...
    ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)image;
...
    // bind it to the texture unit
    sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
    tex->setImage(native_buffer);
}

能看到此時會拿到當前活躍target對應的EGLTextureObject,并且設置到Image中,其實就是設置到EGLTextureObject中的buffer參數(shù)中。

這樣就就不會有一次glTextImage2D一樣有一個中間圖層誕生,這樣加速了整個圖層計算速度。同時讓OpenGL es可以和Skia等其他畫筆進行快速協(xié)調(diào)合作。

總結

先用一張圖總結一下:


OpenGL es紋理繪制過程.png

到這里,我就把Android對OpenGL es的紋理開發(fā)流程的源碼重新梳理了一遍,對整個機制有了更加深刻的理解。

其實第一次接觸OpenGL es為什么要先glGen接著glBind生成一個頂點/緩沖/紋理等對象進行操作。內(nèi)部是怎么一個機制,真的套路都是背的。閱讀源碼之后,才知道原來是這么簡單。

  • 每一個glGen其實就是誕生找到一個合適空閑句柄,每一個glBind這個時候才是把句柄和OpenGL es底層的對象真的綁定起來,并且讓ogles_context_t持有當前操作對象。這也是為什么我們需要glBind之后才進行操作。而且一般句柄為0在OpenGL es中,在管理數(shù)組中要么是默認要么就是無效,所以我們接觸綁定可以把句柄設置為0.

  • 當我們調(diào)用一次glTextImge2D的時候,將數(shù)據(jù)傳到一個臨時的圖層中進行拷貝持有。因此,我們每一次不需要的時候,記得要glDelete對應的紋理還有其他對象,因為會不少臨時變量占用不釋放。

  • 除了頂點和緩沖之外,如紋理,深度測試,透明測試,霧化,裁剪等需要圖層需要圖層承載的操作,都會設置到state_t一個象征著當前操作都設置進去的操作的參數(shù)。就以紋理繪制為例子,ogles_context_t 的texture的數(shù)組保存著EGLTextureObject,在state_t保存著每一個EGLTextureObject對應的參數(shù),當繪制的時候將會

  • 所有圖層(framebuffer_t)都是在一個state_t一個當前狀態(tài)結構體控制,context_t則是狀態(tài)控制器,能夠快捷的獲得一些需要的參數(shù)。

  • 如紋理這種特殊一點,需要進行一次激活,讓最外層的上下文持有當前的活躍對象。也是因為紋理的特殊性不像頂點和緩沖,有時候不需要繪制。

  • glDrawArrays 和 glDrawElements 才是把統(tǒng)統(tǒng)這些操作繪制到一個臨時的圖層。

  • eglSwapBuffer 它的作用就是真正把圖元繪制到各個平臺中。

有了本文的鋪墊,引出了GraphicBuffer這個對象,它是怎么誕生的,又是怎么傳輸?shù)摹O乱黄恼聦痛蠹伊牧?,GraphicBuffer的生成,以及Android新版本引入的GraphicBuffer的內(nèi)核控制機制ion.c,來比較比較ashmem之間的區(qū)別,看看為什么Android要拋棄它。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容