1

我正在用Android NDK和opengl es 2.0为Android开发一个小游戏引擎,最近项目变大了,我需要重构一些代码,我找不到下一个问题的合适设计模式。

在 android 上,当应用程序达到 OnPause() 状态时,opengl 上下文被销毁,但 java 和 c++ 中的变量和对象的状态保持不变。所以每次播放器暂停并恢复应用程序时,我都必须重新初始化 opengl 部分、缓冲区、着色器、顶点等。

我有像“Square”这样制作“方形对象”的类,每个都有自己的属性,并且每个“方形对象”都可以绘制,因此方形可以访问使用的类的静态(opengl)成员被正确渲染。所以这个静态成员必须在绘制对象之前初始化,我在创建或重新创建opengl上下文时这样做。

此外,每个类都有自己的opengl属性,所以每个类都用自己的参数单独初始化,所以我想要一个设计,每个类可以设置一些初始参数,传递或捕获这些参数来初始化类的静态成员(我忘了说这些参数是私有的)。但正如我之前所说,每次应用程序恢复时都需要重新初始化这些参数。

目前我像这样单独初始化这些成员

Square::init(/*hardcoded parameters*/);
Circle::init(/*hardcoded parameters*/);
Triangle::init(/*hardcoded parameters*/);
Polygon::init(/*hardcoded parameters*/);
Shape::init(/*hardcoded parameters*/);
.
.
.
.
// Many other inits.....
.

我想写一些类似的东西

// here all the classes with opengl part are initialized
// all init methods of each class are called here, with their respective parameters
Opengl_Initializer::init(); // <--- magic way, no other init calls

所以我想为类设置一些(静态/硬编码)变量,然后在创建 opengl 上下文时,以“魔术”方式初始化该类,并且不需要为每个调用编写一个 init 方法班级。

我尝试使用继承,但问题是我需要初始化类而不是对象,还尝试实现一个静态对象并在 cpp 文件中初始化该对象,并将指向该对象的指针存储在向量中这是在他的构造器中创建的,在对象自己的类中的向量中,但是这种设计给我带来了很多问题。

有谁知道一些可以帮助我的设计?

编辑:我的班级结构

init()函数非常大,因为shader参数frag是路径文件,我对它们执行一些任务,将执行的结果传递给 opengl 并返回我一个ID程序静态变量,所有带有 opengl 部分的类都实现了相同的过程,参数camera只是将它附加到相机中

class Square {
    // static variable all classes have
    static GLuint program;

    // other glparameters not in common, initialized in the same static init() method
    static GLint uniform1;
    static GLint uniform2;

public;
    // the static init function has the same header on all the classes
    static init(const char* shader, const char* frag, const char *camera);
}

也许我想要的一些结构是

class Square {
    static GLuint program;
    static const char *vertex = "hardcode";
    static const char *frag = "hardcode";
    static const char *cam = "harcode";

    static init();

    /// or somethig like
    static Initializer init(
            "harcode shader", "hardcode frag", "hardcode camera", 
            [&] (void) ->void {
                //this is the init function
            }
    );

public:

}
4

2 回答 2

1

我建议使用版本控制系统,以便可以在使用时自动执行初始化,但在初始化已经完成时以一种非常便宜的方式跳过它。就像是

int global_gl_generation = 0; // increment each time you recreate the context

inline bool check_gl_generation(int& local_generation)
{
    if (local_generation == global_gl_generation)
        return false;

    local_generation = global_gl_generation;
    return true;
}

然后在每一堂课中,

class Square
{
    // static variable all classes have
    static int generation_inited;

    static GLuint program;
    static GLint uniform1;
    static GLint uniform2;

    static init(const char* shader, const char* frag, const char *camera);

public;
    void draw() override
    {
         if (check_gl_generation(generation_inited)) init(...);

         // use program, uniform1, uniform2
    }
};
于 2016-10-22T16:49:30.113 回答
1

这是如何解决您的任务的另一种解决方案。这个想法是有一些应该在你的 Opengl_Initializer::init() 中调用的函数的初始化列表(std::vector):

