blob: 6c49e4ca40996f3297f7ecd71a59594ebab21399 [file] [log] [blame]
/*
Copyright (c) 2011-2013 NVIDIA Corporation
Copyright (c) 2011-2012 Cass Everitt
Copyright (c) 2012 Scott Nations
Copyright (c) 2012 Mathias Schott
Copyright (c) 2012 Nigel Stewart
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "pch.h" /* For MS precompiled header support */
#include "RegalUtil.h"
REGAL_GLOBAL_BEGIN
#include <boost/print/json.hpp>
#include <boost/print/print_string.hpp>
using boost::print::print_string;
#include <map>
using namespace std;
#include "RegalLog.h"
#include "RegalInit.h"
#include "RegalHttp.h"
#include "RegalJson.h"
#include "RegalToken.h"
#include "RegalConfig.h"
#include "RegalContext.h"
#include "RegalThread.h"
#include "RegalDispatcher.h"
#include "RegalContextInfo.h"
#include "RegalPpa.h"
#include "RegalMutex.h"
REGAL_GLOBAL_END
REGAL_NAMESPACE_BEGIN
using Token::toString;
namespace Json { struct Output : public ::boost::print::json::output<std::string> {}; }
static ::REGAL_NAMESPACE_INTERNAL::Init *_init = NULL;
#if !defined(REGAL_NAMESPACE) && REGAL_SYS_WGL
// Phony advapi32.dll, gdi32.dll and user32.dll dependencies for
// closely matching opengl32.dll
extern "C" { void __stdcall RegCloseKey(void *); }
extern "C" { void __stdcall DeleteDC (void *); }
extern "C" { void __stdcall GetFocus (void); }
extern "C" { static void (__stdcall * myRegCloseKey)(void *) = RegCloseKey; }
extern "C" { static void (__stdcall * myDeleteDC )(void *) = DeleteDC; }
extern "C" { static void (__stdcall * myGetFocus )(void ) = GetFocus; }
#endif
typedef map<RegalSystemContext, RegalContext *> SC2RC;
typedef map<Thread::Thread, RegalContext *> TH2RC;
SC2RC sc2rc;
TH2RC th2rc;
Thread::Mutex *sc2rcMutex = NULL;
Thread::Mutex *th2rcMutex = NULL;
Init::Init()
{
atexit(atExit);
#if !defined(REGAL_NAMESPACE) && REGAL_SYS_WGL
// Check our phony advapi32.dll, gdi32.dll and user32.dll dependencies
// to prevent them being optimized out of a release-mode binary.
// NOTE - these function pointers should _not_ be called, ever!
if (!myRegCloseKey || !myDeleteDC || !myGetFocus)
return;
#endif
// If a JSON config file is to be used, parse it first
#ifndef REGAL_NO_GETENV
const char *tmp = GetEnv( "REGAL_CONFIG_FILE" );
if (tmp) Config::configFile = tmp;
#endif
#ifdef REGAL_CONFIG_FILE
Config::configFile = REGAL_EQUOTE(REGAL_CONFIG_FILE);
#endif
#if !REGAL_NO_JSON
if (Config::configFile.length())
{
bool ok = Json::Parser::parseFile(Config::configFile);
if (!ok)
Warning("Failed to parse configuration from ",Config::configFile);
}
#endif
//
Logging::Init();
Config::Init();
if (Config::enableThreadLocking)
{
sc2rcMutex = new Thread::Mutex();
th2rcMutex = new Thread::Mutex();
Logging::createLocks();
}
Http::Init();
Http::Start();
}
Init::~Init()
{
//
// Write out the Regal configuration file as JSON
//
#if !REGAL_NO_JSON
if (Config::configFile.length())
{
Json::Output jo;
jo.object();
jo.object("regal");
Config::writeJSON(jo);
Logging::writeJSON(jo);
jo.end();
jo.end();
FILE *f = fopen(Config::configFile.c_str(),"wt");
if (f)
{
string tmp = jo.str();
fwrite(tmp.c_str(),1,tmp.length(),f);
fclose(f);
Info("Regal configuration written to ",Config::configFile);
}
else
{
Warning("Regal configuration could not be written to ",Config::configFile);
}
}
#endif
//
// Shutdown...
//
Http::Stop();
Logging::Cleanup();
delete sc2rcMutex;
delete th2rcMutex;
sc2rcMutex = NULL;
th2rcMutex = NULL;
}
void
Init::init()
{
if (!_init)
_init = new ::REGAL_NAMESPACE_INTERNAL::Init();
}
void
Init::atExit()
{
if (_init)
{
delete _init;
_init = NULL;
}
}
//
RegalContext *
Init::getContext(RegalSystemContext sysCtx)
{
RegalAssert(sysCtx);
Thread::ScopedLock lock(sc2rcMutex);
SC2RC::iterator i = sc2rc.find(sysCtx);
if (i!=sc2rc.end())
{
Internal("Init::context", "lookup for sysCtx=",sysCtx);
return i->second;
}
else
{
Internal("Init::context", "factory for sysCtx=",sysCtx);
RegalContext *context = new RegalContext();
RegalAssert(context);
sc2rc[sysCtx] = context;
context->sysCtx = sysCtx;
return context;
}
}
void
Init::setContext(RegalContext *context)
{
Thread::Thread thread = Thread::Self();
Internal("Init::setContext","thread=",::boost::print::hex(Thread::threadId())," context=",context," ",context ? context->info->version : "");
// std::map lookup
Thread::ScopedLock lock(th2rcMutex);
TH2RC::iterator i = th2rc.find(thread);
// Associate this thread with the Regal context
if (i!=th2rc.end())
{
// If some other context is associated
// with this thread, disassociate it.
if (i->second!=context)
{
if (i->second)
{
RegalAssert(i->second->thread==thread);
i->second->thread = 0;
}
i->second = context;
}
}
else
th2rc[thread] = context;
if (context && context->thread!=thread)
{
// If some other thread is associated
// with this context, disassociate it.
th2rc.erase(context->thread);
// Associate the context with this thread.
context->thread = thread;
}
setContextTLS(context);
}
//
// TLS Stuff
//
#if REGAL_SYS_WGL
extern "C" { DWORD __stdcall GetCurrentThreadId(void); }
#endif
namespace Thread
{
#if REGAL_NO_TLS
ThreadLocal ThreadLocal::_instance;
#else
#if REGAL_SYS_WGL
#if REGAL_WIN_TLS
DWORD ThreadLocal::_instanceIndex(DWORD(~0));
#else
__declspec(thread) ThreadLocal ThreadLocal::_instance;
#endif
#else
pthread_key_t ThreadLocal::_instanceKey(~0);
#endif
#endif
struct ThreadLocalInit
{
ThreadLocalInit()
{
#if !REGAL_NO_TLS
#if REGAL_SYS_WGL
#if REGAL_WIN_TLS
ThreadLocal::_instanceIndex = TlsAlloc();
#endif
#else
pthread_key_create(&ThreadLocal::_instanceKey, NULL);
#endif
#endif
}
~ThreadLocalInit()
{
#if !REGAL_NO_TLS
#if REGAL_SYS_WGL
#if REGAL_WIN_TLS
TlsFree(ThreadLocal::_instanceIndex);
#endif
#else
// TODO ThreadLocal::_instanceKey
#endif
#endif
}
};
ThreadLocalInit threadLocalInit;
}
void
Init::setContextTLS(RegalContext *context)
{
Internal("Init::setContextTLS","thread=",::boost::print::hex(Thread::threadId())," context=",context);
Thread::ThreadLocal &instance = Thread::ThreadLocal::instance();
instance.currentContext = context;
}
void
Init::checkForGLErrors(RegalContext *context)
{
RegalAssert(context);
GLenum err = context->dispatcher.driver.glGetError();
if (err!=GL_NO_ERROR)
Error("GL error = ",toString(err));
}
//
// API Methods
//
RegalErrorCallback
Init::setErrorCallback(RegalErrorCallback callback)
{
init();
// TODO - warning or error for context==NULL ?
RegalContext *context = REGAL_GET_CONTEXT();
RegalAssert(context);
return context ? context->err.callback = callback : NULL;
}
void
Init::configure(const char *json)
{
#if !REGAL_NO_JSON
bool ok = Json::Parser::parseString(json);
if (!ok)
Warning("Failed to parse configuration from RegalConfigure call.");
#endif
}
void
Init::shareContext(RegalSystemContext a, RegalSystemContext b)
{
init();
RegalContext *contextA = getContext(a);
RegalContext *contextB = getContext(b);
RegalAssert(contextA);
RegalAssert(contextB);
// Either of the groups of contexts needs to be uninitialized.
// In principle Regal might be able to merge the shared
// containers together, but that's not currently implemented.
if (contextA->groupInitializedContext() && contextB->groupInitializedContext())
{
Warning("Regal can't share initialized context groups.");
RegalAssert(false);
return;
}
// Share all the Regal contexts in b into a
std::list<RegalContext *> tmp = *contextB->shareGroup;
for (std::list<RegalContext *>::iterator i = tmp.begin(); i!=tmp.end(); ++i)
{
RegalAssert(*i);
contextA->shareGroup->push_back(*i);
(*i)->shareGroup = contextA->shareGroup;
}
}
void
#if REGAL_SYS_PPAPI
Init::makeCurrent(RegalSystemContext sysCtx, PPB_OpenGLES2 *ppb_interface)
#else
Init::makeCurrent(RegalSystemContext sysCtx)
#endif
{
init();
Internal("Init::makeCurrent","thread=",::boost::print::hex(Thread::threadId())," sysCtx=",sysCtx);
if (sysCtx)
{
RegalContext *context = getContext(sysCtx);
RegalAssert(context);
// Do RegalContext initialization, if necessary.
if (!context->initialized)
{
// Set regal context TLS for initialization purposes
// This is needed for Thread::CurrentContext on Mac OSX
setContextTLS(context);
#if REGAL_SYS_PPAPI
context->ppapiResource = sysCtx;
context->ppapiES2 = ppb_interface;
#endif
// RegalContextInfo init makes GL calls, need an
// active OpenGL context.
context->Init();
RegalAssert(context->initialized);
}
setContext(context);
return;
}
setContext(NULL);
setContextTLS(NULL); // Need to do this in addition to setContext?
}
// Cleanup all the resources associated with sysCtx
// Otherwise, Regal contexts would never be deleted
void
Init::destroyContext(RegalSystemContext sysCtx)
{
if (_init && sysCtx)
{
RegalContext *context = getContext(sysCtx);
if (context)
{
RegalAssert(context->sysCtx==sysCtx);
Thread::ScopedLock thLock(th2rcMutex);
Thread::ScopedLock scLock(sc2rcMutex);
th2rc.erase(context->thread);
sc2rc.erase(sysCtx);
// TODO - clear TLS for other threads too?
if (context==Thread::CurrentContext())
{
context->Cleanup();
setContextTLS(NULL);
}
delete context;
}
}
}
// Output listing of current contexts in HTML; for use by HTTP server
void
Init::getContextListingHTML(std::string &text)
{
static const char *const br = "<br/>\n";
Thread::ScopedLock lock(th2rcMutex);
for (TH2RC::const_iterator i = th2rc.begin(); i!=th2rc.end(); ++i)
{
RegalContext *ctx = i->second;
// Need a per-context read-lock?
text += print_string("ctx = ",ctx,br);
text += br;
if (ctx)
{
if (ctx->info)
{
text += print_string("<b>Vendor </b>:",ctx->info->regalVendor,br);
text += print_string("<b>Renderer </b>:",ctx->info->regalRenderer,br);
text += print_string("<b>Version </b>:",ctx->info->regalVersion,br);
text += print_string("<b>Extensions </b>:",ctx->info->regalExtensions,br);
text += br;
}
#if REGAL_EMULATION
if (ctx->ppa)
{
text += print_string("<b>GL_ACCUM_BUFFER_BIT</b><br/>", ctx->ppa->State::AccumBuffer::toString(br),br);
text += print_string("<b>GL_COLOR_BUFFER_BIT</b><br/>", ctx->ppa->State::ColorBuffer::toString(br),br);
text += print_string("<b>GL_DEPTH_BIT</b><br/>", ctx->ppa->State::Depth::toString(br),br);
text += print_string("<b>GL_ENABLE_BIT</b><br/>", ctx->ppa->State::Enable::toString(br),br);
text += print_string("<b>GL_EVAL_BIT</b><br/>", ctx->ppa->State::Eval::toString(br),br);
text += print_string("<b>GL_FOG_BIT</b><br/>", ctx->ppa->State::Fog::toString(br),br);
text += print_string("<b>GL_HINT_BIT</b><br/>", ctx->ppa->State::Hint::toString(br),br);
text += print_string("<b>GL_LIGHTING_BIT</b><br/>", ctx->ppa->State::Lighting::toString(br),br);
text += print_string("<b>GL_LINE_BIT</b><br/>", ctx->ppa->State::Line::toString(br),br);
text += print_string("<b>GL_LIST_BIT</b><br/>", ctx->ppa->State::List::toString(br),br);
text += print_string("<b>GL_MULTISAMPLE_BIT</b><br/>", ctx->ppa->State::Multisample::toString(br),br);
text += print_string("<b>GL_PIXEL_MODE_BIT</b><br/>", ctx->ppa->State::PixelMode::toString(br),br);
text += print_string("<b>GL_POINT_BIT</b><br/>", ctx->ppa->State::Point::toString(br),br);
text += print_string("<b>GL_POLYGON_BIT</b><br/>", ctx->ppa->State::Polygon::toString(br),br);
text += print_string("<b>GL_POLYGON_STIPPLE_BIT</b><br/>",ctx->ppa->State::PolygonStipple::toString(br),br);
text += print_string("<b>GL_SCISSOR_BIT</b><br/>", ctx->ppa->State::Scissor::toString(br),br);
text += print_string("<b>GL_STENCIL_BUFFER_BIT</b><br/>", ctx->ppa->State::Stencil::toString(br),br);
text += print_string("<b>GL_TRANSFORM_BIT</b><br/>", ctx->ppa->State::Transform::toString(br),br);
text += print_string("<b>GL_VIEWPORT_BIT</b><br/>", ctx->ppa->State::Viewport::toString(br),br);
text += br;
}
#endif
}
}
}
REGAL_NAMESPACE_END
//
// Regal API entry points
//
// These should _not_ be used internally
// Global locking could be implemented in this layer
REGAL_GLOBAL_BEGIN
RegalErrorCallback RegalSetErrorCallback(RegalErrorCallback callback)
{
return ::REGAL_NAMESPACE_INTERNAL::Init::setErrorCallback(callback);
}
void RegalConfigure(const char *json)
{
::REGAL_NAMESPACE_INTERNAL::Init::configure(json);
}
REGAL_DECL void RegalShareContext(RegalSystemContext a, RegalSystemContext b)
{
::REGAL_NAMESPACE_INTERNAL::Init::shareContext(a,b);
}
#if REGAL_SYS_PPAPI
REGAL_DECL void RegalMakeCurrent(RegalSystemContext sysCtx, PPB_OpenGLES2 *ppb_interface)
#else
REGAL_DECL void RegalMakeCurrent(RegalSystemContext sysCtx)
#endif
{
#if REGAL_SYS_PPAPI
::REGAL_NAMESPACE_INTERNAL::Init::makeCurrent(sysCtx,ppb_interface);
#else
::REGAL_NAMESPACE_INTERNAL::Init::makeCurrent(sysCtx);
#endif
}
REGAL_DECL void RegalDestroyContext(RegalSystemContext sysCtx)
{
::REGAL_NAMESPACE_INTERNAL::Init::destroyContext(sysCtx);
}
REGAL_GLOBAL_END