【问题标题】:Native Android multi-threaded odd occurrence原生 Android 多线程奇数发生
【发布时间】:2014-12-06 20:25:09
【问题描述】:

我一直在开发本机 Android /NDK 级别的游戏。一开始我只有一个纹理,但是当我的纹理达到 5 时,我的 fps 从 60 左右慢慢降低到 20 左右(有口吃)。

目前我在一个线程上执行我的所有操作。在引入另一个使用带有 start_routine 的 posix 线程(无限循环并且没有实现)的线程时,我的 fps 似乎无缘无故地达到了 40 左右。

这里的另一点是,在引入该线程后,FPS 稳定在 42-43。但如果没有线程,就会出现口吃 (18-28 fps) 导致动画生涩的情况。 我的疑惑:

  1. 为什么会发生上述情况(线程相关)?
  2. 另外,与我使用 1 个纹理时的唯一区别是我的片段着色器中的计算现在更多了。这是否意味着 GPU 过载,因此 glSwapBuffers 需要更多时间?
  3. 假设 glSwapBuffers 确实需要时间,这是否意味着我的游戏逻辑总是领先于我的渲染器?
  4. 究竟如何为渲染线程提供渲染帧所需的信息?就像我让渲染线程等待由我的游戏逻辑线程提供的队列一样? (代码相关)

代码:

void * start_render (void * param)
{

    while (1) {

    }

    return NULL;
}

void android_main(struct android_app* state) {


    // Creation of this thread, increased my FPS to around 40 even though start_render wasnt doing     anything

    pthread_t renderthread;
    pthread_create(&renderthread,NULL,start_render,NULL);


    struct engine engine;

    memset(&engine, 0, sizeof(engine));
    state->userData = &engine;
    state->onAppCmd = engine_handle_cmd;
    state->onInputEvent = engine_handle_input;


    engine.assetManager = state->activity->assetManager;

    engine.app = state;
    engine.texsize = 4;


    if (state->savedState != NULL) {
        // We are starting with a previous saved state; restore from it.
        engine.state = *(struct saved_state*)state->savedState;
    }

    // loop waiting for stuff to do.

    while (1) {
        // Read all pending events.
        int ident;
        int events;
        struct android_poll_source* source;

        // If not animating, we will block forever waiting for events.
        // If animating, we loop until all events are read, then continue
        // to draw the next frame of animation.
        while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events,
                (void**)&source)) >= 0) {

            // Process this event.
            if (source != NULL) {
                source->process(state, source);
            }


            // Check if we are exiting.
            if (state->destroyRequested != 0) {
                engine_term_display(&engine);
                return;
            }
        }

        if (engine.animating) {



                for (int i = 0; i < 4;i++)
                {
                    float cur = engine.mytextures[i].currentposition;

                    if (cur < 1.0)
                        engine.mytextures[i].currentposition = cur + engine.mytextures[i].relativespeed;
                    else
                        engine.mytextures[i].currentposition = cur - 1.0;
                }

            // How do i enable the render thread (created above) to call the below function?
            on_draw_frame(&engine);




        }
    }
}

void on_draw_frame(engine * engine) {

    glUseProgram(program);

    engine->texsize = 4;

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, engine->mytextures[0].textureid);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, engine->mytextures[1].textureid);

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, engine->mytextures[2].textureid);

    glActiveTexture(GL_TEXTURE3);
    glBindTexture(GL_TEXTURE_2D, engine->mytextures[3].textureid);

    glUniform1i(u_texture_unit_location1,0);
    glUniform1i(u_texture_unit_location2,1);
    glUniform1i(u_texture_unit_location3,2);
    glUniform1i(u_texture_unit_location4,3);


    glUniform1f(timeCoord1,engine->mytextures[0].currentposition);
    glUniform1f(timeCoord2,engine->mytextures[1].currentposition);
    glUniform1f(timeCoord3,engine->mytextures[2].currentposition);
    glUniform1f(timeCoord4,engine->mytextures[3].currentposition);



    glUniform1i(texSize,engine->texsize);

    glBindBuffer(GL_ARRAY_BUFFER, buffer);

    glVertexAttribPointer(a_position_location, 2, GL_FLOAT, GL_FALSE,
        4 * sizeof(GL_FLOAT), BUFFER_OFFSET(0));
    glVertexAttribPointer(a_texture_coordinates_location, 2, GL_FLOAT, GL_FALSE,
        4 * sizeof(GL_FLOAT), BUFFER_OFFSET(2 * sizeof(GL_FLOAT)));
    glEnableVertexAttribArray(a_position_location);
    glEnableVertexAttribArray(a_texture_coordinates_location);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    eglSwapBuffers(engine->display, engine->surface);


    // FPS calculation
    if (fps == 0)
        clock_gettime(CLOCK_MONOTONIC, &starttime);
    else
        clock_gettime (CLOCK_MONOTONIC,&stoptime);

    if (stoptime.tv_sec - starttime.tv_sec == 1) {

        __android_log_print(ANDROID_LOG_VERBOSE, "GAME", "FPS %d",fps);
        fps = 0;

    } else
        fps++;

}

如果您需要有关代码的更多信息,请告诉我。

【问题讨论】:

  • 只是出于好奇:你在什么设备上测量这个?
  • 我使用的是 nexus 4..

标签: android c++ multithreading opengl-es


【解决方案1】:

我不能完全确定,但这看起来很像电源管理对设备的不良影响。

您描述的症状可能是由侧重于 CPU 使用率的电源管理策略引起的。使用这样的策略,如果您的 CPU 使用率非常低(因为您主要受 GPU 限制),整个系统可能会进入较低的功耗状态,并有效地减慢 GPU 的速度,即使 GPU 已完全运行已加载。

在这种情况下,当您通过启动另一个消耗 CPU 时间的线程来增加额外的 CPU 负载时​​,您会将系统保持在更高的电源状态,并让 GPU 运行得更快。

这种电源管理完全被打破了,恕我直言。如果仅仅因为 CPU 利用率低而使 GPU 完全忙碌,那么减慢 GPU 的速度对我来说没有任何意义。但是有些设备上的电源管理非常原始,所以这种行为并不少见。

如果这确实是您的问题,那么作为应用程序开发人员,除了提交错误之外,您无能为力。创建人工 CPU 负载来解决它当然不能令人满意。使用更多的权力来打败电源管理并不是你想要的。许多游戏可能会产生大量 CPU 负载来处理其游戏逻辑/物理,因此它们不会受到影响。

【讨论】:

    猜你喜欢
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-05
    相关资源
    最近更新 更多