问题描述
在尝试将我的游戏引擎移植到mac时,我遇到了一个基本(但大的)问题。在Windows上,我的主要代码看起来像这样(非常简化):
PeekMessage
switch(msg.message)
{
case WM_QUIT:...;
case WM_LBUTTONDOWN:...;
...
}
TranslateMessage(& msg);
DispatchMessage(& msg);
for(std :: vector< CMyBackgroundThread *> :: iterator it = mythreads.begin(); it!= mythreads.end(); ++ it)
{
(* it) - > processEvents();
}
...做其他有用的东西,如看窗口已调整大小和其他东西...
drawFrame(); //使用opengl绘制一个框架
SwapBuffers(hDC); // swap backbuffer / frontbuffer
if(g_sleep> 0)Sleep(g_sleep);
我已经读过,这不是mac的方式。 mac方式检查某种类型的事件,如屏幕的v-sync,以渲染一个新的帧。但是你可以看到,我想做的事情不仅仅是渲染,我想要其他线程做工作。有些线程需要比每1/60秒更快地调用。
(顺便说一下,我的线程基础设施是:线程puts事件在同步队列,主线程调用processEvents处理在主线程内同步队列中的项目,这是用于网络东西,图像加载/处理东西,perlin噪声生成等等...)
我很想能够做到这一点,类似的方式,但很少的信息是可用的。我不介意将渲染放在v-sync事件上(我将在Windows上实现这一点),但我想对其余代码有更多的响应性。
看看这样:我希望能够处理其余的,而GPU正在做东西,我不想等待v-sync,然后开始做的东西,应该已经被处理以仅开始向GPU发送东西。你明白我的意思吗?
如果我需要从完全不同的角度来看这个,请告诉我。
如果我需要阅读书籍/指南/教程/任何东西,请告诉我读什么!
我不是可可开发者,我不是object-c程序员,我的游戏引擎完全是C ++,但我知道我的方式围绕xcode足以使一个窗口出现,并显示我在该窗口内绘制。它只是不更新像我的Windows版本,因为我不知道如何得到它正确。
更新:我甚至相信,我需要某种循环,即使我想在垂直回扫上同步。 MacOSX文档上的OpenGL编程显示,这是通过设置SwapInterval来完成的。因此,如果我理解正确,我将总是需要某种循环,当在Mac上呈现实时,使用swapInterval设置降低功耗。这是真的吗?
虽然我不能是世界上唯一试图实现这一点的人,回答这个(不指向你stackoverflow的家伙,只是指向mac开发人员知道它的工作原理)。
这就是为什么我想改变这种典型的行为,并帮助那些正在寻找同样的事情。换句话说:我找到了解决方案!我仍然需要进一步改进这个代码,但这基本上是!
1 /当你需要一个真正的自己的循环,就像在windows,你需要注意的自己。所以,让可可建立你的标准代码,但取main.m(如果需要,将其更改为.mm,这是在这个例子中,因为我使用c ++)并删除一行和唯一的代码行。您不会允许可可为您设置您的应用程序/窗口/视图。
2 / Cocoa通常为您创建一个AutoreleasePool。它可以帮助您进行内存管理。现在,这不会再发生,所以你需要初始化它。你的第一行在main函数中是:
[[NSAutoreleasePool alloc] init];
3 /现在您需要设置您的应用程序委托。 XCode向导已经为你设置了一个AppDelegate类,所以你只需要使用它。此外,向导已经设置了您的主菜单,并可能称为MainMenu.xib。默认值很好开始。确保#importYourAppDelegate.h在#import< Cocoa / Cocoa.h>之后。线。然后在您的主要函数中添加以下代码:
[NSApplication sharedApplication];
[NSApp setDelegate:[[[YourAppDelegate alloc] init] autorelease]];
[NSBundle loadNibNamed:@MainMenuowner:[NSApp delegate]];
[NSApp finishLaunching];
4 / Mac OS现在将知道应用程序是什么,将添加一个主菜单。运行这个不会做任何事情,因为还没有窗口。让我们创建它:
[Window setDelegate:[NSApp delegate]];
[Window setAcceptsMouseMovedEvents:TRUE];
[Window setIsVisible:TRUE];
[Window makeKeyAndOrderFront:nil];
[Window setStyleMask:NSTitledWindowMask | NSClosableWindowMask];
为了显示这里可以做什么,我添加了一个标题栏并启用了关闭按钮。
5 /现在如果你运行这个,你可能会很幸运,看到一个微秒的窗口,但你可能看不到什么,因为...程序不在一个循环中。让我们添加一个循环并监听传入的事件:
bool quit = false;
while(!quit)
{
NSEvent * event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
switch([(NSEvent *)event type])
{
case NSKeyDown:
quit = true;
break;
默认值:
[NSApp sendEvent:event];
break;
}
[event release];
}
如果运行此操作,您将看到窗口出现,直到你只是按一个键。当您按键时,应用程序将立即退出。
这是它!但小心...检查活动监视器。您将看到您的应用程序正在使用100%的CPU。为什么?因为循环不会使CPU进入睡眠模式。这是你现在。你可以让自己容易和放一个usleep(10000);在一段时间。这将做很多,但不是最佳的。我可能会使用垂直同步的opengl,以确保CPU不被过度使用,我也将等待从我的线程的事件。也许我甚至可以自己检查过去的时间,并做一个计算usleep,使每帧的总时间,以便我有一个体面的帧速率。
有关Opengl :
1 /将cocoa.opengl框架添加到项目中。
2 / Before [Window .. 。] put:
NSOpenGLPixelFormat * format = [[NSOpenGLPixelFormat alloc] initWithAttributes:windowattribs];
NSOpenGLContext * OGLContext = [[NSOpenGLContext alloc] initWithFormat:format shareContext:NULL];
[format release];
3 / After [Window setDelegate:...] put:
[OGLContext setView:[Window contentView]];
4 /在窗口处理后,放置:
[OGLContext makeCurrentContext];
5 / [事件发布]后,输入:
glClearColor(1,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
[OGLContext flushBuffer];
这将清除您的窗口,在我的Mac书pro 2011的3300的帧率完全红色。 / p>
同样,此代码不完整。当您点击关闭按钮并重新打开它,它将不再工作。我可能需要挂钩事件,我不认识他们(经过进一步的研究,这些事情可以在AppDelegate完成)。此外,还有很多其他的事情要做/考虑。但这是一个开始。请随时纠正我/填写我在/...
如果我完全正确,我可以设置一个教程网页,如果没有人打
我希望这可以帮助所有人!
While trying to port my game engine to mac, I stumble upon one basic (but big) problem. On windows, my main code looks like this (very simplified):
PeekMessage(...) // check for windows messages
switch (msg.message)
{
case WM_QUIT: ...;
case WM_LBUTTONDOWN: ...;
...
}
TranslateMessage(&msg);
DispatchMessage (&msg);
for (std::vector<CMyBackgroundThread*>::iterator it = mythreads.begin(); it != mythreads.end(); ++it)
{
(*it)->processEvents();
}
... do other useful stuff like look if the window has resized and other stuff...
drawFrame(); // draw a frame using opengl
SwapBuffers(hDC); // swap backbuffer/frontbuffer
if (g_sleep > 0) Sleep(g_sleep);
I already read that this is not the mac way. The mac way is checking for some kind of event like the v-sync of the screen to render a new frame. But as you can see, I want to do a lot more than only rendering, I want other threads to do work. Some threads need to be called faster than every 1/60th of a second.(by the way, my thread infrastructure is: thread puts event in a synchronized queue, main thread calls processEvents which handles the items in that synchronized queue within the main thread. This is used for network stuff, image loading/processing stuff, perlin noise generation, etc... etc...)
I would love to be able to be able to do this a similar way, but very little information is available about this. I wouldn't mind putting the rendering on a v-sync event (I will implement this also on windows), but I would like to have a bit more responsiveness on the rest of the code.
Look at it this way: I would love to be able to process the rest while the GPU is doing stuff anyway, I do not want to wait for a v-sync to then start doing stuff that should already have been processed to only then start sending stuff to the GPU. Do you understand what I mean?
If I need to look at this from a completely different point of view, please tell me.
If I need to read books/guides/tutorials/anything for this, please tell me what to read!
I'm no cocoa developer, I'm no object-c programmer, my game engine is entirely in C++, but I know my way around xcode enough to make a window appear and show what I draw inside that window. It just doesn't update like my windows version because I don't know how to get it right.
Update: I even believe that I need some kind of loop, even if I want to synchronize on the vertical retrace. The OpenGL programming on MacOSX documentation shows that this is done by setting the SwapInterval. So if I understand correctly, I will always need some kind of loop when rendering real time on the Mac, using the swapInterval setting to lower power usage. Is this true?
Although I can't be the only person in the world trying to achieve this, nobody seems to want to answer this (not point at you stackoverflow guys, just pointing at mac developers who know how it works).That's why I want to change this typical behaviour and help those who are looking for the same thing. In other words: I found the solution! I still need to further improve this code, but this is basically it!
1/ When you need a real own loop, just like in windows, you need to take care of it yourself. So, let cocoa build your standard code, but take main.m (change it to .mm for c++ if necessary, which is in this example because I used c++) and remove the one and only line of code. You will not allow cocoa to set up your application/window/view for you.
2/ Cocoa normally creates an AutoreleasePool for you. It helps you with memory management. Now, this will no longer happen, so you need to initialize it. Your first line in the main function will be:
[[NSAutoreleasePool alloc] init];
3/ Now you need to set up your application delegate. The XCode wizard has already set up a AppDelegate class for you, so you just need to use that one. Also the wizard has already set up your main menu and probably called it MainMenu.xib. The default ones are fine to get started. Make sure you #import "YourAppDelegate.h" after the #import <Cocoa/Cocoa.h> line. Then add the following code in your main function:
[NSApplication sharedApplication];
[NSApp setDelegate:[[[YourAppDelegate alloc] init] autorelease]];
[NSBundle loadNibNamed:@"MainMenu" owner:[NSApp delegate]];
[NSApp finishLaunching];
4/ Mac OS will now know what the application is about, will add a main menu to it. Running this won't do much anyhow, because there is no window yet. Let's create it:
[Window setDelegate:[NSApp delegate]];
[Window setAcceptsMouseMovedEvents:TRUE];
[Window setIsVisible:TRUE];
[Window makeKeyAndOrderFront:nil];
[Window setStyleMask:NSTitledWindowMask|NSClosableWindowMask];
To show what you can do here, I added a title bar and enabled the close button. You can do a lot more, but I also need to study this first.
5/ Now if you run this, you might get lucky and see a window for a microsecond, but you will probably see nothing because... the program is not in a loop yet. Let's add a loop and listen to incoming events:
bool quit = false;
while (!quit)
{
NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
switch([(NSEvent *)event type])
{
case NSKeyDown:
quit = true;
break;
default:
[NSApp sendEvent:event];
break;
}
[event release];
}
If you run this, you will see the window appear and it will stay visible until you just press a key. When you press the key, the application will quit immediately.
This is it! But beware... check out the Activity Monitor. You will see that your application is using 100% CPU. Why? Because the loop doesn't put the CPU in sleep mode. That's up to you now. You can make it easy on yourself and put a usleep(10000); in the while. This will do a lot, but isn't optimal. I will probably use the vertical-sync of opengl to make sure the CPU isn't overly used, and I will also wait for events from my threads. Maybe I will even check out the passed time myself and do a calculated usleep to make the total time per frame so that I have a decent frame rate.
For help with Opengl:
1/ Add the cocoa.opengl framework to the project.
2/ Before [Window...] put:
NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:windowattribs];
NSOpenGLContext *OGLContext = [[NSOpenGLContext alloc] initWithFormat:format shareContext:NULL];
[format release];
3/ After [Window setDelegate:...] put:
[OGLContext setView:[Window contentView]];
4/ Somewhere after the window handling, put:
[OGLContext makeCurrentContext];
5/ After [event release], put:
glClearColor(1, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
[OGLContext flushBuffer];
This will clear your window to fully red on a framerate of 3300 on my mac book pro 2011.
Again, this code is not complete. When you click on the close button and reopen it, it will no longer work. I probably need to hook events for that and I don't know them yet (after further research, these things can be done in the AppDelegate). Also, there are probably lots of other things to do/consider. But this is a start. Please feel free to correct me/fill me in/...
If I get it fully right, I might set up a tutorial web page for this if no one beats me to it.
I hope this helps you all!
这篇关于可可消息循环? (与Windows消息循环)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!