SFML/src/SFML/Window/iOS/EaglContext.mm
2014-11-17 17:32:48 +01:00

235 lines
7.8 KiB
Plaintext

////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2013 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Window/iOS/EaglContext.hpp>
#include <SFML/Window/iOS/WindowImplUIKit.hpp>
#include <SFML/Window/iOS/SFView.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/System/Sleep.hpp>
#include <OpenGLES/EAGL.h>
#include <OpenGLES/EAGLDrawable.h>
#include <OpenGLES/ES1/glext.h>
#include <QuartzCore/CAEAGLLayer.h>
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
EaglContext::EaglContext(EaglContext* shared) :
m_context (nil),
m_framebuffer (0),
m_colorbuffer (0),
m_depthbuffer (0),
m_vsyncEnabled(false),
m_clock ()
{
// Create the context
if (shared)
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1 sharegroup:[shared->m_context sharegroup]];
else
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
}
////////////////////////////////////////////////////////////
EaglContext::EaglContext(EaglContext* shared, const ContextSettings& settings,
const WindowImpl* owner, unsigned int bitsPerPixel) :
m_context (nil),
m_framebuffer (0),
m_colorbuffer (0),
m_depthbuffer (0),
m_vsyncEnabled(false),
m_clock ()
{
const WindowImplUIKit* window = static_cast<const WindowImplUIKit*>(owner);
createContext(shared, window, bitsPerPixel, settings);
}
////////////////////////////////////////////////////////////
EaglContext::EaglContext(EaglContext* shared, const ContextSettings& settings,
unsigned int width, unsigned int height) :
m_context (nil),
m_framebuffer (0),
m_colorbuffer (0),
m_depthbuffer (0),
m_vsyncEnabled(false),
m_clock ()
{
// This constructor should never be used by implementation
err() << "Calling bad EaglContext constructor, please contact your developer :)" << std::endl;
}
////////////////////////////////////////////////////////////
EaglContext::~EaglContext()
{
if (m_context)
{
// Activate the context, so that we can destroy the buffers
EAGLContext* previousContext = [EAGLContext currentContext];
[EAGLContext setCurrentContext:m_context];
// Destroy the buffers
if (m_framebuffer)
glDeleteFramebuffersOES(1, &m_framebuffer);
if (m_colorbuffer)
glDeleteRenderbuffersOES(1, &m_colorbuffer);
if (m_depthbuffer)
glDeleteRenderbuffersOES(1, &m_depthbuffer);
// Restore the previous context
[EAGLContext setCurrentContext:previousContext];
}
}
////////////////////////////////////////////////////////////
void EaglContext::recreateRenderBuffers(SFView* glView)
{
// Activate the context
EAGLContext* previousContext = [EAGLContext currentContext];
[EAGLContext setCurrentContext:m_context];
// Bind the frame buffer
glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_framebuffer);
// Destroy previous render-buffers
if (m_colorbuffer)
glDeleteRenderbuffersOES(1, &m_colorbuffer);
if (m_depthbuffer)
glDeleteRenderbuffersOES(1, &m_depthbuffer);
// Create the color buffer
glGenRenderbuffersOES(1, &m_colorbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorbuffer);
if (glView)
[m_context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)glView.layer];
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, m_colorbuffer);
// Create a depth buffer if requested
if (m_settings.depthBits > 0)
{
// Find the best internal format
GLenum format = m_settings.depthBits > 16 ? GL_DEPTH_COMPONENT24_OES : GL_DEPTH_COMPONENT16_OES;
// Get the size of the color-buffer (which fits the current size of the GL view)
GLint width, height;
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &width);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &height);
// Create the depth buffer
glGenRenderbuffersOES(1, &m_depthbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_depthbuffer);
glRenderbufferStorageOES(GL_RENDERBUFFER_OES, format, width, height);
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, m_depthbuffer);
}
// Make sure that everything's ok
GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
if (status != GL_FRAMEBUFFER_COMPLETE_OES)
err() << "Failed to create a valid frame buffer (error code: " << status << ")" << std::endl;
// Restore the previous context
[EAGLContext setCurrentContext:previousContext];
}
////////////////////////////////////////////////////////////
bool EaglContext::makeCurrent()
{
return [EAGLContext setCurrentContext:m_context];
}
////////////////////////////////////////////////////////////
void EaglContext::display()
{
glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_colorbuffer);
[m_context presentRenderbuffer:GL_RENDERBUFFER_OES];
// The proper way of doing v-sync on iOS would be to use CADisplayLink
// notifications, but it is not compatible with the way SFML is designed;
// therefore we fake it with a manual framerate limit
if (m_vsyncEnabled)
{
static const Time frameDuration = seconds(1.f / 60.f);
sleep(frameDuration - m_clock.getElapsedTime());
m_clock.restart();
}
}
////////////////////////////////////////////////////////////
void EaglContext::setVerticalSyncEnabled(bool enabled)
{
m_vsyncEnabled = enabled;
}
////////////////////////////////////////////////////////////
void EaglContext::createContext(EaglContext* shared,
const WindowImplUIKit* window,
unsigned int bitsPerPixel,
const ContextSettings& settings)
{
// Save the settings
m_settings = settings;
// Adjust the depth buffer format to those available
if (m_settings.depthBits > 16)
m_settings.depthBits = 24;
else if (m_settings.depthBits > 0)
m_settings.depthBits = 16;
// Create the context
if (shared)
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1 sharegroup:[shared->m_context sharegroup]];
else
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
// Activate it
makeCurrent();
// Create the framebuffer (this is the only allowed drawable on iOS)
glGenFramebuffersOES(1, &m_framebuffer);
// Create the render buffers
recreateRenderBuffers(window->getGlView());
// Attach the context to the GL view for future updates
window->getGlView().context = this;
}
} // namespace priv
} // namespace sf