bkaradzic / bgfx

Cross-platform, graphics API agnostic, "Bring Your Own Engine/Framework" style rendering library.
https://bkaradzic.github.io/bgfx/overview.html
BSD 2-Clause "Simplified" License
15.09k stars 1.95k forks source link

Support SDL on iOS #634

Open johngirvin opened 8 years ago

johngirvin commented 8 years ago

SDL 2.0.4 has been released with official support for iOS. However, attempting to use BGFX with SDL on iOS results in an exception in glcontext_eagl.mm at layer.opaque = true;

Initially it looks like the SDL platform data isn't initialised for iOS but it's not clear to me what needs to be done.

bkaradzic commented 8 years ago

Are you using examples/common/entry code for iOS?

johngirvin commented 8 years ago

Not sure what you mean, but I'm using something like:

SDL_Window *window = SDL_CreateWindow(...);
bgfx::sdlSetWindow(window);
bgfx::init();

There's no #if BX_PLATFORM_IOS branch in sdlSetWindow to set up g_platformData.nwh

johngirvin commented 8 years ago

FWIW I get a similar error running on a Metal capable device at renderer_mtl.mm:356

bkaradzic commented 8 years ago

Instead:

    bgfx::sdlSetWindow(window);

Try something like this (GLES2):

    SDL_SysWMinfo wmi;
    SDL_VERSION(&wmi.version);
    SDL_GetWindowWMInfo(_window, &wmi);

    bgfx::PlatformData pd;
    pd.ndt          = NULL;
    pd.nwh          = wmi.info.uikit.window.rootViewController.view.layer;
    pd.context      = NULL;
    pd.backBuffer   = NULL;
    pd.backBufferDS = NULL;
    bgfx::setPlatformData(pd);

It's similar to this code path in entry: https://github.com/bkaradzic/bgfx/blob/master/examples/common/entry/entry_ios.mm#L201

I haven't tested this at all, just going off 2.0.4 docs...

For Metal, you would also need to pass "device" into pd.context that's create like this: https://github.com/bkaradzic/bgfx/blob/master/examples/common/entry/entry_ios.mm#L180

bkaradzic commented 8 years ago

@johngirvin Any luck with this?

johngirvin commented 8 years ago

I tried the above with the addition of: pd.nwh = (__bridge void *) wmi.info.uikit.window.rootViewController.view.layer;

You need to init the SDL Renderer first otherwise layer contains a standard CALayer.

It gets a little further, but in glcontext_eagl.mm the following returns NO which leads to nothing being rendered: [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];

I've confirmed layer isa CAEAGLLayer and context isa EAGLContext at this point.

bgfx.cpp (2350): BGFX Init... bgfx.cpp (1136): BGFX Creating rendering thread. bgfx_p.h (2099): BGFX render thread start bgfx.cpp (1145): BGFX Running in multi-threaded mode glcontext_eagl.mm (56): BGFX Screen size: 0 x 0 glcontext_eagl.mm (67): BGFX CHECK glCheckFramebufferStatus failed 0x00008cd6

0x00008cd6 is GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT

jazzbre commented 8 years ago

Any progress on this issue? I'm willing to help if there's something I can try. Bgfx is awesome as it is, but with SDL support on all platforms it would be even better.

bkaradzic commented 8 years ago

Yeah, help about this would be appreciated.

jazzbre commented 8 years ago

Ok, I've got a hacked version working. After SDL_CreateWindow I do this:

    SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    SDL_SysWMinfo wmi;
    SDL_VERSION(&wmi.version);
    SDL_GetWindowWMInfo(window, &wmi);
    CALayer* layer = wmi.info.uikit.window.rootViewController.view.layer;

    bgfx::PlatformData pd;
    pd.ndt          = NULL;
    pd.nwh          = (__bridge void *)layer;
    pd.context      = NULL;
    pd.backBuffer   = NULL;
    pd.backBufferDS = NULL;
    bgfx::setPlatformData(pd);

It seems that the problem is that the render buffers etc gets generated two times once by SDL and once by BGFX, so this disables it in SDL: SDL_uikitopenglview.mm at line 124:

        /* Set the appropriate scale (for retina display support) */
        self.contentScaleFactor = scale;

        return self;

Any ideas how to do this properly?

bkaradzic commented 8 years ago

You can pass context and backbuffers to bgfx via platform data here:

pd.context      = <whatever SDL created>;
pd.backBuffer   = <whatever SDL created>;
pd.backBufferDS = <whatever SDL created>;
jazzbre commented 8 years ago

I'm not really sure where to get the info from SDL. But still from what I see bgfx will still try to create context and render buffers by itself.

bkaradzic commented 8 years ago

On desktop, SDL doesn't create context unless you add SDL_INIT_VIDEO here: https://github.com/bkaradzic/bgfx/blob/master/examples/common/entry/entry_sdl.cpp#L398

jazzbre commented 8 years ago

It's the same on iOS, but because I have to call SDL_CreateRenderer, to get the context and backbuffer etc, bgfx IMHO shouldn't re-initialize opengl, but just use the stuff from PlatformData.

bkaradzic commented 8 years ago

