【置顶】Lite2D的渲染线程尝试

梦想游戏人
目录:
C/C++

项目地址      https://github.com/dreamyouxi/Lite2D

注意:

1.GL只能和一个线程相关,所以纹理产生,初始化等都要放到渲染线程。

2.双缓冲默认是垂直同步的 可用  glfwSwapInterval; 来设置

GL API 参考自      http://www.glfw.org/docs/latest/group__context.html

暂且用以下方案:

1.渲染线程拷贝一个主线程产生的 纹理(暂定)

2.遍历Node的时候吧渲染参数 包装为命令(RenderCmd)  放到渲染队列里面(RenderCmdQueue)

3.因为线程安全问题,GL相关操作都用回调 在渲染线程来处理

逻辑线程执行的时候 会强制同步到渲染线程

逻辑帧数60,渲染帧数11999,逻辑延时0.000467 MS,测试平台 i7 4790K GTX 950

帧数限制:

通过最大帧数的时间来控制渲染次数,当渲染次数和逻辑次数不匹配会自动丢帧,保证当前渲染的始终是最新帧

static void   ThreadFunc(RenderCmdQueue * reder)
{
	Director::getInstance()->getGLView()->init(); // init gl in render thread 

	LARGE_INTEGER nLast;
	LARGE_INTEGER nNow;

	QueryPerformanceFrequency(&nFreq);
	perFrame.QuadPart = (LONGLONG)(1.0 / DEFAULT_FPS* nFreq.QuadPart);

	QueryPerformanceCounter(&nLast);

	float delta = 0.0f;
	while (true)
	{
		QueryPerformanceCounter(&nNow);
		if (nNow.QuadPart - nLast.QuadPart > perFrame.QuadPart)//default fps is 120
		{
			delta = (float)(nNow.QuadPart - nLast.QuadPart) / (float)nFreq.QuadPart;
			reder->render();
			reder->setRenderFPS(1.0f / delta + 1);

			nLast.QuadPart = nNow.QuadPart;
		}
		else
		{
			//std::this_thread::sleep_for(std::chrono::microseconds(0));
		}
	}

}

demo项目表现良好

渲染命令

//.h
class RenderCmd :public Ref
{
public:
	virtual void exec(Texture2D*) = 0;
	float   *_coord2f = 0;
	Vec2 _vertex[4];
	float _opacity = 0.0f;
	Texture2D*tex=nullptr;
};


class RenderCmd_Quad :public RenderCmd
{
public:
	virtual void exec(Texture2D*)override;
};


//.cpp

void  RenderCmd_Quad::exec(Texture2D *tex)
{

	GLuint id = tex->getTextureID();
	tex->getFileName().c_str();


	glLoadIdentity();// vetex can work once

	glBindTexture(GL_TEXTURE_2D, id);

	// able alpha blend for the texture who has alpha
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	//able opacity
	glEnable(GL_ALPHA_TEST);
	glAlphaFunc(GL_GREATER, 0.0f);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	glColor4f(1.0f, 1.0f, 1.0f, _opacity);


	//start to render

	glBegin(GL_QUADS);

	glTexCoord2f(_coord2f[0], _coord2f[1]); glVertex2f(_vertex[0].x, _vertex[0].y);
	glTexCoord2f(_coord2f[2], _coord2f[3]); glVertex2f(_vertex[1].x, _vertex[1].y);
	glTexCoord2f(_coord2f[4], _coord2f[5]); glVertex2f(_vertex[2].x, _vertex[2].y);
	glTexCoord2f(_coord2f[6], _coord2f[7]); glVertex2f(_vertex[3].x, _vertex[3].y);


	glDisable(GL_BLEND);
	glDisable(GL_ALPHA_TEST);
	glEnd();
}

渲染队列(渲染线程)

class RenderCmdQueue
{

public:

	void addFuncToRenderThread(const std::function<Texture2D*(void)> &func);
	void addCustomFuncToRenderThread(const std::function<void(void)> &func);
private:

	std::unordered_map<std::string  , Texture2D*> tex_pool;

	std::vector<std::function<Texture2D*(void)>> _func;
	std::vector <std::function<void(void)>> _func1;
	void processOtherThreadFunc();



	std::mutex _mutex;

public:
	static RenderCmdQueue*create();
	void addRenderCmd(RenderCmd*cmd);

	void clear();
	void NextTick();//thread safe 
 
	
	void draw();
	void setVerticalSynchronization(bool enable);

private:
	std::atomic<bool> isNextTick = false;
	std::vector<RenderCmd*> _queue;
 
	void clearAllRenderCmd();

