SFML/src/SFML/Window/OSX/SFContext.mm

314 lines
9.9 KiB
Plaintext

////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2019 Marco Antognini (antognini.marco@gmail.com),
// Laurent Gomila (laurent@sfml-dev.org)
//
// 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/OSX/SFContext.hpp>
#include <SFML/Window/OSX/WindowImplCocoa.hpp>
#include <SFML/System/Err.hpp>
#include <dlfcn.h>
#include <stdint.h>
#import <SFML/Window/OSX/AutoreleasePoolWrapper.h>
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
SFContext::SFContext(SFContext* shared) :
m_context(0),
m_view(0),
m_window(0)
{
// Ask for a pool.
ensureThreadHasPool();
// Create the context
createContext(shared,
VideoMode::getDesktopMode().bitsPerPixel,
ContextSettings(0, 0, 0));
}
////////////////////////////////////////////////////////////
SFContext::SFContext(SFContext* shared, const ContextSettings& settings,
const WindowImpl* owner, unsigned int bitsPerPixel) :
m_context(0),
m_view(0),
m_window(0)
{
// Ask for a pool.
ensureThreadHasPool();
// Create the context.
createContext(shared, bitsPerPixel, settings);
// Apply context.
const WindowImplCocoa* ownerCocoa = static_cast<const WindowImplCocoa*>(owner);
ownerCocoa->applyContext(m_context);
}
////////////////////////////////////////////////////////////
SFContext::SFContext(SFContext* shared, const ContextSettings& settings,
unsigned int width, unsigned int height) :
m_context(0),
m_view(0),
m_window(0)
{
// Ensure the process is setup in order to create a valid window.
WindowImplCocoa::setUpProcess();
// Ask for a pool.
ensureThreadHasPool();
// Create the context.
createContext(shared, VideoMode::getDesktopMode().bitsPerPixel, settings);
// Create a dummy window/view pair (hidden) and assign it our context.
m_window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, width, height)
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:NO]; // Don't defer it!
m_view = [[NSOpenGLView alloc] initWithFrame:NSMakeRect(0, 0, width, height)];
[m_window setContentView:m_view];
[m_view setOpenGLContext:m_context];
[m_context setView:m_view];
}
////////////////////////////////////////////////////////////
SFContext::~SFContext()
{
// Notify unshared OpenGL resources of context destruction
cleanupUnsharedResources();
[m_context clearDrawable];
if (m_context == [NSOpenGLContext currentContext])
[NSOpenGLContext clearCurrentContext];
[m_context release];
[m_view release]; // Might be nil but we don't care.
[m_window release]; // Idem.
}
////////////////////////////////////////////////////////////
GlFunctionPointer SFContext::getFunction(const char* name)
{
static void* image = NULL;
if (!image)
image = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_LAZY);
return (image ? reinterpret_cast<GlFunctionPointer>(reinterpret_cast<intptr_t>(dlsym(image, name))) : 0);
}
////////////////////////////////////////////////////////////
bool SFContext::makeCurrent(bool current)
{
if (current)
{
[m_context makeCurrentContext];
return m_context == [NSOpenGLContext currentContext]; // Should be true.
}
else
{
[NSOpenGLContext clearCurrentContext];
return m_context != [NSOpenGLContext currentContext]; // Should be true.
}
}
////////////////////////////////////////////////////////////
void SFContext::display()
{
[m_context flushBuffer];
}
////////////////////////////////////////////////////////////
void SFContext::setVerticalSyncEnabled(bool enabled)
{
GLint swapInterval = enabled ? 1 : 0;
[m_context setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
}
////////////////////////////////////////////////////////////
void SFContext::createContext(SFContext* shared,
unsigned int bitsPerPixel,
const ContextSettings& settings)
{
// Save the settings. (OpenGL version is updated elsewhere.)
m_settings = settings;
// Choose the attributes of OGL context.
std::vector<NSOpenGLPixelFormatAttribute> attrs;
attrs.reserve(20); // max attributes (estimation).
// These casts are safe. C++ is much more strict than Obj-C.
attrs.push_back(NSOpenGLPFAClosestPolicy);
attrs.push_back(NSOpenGLPFADoubleBuffer);
if (bitsPerPixel > 24)
{
attrs.push_back(NSOpenGLPFAAlphaSize);
attrs.push_back((NSOpenGLPixelFormatAttribute)8);
}
attrs.push_back(NSOpenGLPFADepthSize);
attrs.push_back((NSOpenGLPixelFormatAttribute)m_settings.depthBits);
attrs.push_back(NSOpenGLPFAStencilSize);
attrs.push_back((NSOpenGLPixelFormatAttribute)m_settings.stencilBits);
if (m_settings.antialiasingLevel > 0)
{
/*
* Antialiasing techniques are described in the
* "OpenGL Programming Guide for Mac OS X" document.
*
* According to this document, the specification currently allows
* only one multisample buffer.
*
* The document also states that software renderers should be avoided
* because antialisaing techniques are very slow with them.
*/
// Prefer multisampling over supersampling
attrs.push_back(NSOpenGLPFAMultisample);
// Only one buffer is currently available
attrs.push_back(NSOpenGLPFASampleBuffers);
attrs.push_back((NSOpenGLPixelFormatAttribute)1);
// Antialiasing level
attrs.push_back(NSOpenGLPFASamples);
attrs.push_back((NSOpenGLPixelFormatAttribute)m_settings.antialiasingLevel);
// No software renderer - only hardware renderer
attrs.push_back(NSOpenGLPFAAccelerated);
}
// Support for OpenGL 3.2 on Mac OS X Lion and later:
// SFML 2 Graphics module uses some OpenGL features that are deprecated in
// OpenGL 3.0 and that are no longer available in 3.1 and 3.2+ with a core context.
// Therefore the Graphics module won't work as expected.
// 1.x/2.x are mapped to 2.1 since Apple only support that legacy version.
// >=3.0 are mapped to a 3.2 core profile.
bool legacy = m_settings.majorVersion < 3;
if (legacy)
{
m_settings.attributeFlags &= ~ContextSettings::Core;
m_settings.majorVersion = 2;
m_settings.minorVersion = 1;
attrs.push_back(NSOpenGLPFAOpenGLProfile);
attrs.push_back(NSOpenGLProfileVersionLegacy);
}
else
{
if (!(m_settings.attributeFlags & ContextSettings::Core))
{
sf::err() << "Warning. Compatibility profile not supported on this platform." << std::endl;
m_settings.attributeFlags |= ContextSettings::Core;
}
m_settings.majorVersion = 3;
m_settings.minorVersion = 2;
attrs.push_back(NSOpenGLPFAOpenGLProfile);
attrs.push_back(NSOpenGLProfileVersion3_2Core);
}
if (m_settings.attributeFlags & ContextSettings::Debug)
{
sf::err() << "Warning. OpenGL debugging not supported on this platform." << std::endl;
m_settings.attributeFlags &= ~ContextSettings::Debug;
}
attrs.push_back((NSOpenGLPixelFormatAttribute)0); // end of array
// All OS X pixel formats are sRGB capable
m_settings.sRgbCapable = true;
// Create the pixel format.
NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:&attrs[0]];
if (pixFmt == nil)
{
sf::err() << "Error. Unable to find a suitable pixel format." << std::endl;
return;
}
// Use the shared context if one is given.
NSOpenGLContext* sharedContext = shared != NULL ? shared->m_context : nil;
if (sharedContext != nil)
{
[NSOpenGLContext clearCurrentContext];
if (sharedContext == [NSOpenGLContext currentContext])
{
sf::err() << "Failed to deactivate shared context before sharing" << std::endl;
return;
}
}
// Create the context.
m_context = [[NSOpenGLContext alloc] initWithFormat:pixFmt
shareContext:sharedContext];
if (m_context == nil)
{
sf::err() << "Error. Unable to create the context. Retrying without shared context." << std::endl;
m_context = [[NSOpenGLContext alloc] initWithFormat:pixFmt
shareContext:nil];
if (m_context == nil)
sf::err() << "Error. Unable to create the context." << std::endl;
else
sf::err() << "Warning. New context created without shared context." << std::endl;
}
// Free up.
[pixFmt release];
}
} // namespace priv
} // namespace sf