SDL.png

项目位置 https://github.com/deepsadness/SDLCmakeDemo

系列内容导读

  1. SDL2-移植Android Studio+CMakeList集成
  2. Android端FFmpeg +SDL2的简单播放器
  3. SDL2 Android端的简要分析(VideoSubSystem)
  4. SDL2 Android端的简要分析(AudioSubSystem)

Android 部分源码分析

暂时只包括视频系统的部分。

1. Android上SDLThread启动初始化
2.SDL初始化
SDL_Init(): 初始化SDL。
SDL_CreateWindow(): 创建窗口(Window)。
SDL_CreateRenderer(): 基于窗口创建渲染器(Render)。
SDL_CreateTexture(): 创建纹理(Texture)。
3. SDL循环渲染数据
SDL_UpdateTexture(): 设置纹理的数据。
SDL_RenderCopy(): 纹理复制给渲染器。
SDL_RenderPresent(): 显示。

SDLThread 启动的初始化

根据SDLActivity的初始化流程。来看一下SDL的初始化。

SDLActivity::onCreate方法

    @Override    protected void onCreate(Bundle savedInstanceState) {        Log.v(TAG, "Device: " + Build.DEVICE);        Log.v(TAG, "Model: " + Build.MODEL);        Log.v(TAG, "onCreate()");        super.onCreate(savedInstanceState);        // Load shared libraries        String errorMsgBrokenLib = "";        try {            //加载库            loadLibraries();        } catch (UnsatisfiedLinkError e) {            System.err.println(e.getMessage());            mBrokenLibraries = true;            errorMsgBrokenLib = e.getMessage();        } catch (Exception e) {            System.err.println(e.getMessage());            mBrokenLibraries = true;            errorMsgBrokenLib = e.getMessage();        }                  //没加载成功。就弹出框提示。        if (mBrokenLibraries) {            mSingleton = this;            AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);            dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall."                    + System.getProperty("line.separator")                    + System.getProperty("line.separator")                    + "Error: " + errorMsgBrokenLib);            dlgAlert.setTitle("SDL Error");            dlgAlert.setPositiveButton("Exit",                    new DialogInterface.OnClickListener() {                        @Override                        public void onClick(DialogInterface dialog, int id) {                            // if this button is clicked, close current activity                            SDLActivity.mSingleton.finish();                        }                    });            dlgAlert.setCancelable(false);            dlgAlert.create().show();            return;        }        // Set up JNI        SDL.setupJNI();        // Initialize state        SDL.initialize();        // So we can call stuff from static callbacks        mSingleton = this;        SDL.setContext(this);      // 剪贴板        if (Build.VERSION.SDK_INT >= 11) {            mClipboardHandler = new SDLClipboardHandler_API11();        } else {            /* Before API 11, no clipboard notification (eg no SDL_CLIPBOARDUPDATE) */            mClipboardHandler = new SDLClipboardHandler_Old();        }              //HID device        mHIDDeviceManager = HIDDeviceManager.acquire(this);        //创建Surface         mSurface = new SDLSurface(getApplication());        mLayout = new RelativeLayout(this);        mLayout.addView(mSurface);        // Get our current screen orientation and pass it down.        mCurrentOrientation = SDLActivity.getCurrentOrientation();        SDLActivity.onNativeOrientationChanged(mCurrentOrientation);        setContentView(mLayout);        setWindowStyle(false);        getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this);        // Get filename from "Open with" of another application        Intent intent = getIntent();        if (intent != null && intent.getData() != null) {            String filename = intent.getData().getPath();            if (filename != null) {                Log.v(TAG, "Got filename: " + filename);                SDLActivity.onNativeDropFile(filename);            }        }    }
  • loadLibraries
//这个方法,可以看到就是我们之前的定义的库的名称。如果我们在CMakeList.txt中修改了自己编译的库的名称。那这里也要记得修改。 protected String[] getLibraries() {        return new String[]{                "SDL2",                // "SDL2_image",                // "SDL2_mixer",                // "SDL2_net",                // "SDL2_ttf",                "main"        };    } // Load the .so    public void loadLibraries() {        for (String lib : getLibraries()) {            SDL.loadLibrary(lib);        }    }  //如果添加了com.getkeepsafe.relinker.ReLinker这个库,就会用它就加载。没有的话,就系统默认的方法。  public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {        if (libraryName == null) {            throw new NullPointerException("No library name provided.");        }        try {            Class relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");            Class relinkListenerClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");            Class contextClass = mContext.getClassLoader().loadClass("android.content.Context");            Class stringClass = mContext.getClassLoader().loadClass("java.lang.String");            // Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if             // they've changed during updates.            Method forceMethod = relinkClass.getDeclaredMethod("force");            Object relinkInstance = forceMethod.invoke(null);            Class relinkInstanceClass = relinkInstance.getClass();            // Actually load the library!            Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);            loadMethod.invoke(relinkInstance, mContext, libraryName, null, null);        }        catch (final Throwable e) {            // Fall back            try {                System.loadLibrary(libraryName);            }            catch (final UnsatisfiedLinkError ule) {                throw ule;            }            catch (final SecurityException se) {                throw se;            }        }            }

SDL.setupJNI()

这个方法会对SDL中的模块进行初始化。

   public static void setupJNI() {        //SDLActivity中的JNI方法进行初始化。可以一定程度的认为是音频系统的初始化        SDLActivity.nativeSetupJNI();        //音频系统的初始化        SDLAudioManager.nativeSetupJNI();        // 控制系统的初始化        SDLControllerManager.nativeSetupJNI();    }
SDLActivity::nativeSetUpJNI

进入到SDL_android.c文件中。
我们首先来看一下方法签名的定义方式。这里和通常直接写方法签名的方式不同。
SDL使用拼接的方式,来完成我们的JNI方法的定义的。

#define SDL_JAVA_PREFIX                                 org_libsdl_app#define CONCAT1(prefix, class, function)                CONCAT2(prefix, class, function)#define CONCAT2(prefix, class, function)                Java_ ## prefix ## _ ## class ## _ ## function#define SDL_JAVA_INTERFACE(function)                    CONCAT1(SDL_JAVA_PREFIX, SDLActivity, function)

这样的好处,是我们可以方便的修改JNI方法的类。

目前的包名和类名.png
我们可以看到
SDL_JAVA_PREFIX这个宏对应的是 我们的包名。
SDL_JAVA_INTERFACE 的值 CONCAT1(SDL_JAVA_PREFIX, SDLActivity, function) 中的 SDLActivity对应的就是我们的类名。

如果我们修改了包名和类名。只要过来修改这两个宏的值就可以了。
是不是超级方便~
其实如果熟悉JNI的话,也会很清楚JNI方法名定义的套路的。就和这里的CONCAT2宏定义的一样。这儿就不细说了。
定义的话,就接着自己补齐JNIEXPORT 和返回的变量 和变量名称和参数签名就可以了。

  • nativeSetupJNI的完整方法签名
/* Java class SDLActivity */JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(        JNIEnv* mEnv, jclass cls);

因为这些都和包名和类没有关系,所以通过这样的宏,就可以把两者解耦,达到随意改类名和包名了。

  • nativeSetupJNI的方法实现
  1. 将当前的线程attached到当前APP的JVM线程当中
JNIEnv* Android_JNI_GetEnv(void){    /*根据JNI的调试,所有的线程都是Linux线程,在c中创建了线程,并且attached 到         JavaVM上。     在该线程上就可以有了JVM环境,可以调用JNI的方法。     如果attached 一个原生创建的线程会直接在main 线程组创建一个线程对象。     AttachCurrentThread可以随意调用。不管是否已经attached过。    */    JNIEnv *env;    int status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL);    if(status < 0) {        LOGE("failed to attach current thread");        return 0;    }    /* 调用了这个方法,就是在thread_local中把JNIEnv保存起来。      这样的话,会自动创建一个解构方法,在线程终止时。该解构方法,会自动调用DetachCurrentThread 。      因为AttachCurrentThread 和DetachCurrentThread  方法是期待成对出现的。我们用了下面方法,就可以不自己写DetachCurrentThread  了。    */    pthread_setspecific(mThreadKey, (void*) env);    return env;}//上面的mThreadKey,是在加载库初始化的是,创建的如下。对应的解构函数也是自己传入的。JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){    JNIEnv *env;    mJavaVM = vm;    LOGI("JNI_OnLoad called");    if ((*mJavaVM)->GetEnv(mJavaVM, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {        LOGE("Failed to get the environment using GetEnv()");        return -1;    }    if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed) != 0) {        __android_log_print(ANDROID_LOG_ERROR, "SDL", "Error initializing pthread key");    }    Android_JNI_SetupThread();    return JNI_VERSION_1_4;}static void Android_JNI_ThreadDestroyed(void* value){    /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */    JNIEnv *env = (JNIEnv*) value;    if (env != NULL) {        (*mJavaVM)->DetachCurrentThread(mJavaVM);        pthread_setspecific(mThreadKey, NULL);    }}

总结下来,创建mThreadKey有两个好处。

  1. 可以在JVM调试的时候,对给线程进行调试
  2. 可以传入解构函数
  1. 就是讲需要通过JNI调用的Native方法的函数都缓存起来。
    mActivityClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));    midGetNativeSurface = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "getNativeSurface","()Landroid/view/Surface;");    midSetActivityTitle = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "setActivityTitle","(Ljava/lang/String;)Z");    midSetWindowStyle = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "setWindowStyle","(Z)V");    midSetOrientation = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "setOrientation","(IIZLjava/lang/String;)V");    midGetContext = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "getContext","()Landroid/content/Context;");    midIsTablet = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "isTablet", "()Z");    midIsAndroidTV = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "isAndroidTV","()Z");    midIsChromebook = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "isChromebook", "()Z");    midIsDeXMode = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "isDeXMode", "()Z");    midManualBackButton = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "manualBackButton", "()V");    midInputGetInputDeviceIds = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "inputGetInputDeviceIds", "(I)[I");    midSendMessage = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "sendMessage", "(II)Z");    midShowTextInput =  (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "showTextInput", "(IIII)Z");    midIsScreenKeyboardShown = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "isScreenKeyboardShown","()Z");    midClipboardSetText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "clipboardSetText", "(Ljava/lang/String;)V");    midClipboardGetText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "clipboardGetText", "()Ljava/lang/String;");    midClipboardHasText = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "clipboardHasText", "()Z");    midOpenAPKExpansionInputStream = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");    midGetManifestEnvironmentVariables = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass,                                "getManifestEnvironmentVariables", "()Z");    midGetDisplayDPI = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "getDisplayDPI", "()Landroid/util/DisplayMetrics;");    midCreateCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "createCustomCursor", "([IIIII)I");    midSetCustomCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setCustomCursor", "(I)Z");    midSetSystemCursor = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setSystemCursor", "(I)Z");    midSupportsRelativeMouse = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "supportsRelativeMouse", "()Z");    midSetRelativeMouseEnabled = (*mEnv)->GetStaticMethodID(mEnv, mActivityClass, "setRelativeMouseEnabled", "(Z)Z");    if (!midGetNativeSurface ||       !midSetActivityTitle || !midSetWindowStyle || !midSetOrientation || !midGetContext || !midIsTablet || !midIsAndroidTV || !midInputGetInputDeviceIds ||       !midSendMessage || !midShowTextInput || !midIsScreenKeyboardShown ||       !midClipboardSetText || !midClipboardGetText || !midClipboardHasText ||       !midOpenAPKExpansionInputStream || !midGetManifestEnvironmentVariables || !midGetDisplayDPI ||       !midCreateCustomCursor || !midSetCustomCursor || !midSetSystemCursor || !midSupportsRelativeMouse || !midSetRelativeMouseEnabled ||       !midIsChromebook || !midIsDeXMode || !midManualBackButton) {        __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLActivity.java?");    }    fidSeparateMouseAndTouch = (*mEnv)->GetStaticFieldID(mEnv, mActivityClass, "mSeparateMouseAndTouch", "Z");    if (!fidSeparateMouseAndTouch) {        __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java static fields, do you have the latest version of SDLActivity.java?");    }
  1. 检查是否初始化完成。
    这里要上面三个模块,全部初始化完整之后,就会将进入SDL_SetMainReady状态
void checkJNIReady(){    if (!mActivityClass || !mAudioManagerClass || !mControllerManagerClass) {        // We aren't fully initialized, let's just return.        return;    }    SDL_SetMainReady();    }

剩下的SDLAudioManager.nativeSetupJNI()SDLControllerManager.nativeSetupJNI()也基本上一样。就不看了。

SDL.initialize()
初始化上面三个的java对象

创建SDLSurface,并添加到跟布局中。
后面还有一些参数设置。

下面就进入SDLSurface的生命周期当中。

在SDLSurface的对应的生命周期中,会调用handleNativeState对Native的State进行修改。

  /* Transition to next state */    public static void handleNativeState() {        if (mNextNativeState == mCurrentNativeState) {            // Already in same state, discard.            return;        }        // Try a transition to init state        if (mNextNativeState == NativeState.INIT) {            mCurrentNativeState = mNextNativeState;            return;        }        // Try a transition to paused state        if (mNextNativeState == NativeState.PAUSED) {            nativePause();            if (mSurface != null)                mSurface.handlePause();            mCurrentNativeState = mNextNativeState;            return;        }        // Try a transition to resumed state        if (mNextNativeState == NativeState.RESUMED) {            if (mIsSurfaceReady && mHasFocus && mIsResumedCalled) {                if (mSDLThread == null) {                    // This is the entry point to the C app.                    // Start up the C app thread and enable sensor input for the first time                    // FIXME: Why aren't we enabling sensor input at start?                    mSDLThread = new Thread(new SDLMain(), "SDLThread");                    mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);                    mSDLThread.start();                }                nativeResume();                mSurface.handleResume();                mCurrentNativeState = mNextNativeState;            }        }    }

在Resume的状态中,会启动一个SDLThread线程。
线程运行下面这个Runnable .

class SDLMain implements Runnable {    @Override    public void run() {        //得到我们在Activity中定义的参数。        String library = SDLActivity.mSingleton.getMainSharedObject();        //确定运行的朱主函数名称。这里是SDL_main        String function = SDLActivity.mSingleton.getMainFunction();       //我们定义的getArguments 这个在上一遍文章,我们就见过了。并且我们传递了自己的视频路径        String[] arguments = SDLActivity.mSingleton.getArguments();                // 运行nativeRunMain JNI方法        Log.v("SDL", "Running main function " + function + " from library " + library);        SDLActivity.nativeRunMain(library, function, arguments);        Log.v("SDL", "Finished main function");        // Native thread has finished, let's finish the Activity        if (!SDLActivity.mExitCalledFromJava) {            SDLActivity.handleNativeExit();        }    }}

SDLActivity.java::getMainSharedObject()
这个方法会把我们原来写的 main拼接成正确的名字 libmain.so
注意这个函数,是去取数组的最后一个库,作为主函数坐在的库的。所以如果修改传递的时候,一定要小心。我们现在的主函数库就是libmain

    protected String getMainSharedObject() {        String library;        String[] libraries = SDLActivity.mSingleton.getLibraries();        if (libraries.length > 0) {            library = "lib" + libraries[libraries.length - 1] + ".so";        } else {            library = "libmain.so";        }        return getContext().getApplicationInfo().nativeLibraryDir + "/" + library;    }

这里对应的定义了主函数的library和主函数的名称。就是对应了当前项目下的
这个库的名字是我们在CMakeList当中配置的。

CMakeList中的配置

我们这里传入的主函数名称是SDL_Main,在SDL_main.h中,由宏定义的。其实对应的是main方法

SDL_main中的宏 SDL_main指向main.png
项目中 native-lib-su.cpp中对应的主函数名称是 main
.png

进入到nativeRunMain 这个主函数中取看看

/* 开启整个 SDL app的方法,运行在SDLThread当中 */JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv* env, jclass cls, jstring library, jstring function, jobject array){    int status = -1;    const char *library_file;    void *library_handle;    __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeRunMain()");        // 会使用dlopen打开我们传入的library    library_file = (*env)->GetStringUTFChars(env, library, NULL);    library_handle = dlopen(library_file, RTLD_GLOBAL);    if (library_handle) {        const char *function_name;        SDL_main_func SDL_main;        function_name = (*env)->GetStringUTFChars(env, function, NULL);        //并且用dlsym 根据我们的函数库和函数名,打开我们定义的主方法。        SDL_main = (SDL_main_func)dlsym(library_handle, function_name);        if (SDL_main) {            int i;            int argc;            int len;            char **argv;                      //传入参数            /* Prepare the arguments. */            len = (*env)->GetArrayLength(env, array);            argv = SDL_stack_alloc(char*, 1 + len + 1);            argc = 0;            //这里上一遍文章说过,第一个参数是app_process            /* Use the name "app_process" so PHYSFS_platformCalcBaseDir() works.               https://bitbucket.org/MartinFelis/love-android-sdl2/issue/23/release-build-crash-on-start             */            argv[argc++] = SDL_strdup("app_process");            for (i = 0; i < len; ++i) {                const char* utf;                char* arg = NULL;                jstring string = (*env)->GetObjectArrayElement(env, array, i);                if (string) {                    utf = (*env)->GetStringUTFChars(env, string, 0);                    if (utf) {                        arg = SDL_strdup(utf);                        (*env)->ReleaseStringUTFChars(env, string, utf);                    }                    (*env)->DeleteLocalRef(env, string);                }                if (!arg) {                    arg = SDL_strdup("");                }                argv[argc++] = arg;            }            argv[argc] = NULL;                        /*最后开始运行 */            status = SDL_main(argc, argv);            /* Release the arguments. */            for (i = 0; i < argc; ++i) {                SDL_free(argv[i]);            }            SDL_stack_free(argv);        } else {            __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't find function %s in library %s", function_name, library_file);        }        (*env)->ReleaseStringUTFChars(env, function, function_name);        //循环退出之后。dlclose这个库        dlclose(library_handle);    } else {        __android_log_print(ANDROID_LOG_ERROR, "SDL", "nativeRunMain(): Couldn't load library %s", library_file);    }    (*env)->ReleaseStringUTFChars(env, library, library_file);        //这一这里,只是终止的是我们的SDLThread而已。    /* Do not issue an exit or the whole application will terminate instead of just the SDL thread */    /* exit(status); */    return status;}

这样就开始进入到我们自己的
native-lib-su.cpp中的main方法了。

SDL流程

SDL_Init 方法

参考雷博士的图,只保留了视频和Android的部分.png

SDL_InitSubSystem(SDL.c)开始初始化。

  • SDL_TicksInit()
    进入到unix/SDL_systimer.c
    方法只是记录了开始的时间。
    在这个文件中,我们可以看到经常用的SDL_Delay 就在这里。
  • SDL_Delay
voidSDL_Delay(Uint32 ms){    int was_error;#if HAVE_NANOSLEEP    struct timespec elapsed, tv;#else    struct timeval tv;    Uint32 then, now, elapsed;#endif    /* Set the timeout interval */#if HAVE_NANOSLEEP    elapsed.tv_sec = ms / 1000;    elapsed.tv_nsec = (ms % 1000) * 1000000;#else    then = SDL_GetTicks();#endif    do {        errno = 0;#if HAVE_NANOSLEEP        tv.tv_sec = elapsed.tv_sec;        tv.tv_nsec = elapsed.tv_nsec;        was_error = nanosleep(&tv, &elapsed);#else        /* Calculate the time interval left (in case of interrupt) */        now = SDL_GetTicks();        elapsed = (now - then);        then = now;        if (elapsed >= ms) {            break;        }        ms -= elapsed;        tv.tv_sec = ms / 1000;        tv.tv_usec = (ms % 1000) * 1000;        was_error = select(0, NULL, NULL, NULL, &tv);#endif /* HAVE_NANOSLEEP */    } while (was_error && (errno == EINTR));}#endif /* SDL_TIMER_UNIX *//* vi: set ts=4 sw=4 expandtab: */
  • SDL_StartEventLoop
    这个方法中,如果是多线程的话,就会创建互斥锁。
    然后通常的配置是禁用掉。
    下面三种事件
    SDL_EventState(SDL_TEXTINPUT, SDL_DISABLE);    SDL_EventState(SDL_TEXTEDITING, SDL_DISABLE);    SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE);
  • SDL_TimerInit
    简单的理解就是通过SDL_CreateThreadInternal 创建了一个Timer线程。

下面就进入视频系统的初始化

  • SDL_VideoInit(SDL_video.c)
  • Android_InitKeyboardAndroid_InitTouchAndroid_InitMouse
    都是通过JNI方法,分别初始化键盘的数组和touch device Id 和Cursor.(setCustomCursor 对应的是android.view.PointerIcon?)
  • VideoBootStrap->available()VideoBootStrap->create()
    VideoBootStrap,因为在Android平台下,所以对应的是Android_bootstrap分别调用
    在SDL_androidvideo.c中Android_Available方法和Android_CreateDevice方法。
  • Android_Available
    Android_Available方法总是返回1,表示可用。因为只有编译在Android平台时,才会去初始化这个Android_bootstrap。
  • Android_CreateDevice
static SDL_VideoDevice *Android_CreateDevice(int devindex){    SDL_VideoDevice *device;    SDL_VideoData *data;    /* Initialize all variables that we clean on shutdown */    device = (SDL_VideoDevice *) SDL_calloc(1, sizeof(SDL_VideoDevice));    if (!device) {        SDL_OutOfMemory();        return NULL;    }    data = (SDL_VideoData*) SDL_calloc(1, sizeof(SDL_VideoData));    if (!data) {        SDL_OutOfMemory();        SDL_free(device);        return NULL;    }    device->driverdata = data;    /* Set the function pointers */    device->VideoInit = Android_VideoInit;    device->VideoQuit = Android_VideoQuit;    device->PumpEvents = Android_PumpEvents;    device->GetDisplayDPI = Android_GetDisplayDPI;    device->CreateSDLWindow = Android_CreateWindow;    device->SetWindowTitle = Android_SetWindowTitle;    device->SetWindowFullscreen = Android_SetWindowFullscreen;    device->DestroyWindow = Android_DestroyWindow;    device->GetWindowWMInfo = Android_GetWindowWMInfo;    device->free = Android_DeleteDevice;    /* GL pointers */    device->GL_LoadLibrary = Android_GLES_LoadLibrary;    device->GL_GetProcAddress = Android_GLES_GetProcAddress;    device->GL_UnloadLibrary = Android_GLES_UnloadLibrary;    device->GL_CreateContext = Android_GLES_CreateContext;    device->GL_MakeCurrent = Android_GLES_MakeCurrent;    device->GL_SetSwapInterval = Android_GLES_SetSwapInterval;    device->GL_GetSwapInterval = Android_GLES_GetSwapInterval;    device->GL_SwapWindow = Android_GLES_SwapWindow;    device->GL_DeleteContext = Android_GLES_DeleteContext;#if SDL_VIDEO_VULKAN    device->Vulkan_LoadLibrary = Android_Vulkan_LoadLibrary;    device->Vulkan_UnloadLibrary = Android_Vulkan_UnloadLibrary;    device->Vulkan_GetInstanceExtensions = Android_Vulkan_GetInstanceExtensions;    device->Vulkan_CreateSurface = Android_Vulkan_CreateSurface;#endif    /* Screensaver */    device->SuspendScreenSaver = Android_SuspendScreenSaver;    /* Text input */    device->StartTextInput = Android_StartTextInput;    device->StopTextInput = Android_StopTextInput;    device->SetTextInputRect = Android_SetTextInputRect;    /* Screen keyboard */    device->HasScreenKeyboardSupport = Android_HasScreenKeyboardSupport;    device->IsScreenKeyboardShown = Android_IsScreenKeyboardShown;    /* Clipboard */    device->SetClipboardText = Android_SetClipboardText;    device->GetClipboardText = Android_GetClipboardText;    device->HasClipboardText = Android_HasClipboardText;    return device;}

其实可以看到这里如果不是使用Vulcan的话,就使用的是OpenGLes

这些个方法指针,其实提前都被定义好了。
基本上都是GL的方法,其他的都在对应的java类中。最开始初始化的时候,能够看到。

  • SDL_AudioInit
    同样进入SDL_androidaudio.c中。
    ANDROIDAUDIO_Init相关的方法,都在AudioManager当中。暂时省略不提。

其他的暂时不看了。

SDL_Window 初始化

简化只显示Android部分.png

SDL_init之后,我们就创建了SDL_window

在SDL_Init之后,就进入了SDL_CreateWindow .png

  • SDL_CreateWindow 方法

参数含义如下。
title :窗口标题
x :窗口位置x坐标。也可以设置为SDL_WINDOWPOS_CENTERED或SDL_WINDOWPOS_UNDEFINED。
y :窗口位置y坐标。同上。
w :窗口的宽
h :窗口的高
flags :支持下列标识。包括了窗口的是否最大化、最小化,能否调整边界等等属性。
::SDL_WINDOW_FULLSCREEN, ::SDL_WINDOW_OPENGL,
::SDL_WINDOW_HIDDEN, ::SDL_WINDOW_BORDERLESS,
::SDL_WINDOW_RESIZABLE, ::SDL_WINDOW_MAXIMIZED,
::SDL_WINDOW_MINIMIZED, ::SDL_WINDOW_INPUT_GRABBED,
::SDL_WINDOW_ALLOW_HIGHDPI.
返回创建完成的窗口的ID。如果创建失败则返回0。

会进入SDL_androidwindow.c中去创建Window
给window 设置上各种Flag
会通过SDLSurface中的getNativeSurface,然后通过ANativeWindow_fromSurface来获取一个NativeWindow。

SDL_EGL_CreateSurface 然后创建传递给OpenGL 作为Surface
在SDL_egl.c中

EGLSurface *SDL_EGL_CreateSurface(_THIS, NativeWindowType nw) {    /* max 2 values plus terminator. */    EGLint attribs[3];    int attr = 0;    EGLSurface * surface;    if (SDL_EGL_ChooseConfig(_this) != 0) {        return EGL_NO_SURFACE;    }    #if SDL_VIDEO_DRIVER_ANDROID    {        /* Android docs recommend doing this!         * Ref: http://developer.android.com/reference/android/app/NativeActivity.html          */        EGLint format;        _this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display,                                            _this->egl_data->egl_config,                                             EGL_NATIVE_VISUAL_ID, &format);        ANativeWindow_setBuffersGeometry(nw, 0, 0, format);    }#endif        if (_this->gl_config.framebuffer_srgb_capable) {#ifdef EGL_KHR_gl_colorspace        if (SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_KHR_gl_colorspace")) {            attribs[attr++] = EGL_GL_COLORSPACE_KHR;            attribs[attr++] = EGL_GL_COLORSPACE_SRGB_KHR;        } else#endif        {            SDL_SetError("EGL implementation does not support sRGB system framebuffers");            return EGL_NO_SURFACE;        }    }    attribs[attr++] = EGL_NONE;        surface = _this->egl_data->eglCreateWindowSurface(            _this->egl_data->egl_display,            _this->egl_data->egl_config,            nw, &attribs[0]);    if (surface == EGL_NO_SURFACE) {        SDL_EGL_SetError("unable to create an EGL window surface", "eglCreateWindowSurface");    }    return surface;}

可以看到这个过程中,创建OpenGL的步骤其实和java中创建基本上一样的。
额外需要做的是,NativeWindow需要ANativeWindow_setBuffersGeometry 方法,先设置一次Buffer的大小。

接下来是
SDL_SetWindowTitle
也是调用SDLActivity中的方法

接下来是SDL _FinishWindowCreation

static voidSDL_FinishWindowCreation(SDL_Window *window, Uint32 flags){    PrepareDragAndDropSupport(window);    if (flags & SDL_WINDOW_MAXIMIZED) {        SDL_MaximizeWindow(window);    }    if (flags & SDL_WINDOW_MINIMIZED) {        SDL_MinimizeWindow(window);    }    if (flags & SDL_WINDOW_FULLSCREEN) {        SDL_SetWindowFullscreen(window, flags);    }    if (flags & SDL_WINDOW_INPUT_GRABBED) {        SDL_SetWindowGrab(window, SDL_TRUE);    }    if (!(flags & SDL_WINDOW_HIDDEN)) {        SDL_ShowWindow(window);    }}

其实就是设置window的各种状态
这几个方法,android中基本上都没有。只有设置SetWindowFullscreen 有对应的方法
先会去调用setWindowStyle方法,然后修改nativeWindow 的大小。

SDL_androidwindow.c

voidAndroid_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen){    /* If the window is being destroyed don't change visible state */    if (!window->is_destroying) {        Android_JNI_SetWindowStyle(fullscreen);    }    /* Ensure our size matches reality after we've executed the window style change.     *     * It is possible that we've set width and height to the full-size display, but on     * Samsung DeX or Chromebooks or other windowed Android environemtns, our window may      * still not be the full display size.     */    if (!SDL_IsDeXMode() && !SDL_IsChromebook()) {        return;    }    SDL_WindowData * data = (SDL_WindowData *)window->driverdata;    if (!data || !data->native_window) {        return;    }    int old_w = window->w;    int old_h = window->h;    int new_w = ANativeWindow_getWidth(data->native_window);    int new_h = ANativeWindow_getHeight(data->native_window);    if (old_w != new_w || old_h != new_h) {        SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, new_w, new_h);    }}

SDL_window 结构

/** *  \brief The type used to identify a window * *  \sa SDL_CreateWindow() *  \sa SDL_CreateWindowFrom() *  \sa SDL_DestroyWindow() *  \sa SDL_GetWindowData() *  \sa SDL_GetWindowFlags() *  \sa SDL_GetWindowGrab() *  \sa SDL_GetWindowPosition() *  \sa SDL_GetWindowSize() *  \sa SDL_GetWindowTitle() *  \sa SDL_HideWindow() *  \sa SDL_MaximizeWindow() *  \sa SDL_MinimizeWindow() *  \sa SDL_RaiseWindow() *  \sa SDL_RestoreWindow() *  \sa SDL_SetWindowData() *  \sa SDL_SetWindowFullscreen() *  \sa SDL_SetWindowGrab() *  \sa SDL_SetWindowIcon() *  \sa SDL_SetWindowPosition() *  \sa SDL_SetWindowSize() *  \sa SDL_SetWindowBordered() *  \sa SDL_SetWindowResizable() *  \sa SDL_SetWindowTitle() *  \sa SDL_ShowWindow() */typedef struct SDL_Window SDL_Window;
typedef struct{    EGLSurface egl_surface;    EGLContext egl_context; /* We use this to preserve the context when losing focus */    ANativeWindow* native_window;    } SDL_WindowData;
  1. 渲染器的初始化


    SDL_CreateRenderer.png

SDL_CreateRenderer

/** *  \brief Create a 2D rendering context for a window. * *  \param window The window where rendering is displayed. *  \param index    The index of the rendering driver to initialize, or -1 to *                  initialize the first one supporting the requested flags. *  \param flags    ::SDL_RendererFlags. * *  \return A valid rendering context or NULL if there was an error. * *  \sa SDL_CreateSoftwareRenderer() *  \sa SDL_GetRendererInfo() *  \sa SDL_DestroyRenderer() */
  1. 判断是否已经被连接了window 了
  2. 同样去driver中,去得到争取的Renderer
    可以看到Android中,用的是GLRenderer
#if SDL_VIDEO_RENDER_OGL_ES2    &GLES2_RenderDriver,#endif#if SDL_VIDEO_RENDER_OGL_ES    &GLES_RenderDriver,#endif

GLES2_RenderDriver

SDL_RenderDriver GLES2_RenderDriver = {    GLES2_CreateRenderer,    {        "opengles2",        (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE),        4,        {        SDL_PIXELFORMAT_ARGB8888,        SDL_PIXELFORMAT_ABGR8888,        SDL_PIXELFORMAT_RGB888,        SDL_PIXELFORMAT_BGR888        },        0,        0    }};

SDL_render_gles2 中
GLES2_CreateRenderer 方法

static SDL_Renderer *GLES2_CreateRenderer(SDL_Window *window, Uint32 flags){    SDL_Renderer *renderer;    GLES2_DriverContext *data;    GLint nFormats;#ifndef ZUNE_HD    GLboolean hasCompiler;#endif    Uint32 window_flags = 0; /* -Wconditional-uninitialized */    GLint window_framebuffer;    GLint value;    int profile_mask = 0, major = 0, minor = 0;    SDL_bool changed_window = SDL_FALSE;    if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profile_mask) < 0) {        goto error;    }    if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major) < 0) {        goto error;    }    if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor) < 0) {        goto error;    }    window_flags = SDL_GetWindowFlags(window);    /* OpenGL ES 3.0 is a superset of OpenGL ES 2.0 */    if (!(window_flags & SDL_WINDOW_OPENGL) ||        profile_mask != SDL_GL_CONTEXT_PROFILE_ES || major < RENDERER_CONTEXT_MAJOR) {        changed_window = SDL_TRUE;        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RENDERER_CONTEXT_MAJOR);        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RENDERER_CONTEXT_MINOR);        if (SDL_RecreateWindow(window, window_flags | SDL_WINDOW_OPENGL) < 0) {            goto error;        }    }    /* Create the renderer struct */    renderer = (SDL_Renderer *)SDL_calloc(1, sizeof(SDL_Renderer));    if (!renderer) {        SDL_OutOfMemory();        goto error;    }    data = (GLES2_DriverContext *)SDL_calloc(1, sizeof(GLES2_DriverContext));    if (!data) {        GLES2_DestroyRenderer(renderer);        SDL_OutOfMemory();        goto error;    }    renderer->info = GLES2_RenderDriver.info;    renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);    renderer->driverdata = data;    renderer->window = window;    /* Create an OpenGL ES 2.0 context */    data->context = SDL_GL_CreateContext(window);    if (!data->context) {        GLES2_DestroyRenderer(renderer);        goto error;    }    if (SDL_GL_MakeCurrent(window, data->context) < 0) {        GLES2_DestroyRenderer(renderer);        goto error;    }    if (GLES2_LoadFunctions(data) < 0) {        GLES2_DestroyRenderer(renderer);        goto error;    }#if __WINRT__    /* DLudwig, 2013-11-29: ANGLE for WinRT doesn't seem to work unless VSync     * is turned on.  Not doing so will freeze the screen's contents to that     * of the first drawn frame.     */    flags |= SDL_RENDERER_PRESENTVSYNC;#endif    if (flags & SDL_RENDERER_PRESENTVSYNC) {        SDL_GL_SetSwapInterval(1);    } else {        SDL_GL_SetSwapInterval(0);    }    if (SDL_GL_GetSwapInterval() > 0) {        renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;    }    /* Check for debug output support */    if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_FLAGS, &value) == 0 &&        (value & SDL_GL_CONTEXT_DEBUG_FLAG)) {        data->debug_enabled = SDL_TRUE;    }    value = 0;    data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);    renderer->info.max_texture_width = value;    value = 0;    data->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &value);    renderer->info.max_texture_height = value;    /* Determine supported shader formats */    /* HACK: glGetInteger is broken on the Zune HD's compositor, so we just hardcode this */#ifdef ZUNE_HD    nFormats = 1;#else /* !ZUNE_HD */    data->glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &nFormats);    data->glGetBooleanv(GL_SHADER_COMPILER, &hasCompiler);    if (hasCompiler) {        ++nFormats;    }#endif /* ZUNE_HD */    data->shader_formats = (GLenum *)SDL_calloc(nFormats, sizeof(GLenum));    if (!data->shader_formats) {        GLES2_DestroyRenderer(renderer);        SDL_OutOfMemory();        goto error;    }    data->shader_format_count = nFormats;#ifdef ZUNE_HD    data->shader_formats[0] = GL_NVIDIA_PLATFORM_BINARY_NV;#else /* !ZUNE_HD */    data->glGetIntegerv(GL_SHADER_BINARY_FORMATS, (GLint *)data->shader_formats);    if (hasCompiler) {        data->shader_formats[nFormats - 1] = (GLenum)-1;    }#endif /* ZUNE_HD */    data->framebuffers = NULL;    data->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &window_framebuffer);    data->window_framebuffer = (GLuint)window_framebuffer;    /* Populate the function pointers for the module */    renderer->WindowEvent         = GLES2_WindowEvent;    renderer->GetOutputSize       = GLES2_GetOutputSize;    renderer->SupportsBlendMode   = GLES2_SupportsBlendMode;    renderer->CreateTexture       = GLES2_CreateTexture;    renderer->UpdateTexture       = GLES2_UpdateTexture;    renderer->UpdateTextureYUV    = GLES2_UpdateTextureYUV;    renderer->LockTexture         = GLES2_LockTexture;    renderer->UnlockTexture       = GLES2_UnlockTexture;    renderer->SetRenderTarget     = GLES2_SetRenderTarget;    renderer->UpdateViewport      = GLES2_UpdateViewport;    renderer->UpdateClipRect      = GLES2_UpdateClipRect;    renderer->RenderClear         = GLES2_RenderClear;    renderer->RenderDrawPoints    = GLES2_RenderDrawPoints;    renderer->RenderDrawLines     = GLES2_RenderDrawLines;    renderer->RenderFillRects     = GLES2_RenderFillRects;    renderer->RenderCopy          = GLES2_RenderCopy;    renderer->RenderCopyEx        = GLES2_RenderCopyEx;    renderer->RenderReadPixels    = GLES2_RenderReadPixels;    renderer->RenderPresent       = GLES2_RenderPresent;    renderer->DestroyTexture      = GLES2_DestroyTexture;    renderer->DestroyRenderer     = GLES2_DestroyRenderer;    renderer->GL_BindTexture      = GLES2_BindTexture;    renderer->GL_UnbindTexture    = GLES2_UnbindTexture;    renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12;    renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV;    renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_NV12;    renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_NV21;#ifdef GL_TEXTURE_EXTERNAL_OES    renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_EXTERNAL_OES;#endif    GLES2_ResetState(renderer);    return renderer;error:    if (changed_window) {        /* Uh oh, better try to put it back... */        SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile_mask);        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);        SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);        SDL_RecreateWindow(window, window_flags);    }    return NULL;}#endif /* SDL_VIDEO_RENDER_OGL_ES2 && !SDL_RENDER_DISABLED */

经过一系列对window flag的判断,最终走到了创建GLContext 和 MakeCurrent 上
GLES2_LoadFunctions

接着设置刷新率
SDL_GL_SetSwapInterval 如果是跟着设备的v ysn同步信号的话,就是1 否则是0

最后将各种renderer中的各种gl方法,进行初始化。
GLES2_CreateTexture 方法,是创建纹理的方法,可以看到,确实是熟悉的问题,创建纹理的过程。不同的是,如果是yuv就会创建3个纹理 。

static intGLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture){    GLES2_DriverContext *renderdata = (GLES2_DriverContext *)renderer->driverdata;    GLES2_TextureData *data;    GLenum format;    GLenum type;    GLenum scaleMode;    GLES2_ActivateRenderer(renderer);    /* Determine the corresponding GLES texture format params */    switch (texture->format)    {    case SDL_PIXELFORMAT_ARGB8888:    case SDL_PIXELFORMAT_ABGR8888:    case SDL_PIXELFORMAT_RGB888:    case SDL_PIXELFORMAT_BGR888:        format = GL_RGBA;        type = GL_UNSIGNED_BYTE;        break;    case SDL_PIXELFORMAT_IYUV:    case SDL_PIXELFORMAT_YV12:    case SDL_PIXELFORMAT_NV12:    case SDL_PIXELFORMAT_NV21:        format = GL_LUMINANCE;        type = GL_UNSIGNED_BYTE;        break;#ifdef GL_TEXTURE_EXTERNAL_OES    case SDL_PIXELFORMAT_EXTERNAL_OES:        format = GL_NONE;        type = GL_NONE;        break;#endif    default:        return SDL_SetError("Texture format not supported");    }    if (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES &&        texture->access != SDL_TEXTUREACCESS_STATIC) {        return SDL_SetError("Unsupported texture access for SDL_PIXELFORMAT_EXTERNAL_OES");    }    /* Allocate a texture struct */    data = (GLES2_TextureData *)SDL_calloc(1, sizeof(GLES2_TextureData));    if (!data) {        return SDL_OutOfMemory();    }    data->texture = 0;#ifdef GL_TEXTURE_EXTERNAL_OES    data->texture_type = (texture->format == SDL_PIXELFORMAT_EXTERNAL_OES) ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;#else    data->texture_type = GL_TEXTURE_2D;#endif    data->pixel_format = format;    data->pixel_type = type;    data->yuv = ((texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12));    data->nv12 = ((texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21));    data->texture_u = 0;    data->texture_v = 0;    scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GL_NEAREST : GL_LINEAR;    /* Allocate a blob for image renderdata */    if (texture->access == SDL_TEXTUREACCESS_STREAMING) {        size_t size;        data->pitch = texture->w * SDL_BYTESPERPIXEL(texture->format);        size = texture->h * data->pitch;        if (data->yuv) {            /* Need to add size for the U and V planes */            size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);        }        if (data->nv12) {            /* Need to add size for the U/V plane */            size += 2 * ((texture->h + 1) / 2) * ((data->pitch + 1) / 2);        }        data->pixel_data = SDL_calloc(1, size);        if (!data->pixel_data) {            SDL_free(data);            return SDL_OutOfMemory();        }    }    /* Allocate the texture */    GL_CheckError("", renderer);    if (data->yuv) {        renderdata->glGenTextures(1, &data->texture_v);        if (GL_CheckError("glGenTexures()", renderer) < 0) {            return -1;        }        renderdata->glActiveTexture(GL_TEXTURE2);        renderdata->glBindTexture(data->texture_type, data->texture_v);        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode);        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode);        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);        renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL);        renderdata->glGenTextures(1, &data->texture_u);        if (GL_CheckError("glGenTexures()", renderer) < 0) {            return -1;        }        renderdata->glActiveTexture(GL_TEXTURE1);        renderdata->glBindTexture(data->texture_type, data->texture_u);        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode);        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode);        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);        renderdata->glTexImage2D(data->texture_type, 0, format, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, format, type, NULL);        if (GL_CheckError("glTexImage2D()", renderer) < 0) {            return -1;        }    }    if (data->nv12) {        renderdata->glGenTextures(1, &data->texture_u);        if (GL_CheckError("glGenTexures()", renderer) < 0) {            return -1;        }        renderdata->glActiveTexture(GL_TEXTURE1);        renderdata->glBindTexture(data->texture_type, data->texture_u);        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode);        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode);        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);        renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);        renderdata->glTexImage2D(data->texture_type, 0, GL_LUMINANCE_ALPHA, (texture->w + 1) / 2, (texture->h + 1) / 2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, NULL);        if (GL_CheckError("glTexImage2D()", renderer) < 0) {            return -1;        }    }    renderdata->glGenTextures(1, &data->texture);    if (GL_CheckError("glGenTexures()", renderer) < 0) {        return -1;    }    texture->driverdata = data;    renderdata->glActiveTexture(GL_TEXTURE0);    renderdata->glBindTexture(data->texture_type, data->texture);    renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MIN_FILTER, scaleMode);    renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, scaleMode);    renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);    renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);    if (texture->format != SDL_PIXELFORMAT_EXTERNAL_OES) {        renderdata->glTexImage2D(data->texture_type, 0, format, texture->w, texture->h, 0, format, type, NULL);        if (GL_CheckError("glTexImage2D()", renderer) < 0) {            return -1;        }    }    if (texture->access == SDL_TEXTUREACCESS_TARGET) {       data->fbo = GLES2_GetFBO(renderer->driverdata, texture->w, texture->h);    } else {       data->fbo = NULL;    }    return GL_CheckError("", renderer);}

update

static intGLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect,                    const void *pixels, int pitch){    GLES2_DriverContext *data = (GLES2_DriverContext *)renderer->driverdata;    GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata;    GLES2_ActivateRenderer(renderer);    /* Bail out if we're supposed to update an empty rectangle */    if (rect->w <= 0 || rect->h <= 0) {        return 0;    }    /* Create a texture subimage with the supplied data */    data->glBindTexture(tdata->texture_type, tdata->texture);    GLES2_TexSubImage2D(data, tdata->texture_type,                    rect->x,                    rect->y,                    rect->w,                    rect->h,                    tdata->pixel_format,                    tdata->pixel_type,                    pixels, pitch, SDL_BYTESPERPIXEL(texture->format));    if (tdata->yuv) {        /* Skip to the correct offset into the next texture */        pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);        if (texture->format == SDL_PIXELFORMAT_YV12) {            data->glBindTexture(tdata->texture_type, tdata->texture_v);        } else {            data->glBindTexture(tdata->texture_type, tdata->texture_u);        }        GLES2_TexSubImage2D(data, tdata->texture_type,                rect->x / 2,                rect->y / 2,                (rect->w + 1) / 2,                (rect->h + 1) / 2,                tdata->pixel_format,                tdata->pixel_type,                pixels, (pitch + 1) / 2, 1);        /* Skip to the correct offset into the next texture */        pixels = (const void*)((const Uint8*)pixels + ((rect->h + 1) / 2) * ((pitch + 1)/2));        if (texture->format == SDL_PIXELFORMAT_YV12) {            data->glBindTexture(tdata->texture_type, tdata->texture_u);        } else {            data->glBindTexture(tdata->texture_type, tdata->texture_v);        }        GLES2_TexSubImage2D(data, tdata->texture_type,                rect->x / 2,                rect->y / 2,                (rect->w + 1) / 2,                (rect->h + 1) / 2,                tdata->pixel_format,                tdata->pixel_type,                pixels, (pitch + 1) / 2, 1);    }    if (tdata->nv12) {        /* Skip to the correct offset into the next texture */        pixels = (const void*)((const Uint8*)pixels + rect->h * pitch);        data->glBindTexture(tdata->texture_type, tdata->texture_u);        GLES2_TexSubImage2D(data, tdata->texture_type,                rect->x / 2,                rect->y / 2,                (rect->w + 1) / 2,                (rect->h + 1) / 2,                GL_LUMINANCE_ALPHA,                GL_UNSIGNED_BYTE,                pixels, 2 * ((pitch + 1) / 2), 2);    }    return GL_CheckError("glTexSubImage2D()", renderer);}

GLES2_TexSubImage2D 方法

static intGLES2_TexSubImage2D(GLES2_DriverContext *data, GLenum target, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels, GLint pitch, GLint bpp){    Uint8 *blob = NULL;    Uint8 *src;    int src_pitch;    int y;    if ((width == 0) || (height == 0) || (bpp == 0)) {        return 0;  /* nothing to do */    }    /* Reformat the texture data into a tightly packed array */    src_pitch = width * bpp;    src = (Uint8 *)pixels;    if (pitch != src_pitch) {        blob = (Uint8 *)SDL_malloc(src_pitch * height);        if (!blob) {            return SDL_OutOfMemory();        }        src = blob;        for (y = 0; y < height; ++y)        {            SDL_memcpy(src, pixels, src_pitch);            src += src_pitch;            pixels = (Uint8 *)pixels + pitch;        }        src = blob;    }    data->glTexSubImage2D(target, 0, xoffset, yoffset, width, height, format, type, src);    if (blob) {        SDL_free(blob);    }    return 0;}

GLES2_UpdateTextureYUV 方法中,就是减少了判断。

SDL_RenderCopy 方法会将render 中的参数,使用texture来进行绘制。
GLES2_SetupCopy-》GLES2_SelectProgram

这个过程中,会进行创建Shader Fragment着色器,创建program 来进行使用。
同时绑定纹理,坐标系和blendMode 做好绘制的准备

GLES2_RenderReadPixels 读取像素

GLES2_RenderPresent 调用Swap方法 SwapBuffers

  1. 纹理SDL_Texture


    image.png
/** *  \brief Create a texture for a rendering context. * *  \param renderer The renderer. *  \param format The format of the texture. *  \param access One of the enumerated values in ::SDL_TextureAccess. *  \param w      The width of the texture in pixels. *  \param h      The height of the texture in pixels. * *  \return The created texture is returned, or NULL if no rendering context was *          active,  the format was unsupported, or the width or height were out *          of range. * *  \note The contents of the texture are not defined at creation. * *  \sa SDL_QueryTexture() *  \sa SDL_UpdateTexture() *  \sa SDL_DestroyTexture() */

SDL循环渲染数据

update_circle.png

总结


image.png

参考

雷神SDL2源代码分析系列

更多相关文章

  1. Android四种联网方式
  2. Android中对话框(Dialog)的创建方法
  3. Android窗口机制之由setContentView引发的Window,PhoneWindow,Deco
  4. Android完全关闭应用程序
  5. Android官方架构组件ViewModel+LiveData+DataBinding架构属于自
  6. Android(安卓)MediaPlayer 字幕同步
  7. Android(安卓)8.1 来电默认全屏显示 如何修改
  8. Android(安卓)Studio finish()方法的使用与解决app点击“返回”,
  9. Android(安卓)EventBus 架构设计

随机推荐

  1. .NET和Android解压缩处理
  2. Android(安卓)读取并显示通讯录
  3. Android(安卓)时间 日期 时区
  4. Android(安卓)显示网页图片
  5. 20150618_Andriod_设置TextView垂直滚动
  6. Android代码实现飞行模式的打开
  7. Android(安卓)app的登录和注册功能
  8. Android实现对imageview的拖动以及缩放
  9. Android通过手势实现的缩放处理
  10. Android(安卓)Robotium的自动化代码