	RenderCmdQueue()
	{
		_queue.reserve(200);
	}
};
//cpp
static void   ThreadFunc(RenderCmdQueue * reder)
{
	Director::getInstance()->getGLView()->init(); // init gl in render thread 

	LARGE_INTEGER nLast;
	LARGE_INTEGER nNow;

	QueryPerformanceFrequency(&nFreq);
	perFrame.QuadPart = (LONGLONG)(1.0 / 120 * nFreq.QuadPart);

	QueryPerformanceCounter(&nLast);

	float delta = 0.0f;
	while (true)
	{
		QueryPerformanceCounter(&nNow);
		if (nNow.QuadPart - nLast.QuadPart > perFrame.QuadPart)
		{
			delta = (float)(nNow.QuadPart - nLast.QuadPart) / (float)nFreq.QuadPart;
			reder->render();
			reder->setRenderFPS(1.0f / delta + 1);

			nLast.QuadPart = nNow.QuadPart;
		}
		else
		{
			//std::this_thread::sleep_for(std::chrono::microseconds(0));
		}
	}

}


RenderCmdQueue* RenderCmdQueue::create()
{
	RenderCmdQueue*ret = new RenderCmdQueue;

	//Director::getInstance()->getGLView()->init();

	/*std::thread t([ret]()
	{
	Director::getInstance()->getGLView()->init();

	while (true)
	{
	ret->draw();
	//	Sleep(100);

	}

	}); t.detach();*/

	static std::thread t(&ThreadFunc, ret); t.detach();

	return ret;
}


void RenderCmdQueue::addRenderCmd(RenderCmd*cmd)
{
	_mutex.lock();
	_queue.push_back(cmd);
	_mutex.unlock();
}



void RenderCmdQueue::clear()
{
	isNextTick = false;
	_mutex.lock();
	++_tick_status;
	if (_tick_status > 5)
	{
		_tick_status = 0;
		auto dir = Director::getInstance();

		_cache_fps = dir->getFPS();
		_cache_last_fps = this->getRenderFPS();
		_cache_redertime = dir->getRenderTime();
	}
	this->clearAllRenderCmd();

	_mutex.unlock();
}

void  RenderCmdQueue::setVerticalSynchronization(bool enable)
{
	if (enable)
	{
		glfwSwapInterval(0xff);
	}
	else
	{
		glfwSwapInterval(0x0);
	}
}


void RenderCmdQueue::NextTick()//thread safe 
{
	this->isNextTick = true;
}

void  RenderCmdQueue::clearAllRenderCmd()
{

	for (int i = 0; i < _queue.size(); i++)
	{
		_queue[i]->release();
	}
	_queue.clear();

}




void RenderCmdQueue::render()
{
	do
	{
		if (isNextTick == false)break;

		std::lock_guard<std::mutex> lock(_mutex);

		//	if (_queue.empty())break;

		glClear(GL_COLOR_BUFFER_BIT);
		//	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glEnable(GL_TEXTURE_2D);

		for (int i = 0; i < _queue.size(); i++)
		{

			RenderCmd *cmd = _queue[i];

			auto it = tex_pool.find(cmd->tex->getFileName());
			if (it == tex_pool.end())
			{

				Texture2D* tex = new Texture2D;

				auto image = new Image;
				image->initWithFile(cmd->tex->getFileName());
				tex->initWithImage(image);

				cmd->exec(tex);
				tex_pool[cmd->tex->getFileName()] = tex;
			}
			else
			{
				cmd->exec((*it).second);
			}
		}

		glDisable(GL_TEXTURE_2D);
		Director::getInstance()->getGLView()->swapBuffers();


	} while (false);

	this->processOtherThreadFunc();

}





void  RenderCmdQueue::processOtherThreadFunc()
{
	_mutex.lock();

	for (auto &func : _func)
	{
		Texture2D*  tex = func();
		tex_pool[tex->getFileName()] = tex;
	}
	_func.clear();

	for (auto &func : _func1)
	{
		func();
	}
	_func1.clear();

	_mutex.unlock();
}


void  RenderCmdQueue::addFuncToRenderThread(const std::function<Texture2D*(void)> &func)
{
	this->_mutex.lock();

	_func.push_back(func);
	this->_mutex.unlock();
}


void  RenderCmdQueue::addCustomFuncToRenderThread(const std::function<void(void)> &func)
{
	_mutex.lock();
	_func1.push_back(func);
	_mutex.unlock();
}

实测,当object很多时候 渲染效率反而底下,比如1万个同纹理的 单线程依然50+FPS 多线程版本就只有20+FPS,

Scroll Up