【问题标题】:SDL_CreateWindow (SDL2.0.3) sometimes will never finishSDL_CreateWindow (SDL2.0.3) 有时永远不会完成
【发布时间】:2014-08-25 07:19:58
【问题描述】:

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

const HWND desk = GetDesktopWindow();
RECT dsize;
FILE *cfile;
int count1, count2;

printf("Reading font file\r\n");
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 read\r\n");

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 SDL\r\n");
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
    printf("SDL Initialization failed\r\n");
    Sleep(2000);
    exit(-1);
}

printf("Creating Window\r\n");
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 screen\r\n");Sleep(2000);exit(-1);}
printf("Screen Created\r\n");

主要介绍点代码为:

int main(int argc, char* argv[])
{
    unsigned char lv$[10];
    int count;

    srand((unsigned int)time(NULL));

    _beginthread(Video, 0, NULL);
    Sleep(5000);

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

问题是,大约 10 次中有 8 次,窗户永远不会打开。输出在“Creating Window”处停止,永远不会得到其他任何东西。我已经等了 30 分钟,一无所获。我必须强制退出(通过窗户)。我无法弄清楚任何类型的模式。我将毫无问题地加载游戏一次,然后接下来的五次(打开或关闭 Visual Studio)它将失败。然后它将在接下来的 10 次成功,然后失败 20 次,依此类推......当它工作时,它工作得很好,没有任何问题或崩溃(到目前为止)。通过调试模式(Visual C++ 2010)到那一点,然后就坐在那里。没有错误消息,什么都没有。我按 F11(步入),它就在那里。进不去,进不去。

我试图在 SDL 论坛上发布问题,但显然它已被锁定,因此我无法发布新消息。而且,是的,我确实使用电子邮件验证了我的帐户。

【问题讨论】:

  • sdl 是否允许您从与主线程不同的线程更新视图?通常允许主线程在屏幕上绘制,并将逻辑移动到不同的线程
  • SDL 从未以线程安全的方式设计,Sam 已公开承认这一点。创建主窗口的线程是唯一可以/应该访问它的线程(包括例如处理事件)。从我所看到的情况来看,这更多是对底层 API 的限制。
  • 所有 SDL 都在一个线程中处理。我在线程之外所做的只是改变一个缓冲区(实际上是一个数组)。渲染线程读取数组,将其转换为 ARGB 像素,更新纹理,渲染它,等待片刻,然后重复。它还轮询事件并将结果放入我可以从主线程读取的结构中,但我还没有完成编写该代码。

标签: c++ multithreading visual-studio-2010 visual-c++ sdl-2


【解决方案1】:

This forum post 是旧的(从 SDL2 之前开始),但我认为这部分变化不大。我建议您将处理循环移到另一个线程,并将所有与视频和事件相关的内容留在主线程中,因为 SDL 似乎还没有为您的用例做好准备,您将获得相同的预期效果。

另外请注意,将渲染循环与更新循环分离可能会导致一些游戏问题(例如,玩家倾向于依靠正确的“帧范围”进行攻击的格斗游戏)。您可能会看到过去的帧而不是当前帧。

【讨论】:

  • 好的,已经进行了 100 多次测试,问题似乎已经停止。我不知道我做了什么(如果我什至做了什么)来解决这个问题。我改变了一些事情的顺序,但变化不大。在我用_beginthread调用主循环函数之前,它立即调用了InitVideo函数。现在,我用 _beginthread 调用 InitVideo 函数,它调用循环(然后在我关闭线程时等待返回。)也许与它有关,我只是不知道 :(
  • 一旦我将整个 SDL 块移动到单个线程中,就没有任何这种性质的问题。在此之前,我有无法解释的崩溃等(我无法确定押韵或原因。)我正在阅读 SDL 的线程问题,我将它们一起移动(这些问题消失了。)此外,即使在渲染之间有一点延迟循环,它仍然可以跟上游戏的速度(如果你可以这么说的话)。这是我移植的大约 1983 年的游戏,速度不是问题。如果循环可以保持 30FPS,它就比游戏本身可以做的任何事情都要好。
  • 是的,似乎不建议将 SDL 视频和事件调用放在主线程以外的另一个线程(即使它们初始化 SDL),特别是在 Android 等系统上,SDL 将创建其他线程让事情发挥作用。由于您正在移植一个旧游戏,我认为多线程不会有太多好处,只有它的复杂性。
  • 另外我总是这么说,你必须考虑你的程序是否真的需要多线程,因为大多数时候诚实的答案是否定的,它只是一个额外的复杂性,有时会导致性能下降如果做错了。
【解决方案2】:

这主要是针对所有的cmets,也有我原来的问题的答案。评论只是没有空间让我在这里说所有需要说的内容。

首先,答案似乎适用于我的问题。看来我发布的代码实际上工作得很好(至少就我的问题而言)。似乎 Visual C++ 以某种方式切换到“发布”模式,而我仍在使用调试 exe 文件进行测试。我不记得改变它(或改变它回来),也从来没有任何理由这样做。我在切换到发布模式时发现了这一点,并发现那里已经构建了一个 exe 文件(没有任何所需的支持文件存在 - 例如 SDL2.dll。)回顾我的存档(我定期存档整个项目目录,)我追踪了它切换的两个点(或关闭),这使我得出以下结论。

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

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

到目前为止,我已经差不多完成了,我没有遇到任何其他问题(除了潜入任何程序的标准错误)。它现在运行顺利。我不得不时不时地进行调整,但仅此而已(这里的时间有点不对,忘记清除那里的变量,诸如此类。)

现在,回答一些 cmets。视频的渲染被线程化了,因为它实际上使编写游戏代码本身更容易。原始代码是围绕不需要您对视频渲染进行编程的系统设计的。因此,它使用打印语句和图形内存地址。无需手动添加渲染调用,我只需使用一个数组来表示原始系统的“图形内存”。然后当我将数百行用值戳图形RAM的原始代码转换时,我不必每次都立即调用渲染函数。

此外,原始游戏有几个依赖于相当准确时间的后台任务。它通过使用硬件中断来做到这一点(微软以其无限的智慧删除了所有访问权限。)我最初尝试以各种方式模拟硬件中断,但都没有成功。将它们放在一个单独的线程中(我标记为视频只是因为它最重的负载是转换和渲染视频)具有可调节的延迟时间是唯一有效的方法。其中之一是每秒更改显示区域的颜色 7.5 次(每 2 秒 15 次。)因为我已经将所有颜色责任都交给了视频功能,而所有 SDL(如视频功能)都必须在同一个线程中,SDL 需要与该例程在同一个线程中。

最后,后台任务之一是播放各种合成的音乐曲调(方波)。我想保留游戏的原始感觉,所以我也想合成音乐。我不想录制几个波形文件(其中最小的是 253K)来替换原始游戏用于音乐的 3k 数据。因此,除了任何其他任务所需的时间之外,它还需要谨慎的时间安排。我还需要一个可以做到这一点的声音库,但这是一个单独的问题(尽管受到他人的侮辱,或者我的问题被完全忽略,但我还是设法解决了这个问题。)

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-29
    • 2018-05-11
    相关资源
    最近更新 更多