std::vector<std::function<void()>> initializer_list;

如果我们可以将您所有的 Square/Circle/Triangle... init 函数放入此列表中,您的任务将变得微不足道 - 只需迭代列表并调用所有函数:

// inside Opengl_Initializer::init()
for (auto fn : initializer_list)
   fn();

您可以手动添加函数,例如,从 int main():

initializer_list.push_back(&Square::init); 
...

但我建议您需要一些架构设计,使您能够在不更改主代码或任何其他全局代码的情况下将函数添加到初始化列表中。为了解决这个任务,我们可以创建一个小的帮助类来自动注册你的 init 函数:

struct OpenGLHelper_initializer
{
    OpenGLHelper_initializer(std::function<void()> fn)
    {
        initializer_list.push_back(fn);
    }
};

所以你可以在你的 Square/Circle 中声明这个类的实例:

   struct Square
   {
      static OpenGLHelper_initializer __initializer;
   };

在您的 Square.cpp 文件中:

OpenGLHelper_initializer Square::__initializer(&Square::init);

因此,当程序加载时,所有这些初始化程序都将被构造,并且所有“init”函数都将注册到 initializer_list 中。

这看起来像更多代码,但它可以让您添加任意数量的形状,而无需更改 Opengl_Initializer::init(); 或 main.cpp 或任何其他全局代码

如果您不喜欢初始化函数并使用 lambda,您现在可以删除它们:

// in square.cpp
OpenGLHelper_initializer Square::__initializer([](){
  std::cout << "Square is initialized now" << std::endl; 
});

这是完整的源代码(使用静态函数更新)(但没有 cpp 文件 - 多合一):

#include <iostream>
#include <memory>
#include <vector>

using namespace std;


/////////////////////////////////////////
// opengl_helper.h
// this is some manager class that knows what should be initialized later
struct OpenGLHelper
{
    typedef std::function<void()> function_type;

    static std::vector<function_type>& get_initialization_list();

    static void register_initializer(function_type fn);

    static void run_init();
};
// helper class that will register some function at construction time
struct OpenGLHelper_initializer
{
    OpenGLHelper_initializer(OpenGLHelper::function_type fn)
    {
        OpenGLHelper::register_initializer(fn);
    }
};
/////////////////////////////////////////
//opengl_helper.cpp

// using this function we will make our initializer_list be constructued
// before adding anything into it
std::vector<OpenGLHelper::function_type>& OpenGLHelper::get_initialization_list()
{
    static std::vector<function_type> initializer_list;
    return initializer_list;    
}

// function that puts initializer into a list. 
void OpenGLHelper::register_initializer(OpenGLHelper::function_type fn)
{

    get_initialization_list().push_back(fn);
}

void OpenGLHelper::run_init()
{
    for (auto fn : get_initialization_list())
        fn();
}

/////////////////////////////////////////
// figure.h
// here is sample class that will be registered for initialization
struct Square
{
    static int to_be_initialized;

    // static member that will register Square class to be initialized
    static OpenGLHelper_initializer __initializer;
};

/////////////////////////////////////////
// Square.cpp
int Square::to_be_initialized = 0;
// this is the most interesting part - register square into initializer list
OpenGLHelper_initializer Square::__initializer([](){
    Square::to_be_initialized = 15; 
    std::cout << "Called Square::init: " << to_be_initialized << std::endl; 
});

int main() 
{
    std::cout << "Before initialization : " << Square::to_be_initialized << std::endl;
    OpenGLHelper::run_init();
    std::cout << "After  initialization : " << Square::to_be_initialized << std::endl;
    return 0;
}

输出:

Before initialization : 0
Called Square::init: 15
After  initialization : 15

现场测试

顺便说一句,QT 的元类型系统使用这种初始化方式 - 它使用宏来简化代码

更新: 正如 Ben 所建议的,如果我们将初始化列表放入静态函数中,我们可以消除由随机链接分配造成的小内存泄漏。这是新代码

于 2016-10-24T07:13:46.633 回答