SDL_CreateWindow (SDL2.0.3)有时永远不会完成

SDL_CreateWindow (SDL2.0.3) sometimes will never finish

本文关键字:永远 时永 CreateWindow SDL2 SDL      更新时间:2023-10-16

我已经设置了一个单独的线程来处理游戏端口上的屏幕刷新。这使我能够专注于更新我使用简单块图形的主要游戏的内存缓冲区。所有我要担心的是改变什么是在哪里,渲染线程将自动更新显示纹理在后台。它工作得很好(在渲染循环之间添加延迟之后)。问题是,在看似随机的情况下,SDL_CreateWindow调用永远不会返回。它不会报错,它只是坐在那里什么都不做。线程的InitVideo部分是:

const HWND desk = GetDesktopWindow();
RECT dsize;
FILE *cfile;
int count1, count2;
printf("Reading font filern");
fopen_s(&cfile,"character.dat","rb");
for(count1 = 0; count1 < 128; count1++)
for(count2 = 0; count2 < 8; count2++)
chROM[count1][count2] = (unsigned char)fgetc(cfile);
fclose(cfile);
printf("Font data readrn");
GetWindowRect(desk,&dsize);
if (fullscreen) {
width = dsize.right;
height = dsize.bottom;
} else {
height = (int)(dsize.bottom * .85);
if (((height * 6) / 5) > dsize.right) {
width = (int)(dsize.right * .85);
height = (int)((width * 5) / 6);
} else width = (int)((height * 6) / 5);
}
printf("Initializing SDLrn");
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
printf("SDL Initialization failedrn");
Sleep(2000);
exit(-1);
}
printf("Creating Windowrn");
if (fullscreen) 
screen = SDL_CreateWindow("Ultima: Escape from Mount Drash!!",SDL_WINDOWPOS_UNDEFINED 
,SDL_WINDOWPOS_UNDEFINED,0,0,SDL_WINDOW_FULLSCREEN_DESKTOP |     SDL_WINDOW_OPENGL);
else screen = SDL_CreateWindow("Ultima: Escape from Mount Drash",SDL_WINDOWPOS_CENTERED 
,SDL_WINDOWPOS_CENTERED,width,height,SDL_WINDOW_OPENGL);
if(!screen) {printf("Failed to create screenrn");Sleep(2000);exit(-1);}
printf("Screen Createdrn");

主要介绍点代码为:

int main(int argc, char* argv[])
{
unsigned char lv$[10];
int count;
srand((unsigned int)time(NULL));
_beginthread(Video, 0, NULL);
Sleep(5000);

Sleep命令是给视频初始化和打开的时间。它只是启动并运行后的几秒钟黑屏,但我可以忍受。第一个屏幕只是一个信息屏幕。如果我碰巧在屏幕打开并开始渲染之前将某些内容放入缓冲区,这应该不是问题。无论如何,它在创建时被0填充(在header -全局变量中),因此渲染&主线程可以访问它)

问题是,10次中有8次,窗户永远不会打开。输出停止在"创建窗口",并没有得到任何其他东西。我已经等了30分钟了,一无所获。我必须强行(从窗户)出去。我想不出任何规律。我加载游戏一次没有问题,然后再加载五次(无论打开或关闭Visual Studio),它都会失败。然后它会成功10次,然后失败20次,等等…当它工作时,它工作得很好,没有任何问题或崩溃(到目前为止)。通过调试模式(Visual c++ 2010)可以达到这一点,并且只是停留在那里。没有错误消息,什么都没有。我按下F11(进入),它就在那里。它不上,它不进

我试图在SDL论坛上发布一个问题,但显然它被锁定了,所以我不能发布新的消息。而且,是的,我确实用电子邮件验证了我的帐户。

这个论坛帖子是旧的(从SDL2之前),但我认为这部分没有太大变化。我建议你把你的处理循环移动到另一个线程,把所有视频和事件相关的东西都留在主线程中,因为SDL似乎还没有为你的用例做好准备,你会达到同样的预期效果。

还要注意,将渲染循环与更新循环分离可能会导致一些游戏玩法问题(例如,在格斗游戏中,玩家倾向于依靠正确的"帧范围"来发动攻击)。你可能会看到过去的帧,而不是当前的帧。

这主要是在回应所有的评论,也有我原来的问题的答案。注释并没有给我足够的空间把所有需要说的都说出来。

