Opentk/Source/OpenTK/Platform/SDL2/Sdl2GraphicsContext.cs
Stefanos A. 498f16c2bd Improved diagnostics
This helps when tracking down GraphicsContext-related issues in SDL2.
2013-12-16 10:39:54 +01:00

365 lines
12 KiB
C#

#region License
//
// The Open Toolkit Library License
//
// Copyright (c) 2006 - 2013 Stefanos Apostolopoulos for the Open Toolkit library.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
#endregion
using System;
using System.Diagnostics;
using OpenTK;
using OpenTK.Graphics;
namespace OpenTK.Platform.SDL2
{
class Sdl2GraphicsContext : DesktopGraphicsContext
{
IWindowInfo Window { get; set; }
ContextHandle SdlContext { get; set; }
Sdl2GraphicsContext(IWindowInfo window)
{
// It is possible to create a GraphicsContext on a window
// that is not owned by SDL (e.g. a GLControl). In that case,
// we need to use SDL_CreateWindowFrom in order to
// convert the foreign window to a SDL window.
if (window is Sdl2WindowInfo)
{
Window = window;
}
else
{
Window = new Sdl2WindowInfo(
SDL.CreateWindowFrom(window.Handle),
null);
}
}
public Sdl2GraphicsContext(GraphicsMode mode,
IWindowInfo win, IGraphicsContext shareContext,
int major, int minor,
OpenTK.Graphics.GraphicsContextFlags flags)
: this(win)
{
lock (SDL.Sync)
{
SetGLAttributes(mode, shareContext, major, minor, flags);
SdlContext = new ContextHandle(SDL.GL.CreateContext(Window.Handle));
if (SdlContext == ContextHandle.Zero)
{
var error = SDL.GetError();
Debug.Print("SDL2 failed to create OpenGL context: {0}", error);
throw new GraphicsContextException(error);
}
Mode = GetGLAttributes(SdlContext, out flags);
}
Handle = GraphicsContext.GetCurrentContext();
Debug.Print("SDL2 created GraphicsContext (handle: {0})", Handle);
Debug.Print(" GraphicsMode: {0}", Mode);
Debug.Print(" GraphicsContextFlags: {0}", flags);
}
#region Private Members
static GraphicsMode GetGLAttributes(ContextHandle sdlContext, out GraphicsContextFlags context_flags)
{
context_flags = 0;
int accum_red, accum_green, accum_blue, accum_alpha;
SDL.GL.GetAttribute(ContextAttribute.ACCUM_RED_SIZE, out accum_red);
SDL.GL.GetAttribute(ContextAttribute.ACCUM_GREEN_SIZE, out accum_green);
SDL.GL.GetAttribute(ContextAttribute.ACCUM_BLUE_SIZE, out accum_blue);
SDL.GL.GetAttribute(ContextAttribute.ACCUM_ALPHA_SIZE, out accum_alpha);
int buffers;
SDL.GL.GetAttribute(ContextAttribute.DOUBLEBUFFER, out buffers);
// DOUBLEBUFFER return a boolean (0-false, 1-true), so we need
// to adjust the buffer count (false->1 buffer, true->2 buffers)
buffers++;
int red, green, blue, alpha;
SDL.GL.GetAttribute(ContextAttribute.RED_SIZE, out red);
SDL.GL.GetAttribute(ContextAttribute.GREEN_SIZE, out green);
SDL.GL.GetAttribute(ContextAttribute.BLUE_SIZE, out blue);
SDL.GL.GetAttribute(ContextAttribute.ALPHA_SIZE, out alpha);
int depth, stencil;
SDL.GL.GetAttribute(ContextAttribute.DEPTH_SIZE, out depth);
SDL.GL.GetAttribute(ContextAttribute.STENCIL_SIZE, out stencil);
int samples;
SDL.GL.GetAttribute(ContextAttribute.MULTISAMPLESAMPLES, out samples);
int stereo;
SDL.GL.GetAttribute(ContextAttribute.STEREO, out stereo);
int major, minor;
SDL.GL.GetAttribute(ContextAttribute.CONTEXT_MAJOR_VERSION, out major);
SDL.GL.GetAttribute(ContextAttribute.CONTEXT_MINOR_VERSION, out minor);
int flags;
SDL.GL.GetAttribute(ContextAttribute.CONTEXT_FLAGS, out flags);
int egl;
SDL.GL.GetAttribute(ContextAttribute.CONTEXT_EGL, out egl);
int profile;
SDL.GL.GetAttribute(ContextAttribute.CONTEXT_PROFILE_MASK, out profile);
if (egl != 0 && (profile & (int)ContextProfileFlags.ES) != 0)
{
context_flags |= GraphicsContextFlags.Embedded;
}
if ((flags & (int)ContextFlags.DEBUG) != 0)
{
context_flags |= GraphicsContextFlags.Debug;
}
if ((profile & (int)ContextProfileFlags.CORE) != 0)
{
context_flags |= GraphicsContextFlags.ForwardCompatible;
}
return new GraphicsMode(
new ColorFormat(red, green, blue, alpha),
depth,
stencil,
samples,
new ColorFormat(accum_red, accum_green, accum_blue, accum_alpha),
buffers,
stereo != 0 ? true : false);
}
static void SetGLAttributes(GraphicsMode mode,
IGraphicsContext shareContext,
int major, int minor,
GraphicsContextFlags flags)
{
if (mode.AccumulatorFormat.BitsPerPixel > 0)
{
SDL.GL.SetAttribute(ContextAttribute.ACCUM_ALPHA_SIZE, mode.AccumulatorFormat.Alpha);
SDL.GL.SetAttribute(ContextAttribute.ACCUM_RED_SIZE, mode.AccumulatorFormat.Red);
SDL.GL.SetAttribute(ContextAttribute.ACCUM_GREEN_SIZE, mode.AccumulatorFormat.Green);
SDL.GL.SetAttribute(ContextAttribute.ACCUM_BLUE_SIZE, mode.AccumulatorFormat.Blue);
}
if (mode.Buffers > 0)
{
SDL.GL.SetAttribute(ContextAttribute.DOUBLEBUFFER, mode.Buffers > 1 ? 1 : 0);
}
if (mode.ColorFormat > 0)
{
SDL.GL.SetAttribute(ContextAttribute.ALPHA_SIZE, mode.ColorFormat.Alpha);
SDL.GL.SetAttribute(ContextAttribute.RED_SIZE, mode.ColorFormat.Red);
SDL.GL.SetAttribute(ContextAttribute.GREEN_SIZE, mode.ColorFormat.Green);
SDL.GL.SetAttribute(ContextAttribute.BLUE_SIZE, mode.ColorFormat.Blue);
}
if (mode.Depth > 0)
{
SDL.GL.SetAttribute(ContextAttribute.DEPTH_SIZE, mode.Depth);
}
if (mode.Samples > 0)
{
SDL.GL.SetAttribute(ContextAttribute.MULTISAMPLEBUFFERS, 1);
SDL.GL.SetAttribute(ContextAttribute.MULTISAMPLESAMPLES, mode.Samples);
}
if (mode.Stencil > 0)
{
SDL.GL.SetAttribute(ContextAttribute.STENCIL_SIZE, mode.Stereo ? 1 : 0);
}
if (mode.Stereo)
{
SDL.GL.SetAttribute(ContextAttribute.STEREO, 1);
}
if (major > 0)
{
SDL.GL.SetAttribute(ContextAttribute.CONTEXT_MAJOR_VERSION, major);
SDL.GL.SetAttribute(ContextAttribute.CONTEXT_MINOR_VERSION, minor);
}
if ((flags & GraphicsContextFlags.Debug) != 0)
{
SDL.GL.SetAttribute(ContextAttribute.CONTEXT_FLAGS, ContextFlags.DEBUG);
}
/*
if ((flags & GraphicsContextFlags.Robust) != 0)
{
SDL.GL.SetAttribute(ContextAttribute.CONTEXT_FLAGS, ContextFlags.ROBUST_ACCESS_FLAG);
}
if ((flags & GraphicsContextFlags.ResetIsolation) != 0)
{
SDL.GL.SetAttribute(ContextAttribute.CONTEXT_FLAGS, ContextFlags.RESET_ISOLATION_FLAG);
}
*/
{
ContextProfileFlags cpflags = 0;
if ((flags & GraphicsContextFlags.Embedded) != 0)
{
cpflags |= ContextProfileFlags.ES;
SDL.GL.SetAttribute(ContextAttribute.CONTEXT_EGL, 1);
}
if ((flags & GraphicsContextFlags.ForwardCompatible) != 0)
{
cpflags |= ContextProfileFlags.CORE;
}
if (cpflags != 0)
{
SDL.GL.SetAttribute(ContextAttribute.CONTEXT_PROFILE_MASK, cpflags);
}
}
if (shareContext != null)
{
if (shareContext.IsCurrent)
{
SDL.GL.SetAttribute(ContextAttribute.SHARE_WITH_CURRENT_CONTEXT, 1);
}
else
{
Trace.WriteLine("Warning: SDL2 requires a shared context to be current before sharing. Sharing failed.");
}
}
}
#endregion
#region Public Members
public static ContextHandle GetCurrentContext()
{
return new ContextHandle(SDL.GL.GetCurrentContext());
}
#endregion
#region GraphicsContextBase Members
public override void SwapBuffers()
{
SDL.GL.SwapWindow(Window.Handle);
}
public override void MakeCurrent(IWindowInfo window)
{
int result = 0;
if (window != null)
{
result = SDL.GL.MakeCurrent(window.Handle, SdlContext.Handle);
}
else
{
result = SDL.GL.MakeCurrent(IntPtr.Zero, IntPtr.Zero);
}
if (result < 0)
{
Debug.Print("SDL2 MakeCurrent failed with: {0}", SDL.GetError());
}
}
public override IntPtr GetAddress(string function)
{
return SDL.GL.GetProcAddress(function);
}
public override IntPtr GetAddress(IntPtr function)
{
return SDL.GL.GetProcAddress(function);
}
public override bool IsCurrent
{
get
{
return GraphicsContext.GetCurrentContext() == Context;
}
}
public override int SwapInterval
{
get
{
return SDL.GL.GetSwapInterval();
}
set
{
if (SDL.GL.SetSwapInterval(value) < 0)
{
Debug.Print("SDL2 failed to set swap interval: {0}", SDL.GetError());
}
}
}
#endregion
#region IDisposable Members
void Dispose(bool manual)
{
if (!IsDisposed)
{
if (manual)
{
Debug.Print("Disposing {0} (handle: {1})", GetType(), Handle);
lock (SDL.Sync)
{
SDL.GL.DeleteContext(SdlContext.Handle);
}
}
else
{
Debug.Print("Sdl2GraphicsContext (handle: {0}) leaked, did you forget to call Dispose()?",
Handle);
}
IsDisposed = true;
}
}
public override void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~Sdl2GraphicsContext()
{
Dispose(false);
}
#endregion
}
}