Native層-OpenGL ES-雙緩衝離屏渲染

OpenGL ES相關的數據結構:

typedef struct {
	EGLint MajorVersion;
	EGLint MinorVersion;
	EGLDisplay Display;
	EGLConfig Config;
	EGLSurface TinySurface;
	EGLSurface MainSurface;
	EGLContext Context;
} ovrEgl;

1.初始化EGLDisplay Display

egl->Display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

2.利用display對象獲得opengl版本

eglInitialize(egl->Display, &egl->MajorVersion, &egl->MinorVersion);
3.獲取display對象的配置

	const int MAX_CONFIGS = 1024;
	EGLConfig configs[MAX_CONFIGS];
	EGLint numConfigs = 0;
	if (eglGetConfigs(egl->Display, configs, MAX_CONFIGS, &numConfigs)
			== EGL_FALSE) {
		ALOGE("        eglGetConfigs() failed: %s",
				EglErrorString(eglGetError()));
		return;
	}
4.自定義配置條件

	const EGLint configAttribs[] = { EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8,
			EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 0, EGL_SAMPLES, 0, EGL_NONE };
5.獲取符合自定義配置條件的配置
	egl->Config = 0;
	for (int i = 0; i < numConfigs; i++) {
		EGLint value = 0;
		//從configs中獲取指定屬性的值,這裏是獲取屬性EGL_RENDERABLE_TYPE的值
		//Returns a bitmask indicating the types of supported client API contexts
		eglGetConfigAttrib(egl->Display, configs[i], EGL_RENDERABLE_TYPE,
				&value);
		//獲取到了之後判斷是否符合條件,如果不符合條件
		ALOGV("EGL_RENDERABLE_TYPE's value=%x,", value);
		if ((value & EGL_OPENGL_ES3_BIT_KHR) != EGL_OPENGL_ES3_BIT_KHR) {
			continue;
		}

		// The pbuffer config also needs to be compatible with normal window rendering
		// so it can share textures with the window context.
		eglGetConfigAttrib(egl->Display, configs[i], EGL_SURFACE_TYPE, &value);
		ALOGV("EGL_SURFACE_TYPE's value=%x,", value);
		if ((value & (EGL_WINDOW_BIT | EGL_PBUFFER_BIT))
				!= (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) {
			continue;
		}
		//判斷屬性是否與表configAttribs中定義的屬性值一致,好像什麼也沒做啊
		int j = 0;
		ALOGV("i = %d", i);
		for (; configAttribs[j] != EGL_NONE; j += 2) {
			eglGetConfigAttrib(egl->Display, configs[i], configAttribs[j],
					&value);
			if (value != configAttribs[j + 1]) {
				break;
			}
		}
		//如果完全一致,那麼將配置i賦給egl->Config
		if (configAttribs[j] == EGL_NONE) {
			egl->Config = configs[i];
			break;
		}
	}
	//如果沒有找到Config,那麼提示錯誤信息,並返回
	if (egl->Config == 0) {
		ALOGE("        eglChooseConfig() failed: %s",
				EglErrorString(eglGetError()));
		return;
	}
6.創建eglContext

//如果配置信息正確,那麼創建一個eglContext
	EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE };
	egl->Context = eglCreateContext(egl->Display, egl->Config,
			(shareEgl != NULL) ? shareEgl->Context : EGL_NO_CONTEXT,
			contextAttribs);
	//如果創建失敗,則返回
	if (egl->Context == EGL_NO_CONTEXT) {
		ALOGE("        eglCreateContext() failed: %s",
				EglErrorString(eglGetError()));
		return;
	}

7.如果創建eglContext成功,則接着創建一個PbufferSurface

	const EGLint surfaceAttribs[] = { EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE };
	egl->TinySurface = eglCreatePbufferSurface(egl->Display, egl->Config,
			surfaceAttribs);
	if (egl->TinySurface == EGL_NO_SURFACE) {
		ALOGE("        eglCreatePbufferSurface() failed: %s",
				EglErrorString(eglGetError()));
		eglDestroyContext(egl->Display, egl->Context);
		egl->Context = EGL_NO_CONTEXT;
		return;
	}

8.將EGL rendering context綁定到EGL surfaces

//eglMakeCurrent — attach an EGL rendering context to EGL surfaces
	if (eglMakeCurrent(egl->Display, egl->TinySurface, egl->TinySurface,
			egl->Context) == EGL_FALSE) {
		ALOGE("        eglMakeCurrent() failed: %s",
				EglErrorString(eglGetError()));
		eglDestroySurface(egl->Display, egl->TinySurface);
		eglDestroyContext(egl->Display, egl->Context);
		egl->Context = EGL_NO_CONTEXT;
		return;
	}

9.初始化MainSurface

if (app->NativeWindow != NULL && app->Egl.MainSurface == EGL_NO_SURFACE) {
		ALOGV("ovrEgl_CreateSurface");
		ovrEgl_CreateSurface(&app->Egl, app->NativeWindow);
	}

static void ovrEgl_CreateSurface(ovrEgl * egl, ANativeWindow * nativeWindow) {
	if (egl->MainSurface != EGL_NO_SURFACE) {
		return;
	}
	ALOGV(
			"        MainSurface = eglCreateWindowSurface( Display, Config, nativeWindow, attribs )");
	const EGLint surfaceAttribs[] = { EGL_NONE };
	egl->MainSurface = eglCreateWindowSurface(egl->Display, egl->Config,
			nativeWindow, surfaceAttribs);
	if (egl->MainSurface == EGL_NO_SURFACE) {
		ALOGE("        eglCreateWindowSurface() failed: %s",
				EglErrorString(eglGetError()));
		return;
	}
	ALOGV(
			"        eglMakeCurrent( display, MainSurface, MainSurface, Context )");
	if (eglMakeCurrent(egl->Display, egl->MainSurface, egl->MainSurface,
			egl->Context) == EGL_FALSE) {
		ALOGE("        eglMakeCurrent() failed: %s",
				EglErrorString(eglGetError()));
		return;
	}
}

注意,這裏通過eglMakeCurrent函數將context由步驟8中的TinySurface綁定到了MainSurface上。

之後的渲染就渲染在了MainSurface之上。

10.構建渲染數據


省略,此部分由用戶決定


11.將surface上的內容畫出來

eglSwapBuffers(appState.Egl.Display, appState.Egl.MainSurface);

由文章:

http://blog.copper3d.org/OpenGL_ES_3.0/an_introduction_to_egl/creating_an_off-screen_rendering_area_egl_pbuffers.html

可知,上面是創建了一個On-Screen 渲染surface(MainSurface),一個Off-Screen 渲染surface(TinySurface)


這裏我們關注到eglMakeCurrent的函數參數

EGLBoolean eglMakeCurrent(EGLDisplay display,
                          EGLSurface draw,
                          EGLSurface read,
                          EGLContext context)
------------------------------------------------------
display         specifies the EGL display connection
draw            specifies the EGL draw surface
read            specifies the EGL read surface
context         specifies the EGL rendering context to be attached to the
                surfaces

可以知道,每次調用eglMakeCurrent都可以重新指定用戶讀和寫的Surface

因此,雙緩衝的buffer切換,就可以利用這個來實現。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章