So if you don't pass any context to bgfx it should create context, but if you do it should skip it. I was just looking glcontext_eagl.mm and it doesn't handle case when context is passed properly. You can add handling for data passed, follow example here: https://github.com/bkaradzic/bgfx/blob/master/src/glcontext_egl.cpp#L159

slime73 commented 8 years ago

Don't create a SDL renderer, it creates an OpenGL ES context of an implementation-dependent version. If you really want to create an OpenGL ES context using SDL, use SDL_GL_CreateContext.

Here's an example of using Metal with SDL on iOS: https://gist.github.com/slime73/12284a8299be857d2581

jimon commented 8 years ago

I started to look on this one (duh, after years in game industry I'm a bit way too lazy to write all entry points myself again every time I decide to prototype a new engine).

So the thing is, at least from looking on sources of SDL 2.0.5 - when one create a SDL window one don't get EAGL enabled UIView. So if one does pd.nwh = wmi.info.uikit.window.rootViewController.view.layer; without asking SDL to create opengl renderer then everything fails here :

    layer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys
                                    : [NSNumber numberWithBool:false]
                                    , kEAGLDrawablePropertyRetainedBacking
                                    , kEAGLColorFormatRGBA8
                                    , kEAGLDrawablePropertyColorFormat
                                    , nil
                                    ];

Because layer is CALayer and not CAEAGLLayer duh. And CALayer doesn't have drawableProperties, @bkaradzic consider adding an assert there just to check that layer have a correct class type or at least have drawableProperties property.

This can/should be fixed in SDL itself by adding + (Class)layerClass {return [CAEAGLLayer class];} somewhere in SDL_uikitview.m, but for now I guess we can just create a separate UIView instance just for bgfx ...

TBH: I'll probably will give up on SDL for iOS here, because it doesn't really looks like it can deliver proper quality for production ready game - aka how the heck do you plan to integrate ads, gamecenter, etc with it? :/

slime73 commented 8 years ago

SDL_GL_CreateContext creates an EAGLLayer-backed UIView and attaches it to the window's view as a subview.

jimon commented 8 years ago

@slime73 yes, and SDL_GL_CreateContext also creates EAGLContext and framebuffers, while what we want here is for SDL to create just a UIWindow + UIView and let bgfx create and own EAGLContext with all buffers.

slime73 commented 8 years ago

how the heck do you plan to integrate ads, gamecenter, etc with it? :/

SDL doesn't provide APIs for those itself (that's way outside the scope of the library), but it doesn't stop you from having them.

SDL_GL_CreateContext also creates EAGLContext and framebuffers, while what we want here is for SDL to create just a UIWindow + UIView and let bgfx create and own EAGLContext with all buffers.

From what I understand based on what @bkaradzic wrote, BGFX supports both cases of the platform API (i.e. SDL) creating its own context and backbuffers, and the case where it doesn't. SDL lets you do either as well, by calling or not calling SDL_GL_CreateContext. If BGFX doesn't properly handle the platform layer not creating an EAGLLayer when a platform backbuffer isn't wanted, that sounds like something that should be fixed in BGFX's code.

slime73 commented 8 years ago

You can manually add a CAEAGLLayer to a UIView by creating one and calling addSublayer on the SDL UIView's base CALayer, in BGFX's code (not modifying SDL at all).

jpmcmu commented 7 years ago

I was able to get SDL to work with BGFX, but none of the above worked for me. The overall outline of the solution I used is:

Example code:

#include <video/uikit/SDL_uikitview.h>
#include <SDL_syswm.h>

@interface SDL_openglview : SDL_uikitview

@end

@implementation SDL_openglview
    + (Class)layerClass
    {
        return [CAEAGLLayer class];
    }
@end

SDL_SysWMinfo wmi;
SDL_GetWindowWMInfo(window, &wmi);
SDL_openglview* view = [[SDL_openglview alloc] initWithFrame: CGRectMake(0,0,0,0) ];

// This function will automatically remove the default view created by SDL and 
// and attach our newly created view
[view setSDLWindow: window];

float scale = 1.0f;
#ifdef __IPHONE_8_0
if ([ [UIScreen mainScreen] respondsToSelector:@selector(nativeScale)]) {
    scale = [UIScreen mainScreen].nativeScale;
} else
#endif
{
    scale = [UIScreen mainScreen].scale;
}
[view setContentScaleFactor: scale];
hsdk123 commented 7 years ago

Is this currently being looked into? I'm finding myself unsure whether to use BGFX due to this apparent instability on iOS.

bkaradzic commented 7 years ago

I'm finding myself unsure whether to use BGFX due to this apparent instability on iOS.

This is SDL related issue on iOS, not bgfx iOS issue (there are games that ship on iOS with bgfx). I'm not looking at it. Someone who is using SDL on iOS should submit PR with fix.

slime73 commented 7 years ago

I don't think this is a SDL source code issue at all - you just have to (in your code that uses BGFX) create a CAEAGLLayer or a CAEAGLLayer-backed view, and attach it to the SDL window's main UIView obtained via SDL_GetWindowWMInfo.

This is because SDL doesn't create an OpenGL surface on iOS unless you use SDL_GL_CreateContext, but it still provides you with enough tools to create an OpenGL surface yourself if you don't use CreateContext.

My Metal example in one of my previous replies to this thread does the same thing (sans BGFX API calls), but with Metal instead of OpenGL ES.