首先,答案,它似乎适用于我的问题。看起来我发布的代码实际上工作得很好(至少就我的问题而言)。似乎visualc++不知何故切换到"发布"模式,而我仍然使用调试exe文件进行测试。我不记得改过(或改回来),也从来没有任何理由这么做。当我切换到发布模式时,我发现了这个问题,并发现一个exe文件已经在那里构建了(没有任何必要的支持文件存在-例如SDL2.dll)。回顾我的存档(我定期存档整个项目目录),我找到了它切换的两个点(或接近),这使我得出以下结论。

1)如果要执行SDL例程,那么所有SDL调用都必须在该线程中。在该线程之外进行任何SDL调用(甚至似乎是事件轮询)将导致明显的随机(在某些情况下是相当不合逻辑的)错误。例如,由于某种原因,在单独的线程中使用SDL调用会在关闭文件流(本例中是我的日志文件)时导致访问冲突错误。文件关闭(lfile);调用一直导致错误,即使它与SDL无关。

2)当你启动SDL线程时,确保在继续主线程之前等待它完成初始化。如果在SDL线程创建某些东西时,主线程中发生了错误的事情,那么由于某种原因,主线程可能会锁定。SDL_CreateWindow()函数似乎是唯一的罪魁祸首,但我不能肯定地说。在最后的代码中,我使用了一个全局布尔变量来代替延迟。基本上,我创建了变量(bool SDLRunning;)并将其设置为false。然后启动SDL线程(_beginthread(Video, 0, NULL);)。接下来,我设置了一个循环,它只监视变量(while (!SDLRunning) Sleep(10);)。最后,当SDL线程完成所有初始化,并且除了函数循环之外什么都不做时,它将变量设置为true,以让主线程知道继续是安全的。

到目前为止,我几乎已经完成了,我没有遇到任何其他类型的问题(除了任何程序中都会出现的标准错误)。它现在运行得很顺利。我不得不一次又一次地去调整东西,但就是这样(这里的时间有点偏离,忘记清除那里的变量,诸如此类的事情)

现在,回答一些评论。视频的渲染被拖出,因为它实际上使编程游戏代码本身更容易。原始代码是围绕一个不需要编程视频渲染的系统设计的。因此,它使用打印语句和图形的内存地址。我不需要手动添加渲染调用,而是使用一个数组来表示原始系统的"图形ram"。然后,当我用一个值转换数百行原始代码时,我不必每次都立即调用渲染函数。

同样,最初的游戏有几个背景任务依赖于相当精确的时间。它通过使用硬件中断实现了这一点(微软以其无限的智慧取消了所有访问)。我最初试图以各种方式模仿HW中断,但都没有成功。将它们放在一个单独的线程中(我标记为Video只是因为它的最大负载是转换和渲染视频),并使用可调整的延迟时间是唯一有效的方法。其中之一是每秒改变显示区域的颜色7.5次(每2秒15次)。因为我已经把所有的颜色责任与视频函数,和所有的东西SDL(像视频函数,)必须在同一个线程中,SDL需要在同一个线程作为例程。

最后,其中一项背景任务是播放各种合成音乐曲调(方波)。我想保留游戏的原始感觉,所以我也想合成音乐。我不想录制几个wave文件(其中最小的是253K)来取代原始游戏用于音乐的3k数据。所以,比起其他任务,它需要更仔细的时间安排。我还需要一个可以做到这一点的声音库,但这是一个单独的问题(尽管其他人的侮辱,或者我的问题完全被忽视,我还是设法解决了)

我最初试图在没有任何中断(或任何模拟)或创建单独线程的情况下完成所有这些。这导致了真正混乱的复杂性,浪费了一个月的编程时间。正是这种最初的代码失败,实际上是不可能遵循的(即使是我,我写了这个该死的东西,),促使我开始尝试在中断例程中编程。如果我是为DOS编程的话,我可以很容易地做到这一点(我已经有点厌倦了许多人对每个想为DOS编程的人的侮辱),但是微软取消了中断访问使事情变得复杂。在DOS中,您只需读取一个以稳定规律(每秒x次)触发的中断向量。将向量重定向到代码中。然后确保中断代码的最后一个动作是跳转到原始向量。最后,当程序退出时,将中断恢复到其原始向量。又好又简单。我猜微软不相信程序员知道最后两个步骤的必要性,但他们至少可以包括一些方法来"插入"一个函数(或多个函数)到一个预先存在的中断例程。