Retrieve all pixel formats at once and select the correct one through a custom selection predicate. Simplifies the code significantly and reduces the chance of race conditions.

This commit is contained in:
the_fiddler 2010-11-08 19:41:24 +00:00
parent d08438785e
commit fd3e1850b7

View file

@ -2,7 +2,7 @@
//
// The Open Toolkit Library License
//
// Copyright (c) 2006 - 2009 the Open Toolkit library.
// Copyright (c) 2006 - 2010 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
@ -28,23 +28,20 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using OpenTK.Graphics;
using ColorDepth = OpenTK.Graphics.ColorFormat;
namespace OpenTK.Platform.Windows
{
internal class WinGraphicsMode : IGraphicsMode
{
// Todo: Get rid of the System.Windows.Forms.Control dependency.
#region --- Fields ---
#region Fields
// To avoid recursion when calling GraphicsMode.Default
bool creating;
readonly List<GraphicsMode> modes = new List<GraphicsMode>();
static readonly object SyncRoot = new object();
#endregion
@ -52,127 +49,135 @@ namespace OpenTK.Platform.Windows
#region --- Constructors ---
public WinGraphicsMode()
{
}
#endregion
#region --- IGraphicsMode Members ---
public GraphicsMode SelectGraphicsMode(ColorDepth color, int depth, int stencil, int samples, ColorDepth accum,
int buffers, bool stereo)
{
lock (SyncRoot)
{
GraphicsMode mode = null;
if (!creating)
using (INativeWindow native = new NativeWindow())
{
creating = true;
try
{
mode = SelectGraphicsModeARB(color, depth, stencil, samples, accum, buffers, stereo);
}
finally
{
if (mode == null)
mode = SelectGraphicsModePFD(color, depth, stencil, samples, accum, buffers, stereo);
}
modes.AddRange(GetModesARB(native));
if (modes.Count == 0)
modes.AddRange(GetModesPFD(native));
}
creating = false;
return mode;
modes.Sort(new GraphicsModeComparer());
}
}
#endregion
#region --- Private Methods ---
#region IGraphicsMode Members
#region SelectGraphicsModePFD
GraphicsMode SelectGraphicsModePFD(ColorDepth color, int depth, int stencil, int samples, ColorDepth accum,
int buffers, bool stereo)
public GraphicsMode SelectGraphicsMode(ColorFormat color, int depth, int stencil, int samples,
ColorFormat accum, int buffers, bool stereo)
{
using (INativeWindow native_window = new NativeWindow())
GraphicsMode mode = null;
do
{
WinWindowInfo window = native_window.WindowInfo as WinWindowInfo;
IntPtr deviceContext = ((WinWindowInfo)window).DeviceContext;
Debug.WriteLine(String.Format("Device context: {0}", deviceContext));
Debug.Write("Selecting pixel format (PFD)... ");
PixelFormatDescriptor pixelFormat = new PixelFormatDescriptor();
pixelFormat.Size = API.PixelFormatDescriptorSize;
pixelFormat.Version = API.PixelFormatDescriptorVersion;
pixelFormat.Flags =
PixelFormatDescriptorFlags.SUPPORT_OPENGL |
PixelFormatDescriptorFlags.DRAW_TO_WINDOW;
pixelFormat.ColorBits = (byte)(color.Red + color.Green + color.Blue);
pixelFormat.PixelType = color.IsIndexed ? PixelType.INDEXED : PixelType.RGBA;
pixelFormat.RedBits = (byte)color.Red;
pixelFormat.GreenBits = (byte)color.Green;
pixelFormat.BlueBits = (byte)color.Blue;
pixelFormat.AlphaBits = (byte)color.Alpha;
if (accum.BitsPerPixel > 0)
mode = modes.Find(delegate(GraphicsMode current)
{
pixelFormat.AccumBits = (byte)(accum.Red + accum.Green + accum.Blue);
pixelFormat.AccumRedBits = (byte)accum.Red;
pixelFormat.AccumGreenBits = (byte)accum.Green;
pixelFormat.AccumBlueBits = (byte)accum.Blue;
pixelFormat.AccumAlphaBits = (byte)accum.Alpha;
return ModeSelector(current, color, depth, stencil, samples, accum, buffers, stereo);
});
} while (mode == null && RelaxParameters(
ref color, ref depth, ref stencil, ref samples, ref accum, ref buffers, ref stereo));
return mode;
}
bool RelaxParameters(ref ColorFormat color, ref int depth, ref int stencil, ref int samples,
ref ColorFormat accum, ref int buffers, ref bool stereo)
{
if (stereo) { stereo = false; return true; }
if (buffers != 2) { buffers = 2; return true; }
if (accum != 0) { accum = 0; return true; }
if (samples != 0) { samples = 0; return true; }
if (color == 32 && depth == 24 && stencil != 8) { color = 32; depth = 24; stencil = 8; return true; }
if (color == 32 && depth == 24 && stencil == 8) { color = 32; depth = 24; stencil = 0; return true; }
if (color == 32 && depth != 16) { color = 32; depth = 16; stencil = 0; return true; }
if (color == 24 && depth == 24 && stencil != 8) { color = 24; depth = 24; stencil = 8; return true; }
if (color == 24 && depth == 24 && stencil == 8) { color = 24; depth = 24; stencil = 0; return true; }
if (color == 24 && depth != 16) { color = 24; depth = 16; stencil = 0; return true; }
if (color == 16 && depth == 24 && stencil != 8) { color = 16; depth = 24; stencil = 8; return true; }
if (color == 16 && depth == 24 && stencil == 8) { color = 16; depth = 24; stencil = 0; return true; }
if (color == 16 && depth != 16) { color = 16; depth = 16; stencil = 0; return true; }
if (color < 16) { color = 16; return true; }
return false;
}
#endregion
#region Private Methods
#region DescribePixelFormat
static int DescribePixelFormat(IntPtr hdc, int ipfd, int cjpfd, ref PixelFormatDescriptor pfd)
{
unsafe
{
fixed (PixelFormatDescriptor* ppfd = &pfd)
{
// Note: DescribePixelFormat found in gdi32 is extremely slow
// on nvidia, for some reason.
return Wgl.Imports.DescribePixelFormat(hdc, ipfd, (uint)cjpfd, ppfd);
}
}
}
pixelFormat.DepthBits = (byte)depth;
pixelFormat.StencilBits = (byte)stencil;
#endregion
if (depth <= 0) pixelFormat.Flags |= PixelFormatDescriptorFlags.DEPTH_DONTCARE;
if (stereo) pixelFormat.Flags |= PixelFormatDescriptorFlags.STEREO;
if (buffers > 1) pixelFormat.Flags |= PixelFormatDescriptorFlags.DOUBLEBUFFER;
#region GetModesPFD
int pixel = Functions.ChoosePixelFormat(deviceContext, ref pixelFormat);
if (pixel == 0)
throw new GraphicsModeException("The requested GraphicsMode is not available.");
IEnumerable<GraphicsMode> GetModesPFD(INativeWindow native)
{
WinWindowInfo window = native.WindowInfo as WinWindowInfo;
IntPtr deviceContext = ((WinWindowInfo)window).DeviceContext;
Debug.WriteLine(String.Format("Device context: {0}", deviceContext));
Debug.WriteLine("Retrieving PFD pixel formats... ");
PixelFormatDescriptor pfd = new PixelFormatDescriptor();
pfd.Size = API.PixelFormatDescriptorSize;
pfd.Version = API.PixelFormatDescriptorVersion;
pfd.Flags =
PixelFormatDescriptorFlags.SUPPORT_OPENGL |
PixelFormatDescriptorFlags.DRAW_TO_WINDOW;
int pixel = 0;
while (DescribePixelFormat(deviceContext, ++pixel, API.PixelFormatDescriptorSize, ref pfd) != 0)
{
// Ignore non-accelerated formats.
if ((pfd.Flags & PixelFormatDescriptorFlags.GENERIC_FORMAT) != 0)
continue;
// Find out what we really got as a format:
PixelFormatDescriptor pfd = new PixelFormatDescriptor();
pixelFormat.Size = API.PixelFormatDescriptorSize;
pixelFormat.Version = API.PixelFormatDescriptorVersion;
Functions.DescribePixelFormat(deviceContext, pixel, API.PixelFormatDescriptorSize, ref pfd);
GraphicsMode fmt = new GraphicsMode((IntPtr)pixel,
new ColorDepth(pfd.RedBits, pfd.GreenBits, pfd.BlueBits, pfd.AlphaBits),
new ColorFormat(pfd.RedBits, pfd.GreenBits, pfd.BlueBits, pfd.AlphaBits),
pfd.DepthBits,
pfd.StencilBits,
0,
new ColorDepth(pfd.AccumBits),
new ColorFormat(pfd.AccumBits),
(pfd.Flags & PixelFormatDescriptorFlags.DOUBLEBUFFER) != 0 ? 2 : 1,
(pfd.Flags & PixelFormatDescriptorFlags.STEREO) != 0);
return fmt;
yield return fmt;
}
}
#endregion
#region SelectGraphicsModeARB
#region GetModesARB
GraphicsMode SelectGraphicsModeARB(ColorDepth color, int depth, int stencil, int samples, ColorDepth accum,
int buffers, bool stereo)
IEnumerable<GraphicsMode> GetModesARB(INativeWindow native)
{
using (INativeWindow native_window = new NativeWindow())
using (IGraphicsContext context = new WinGLContext(
using (IGraphicsContext context = new GraphicsContext(
new GraphicsMode(new IntPtr(2), new ColorFormat(), 0, 0, 0, new ColorFormat(), 2, false),
(WinWindowInfo)native_window.WindowInfo, null, 1, 0, GraphicsContextFlags.Default))
(WinWindowInfo)native.WindowInfo, 1, 0, GraphicsContextFlags.Default))
{
WinWindowInfo window = (WinWindowInfo)native_window.WindowInfo;
WinWindowInfo window = (WinWindowInfo)native.WindowInfo;
// See http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt
// See http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt
// for more details
Debug.Write("Selecting pixel format (ARB)... ");
Debug.Write("Retrieving ARB pixel formats.... ");
if (Wgl.Delegates.wglChoosePixelFormatARB == null || Wgl.Delegates.wglGetPixelFormatAttribivARB == null)
{
Debug.WriteLine("failed");
return null;
Debug.WriteLine("failed.");
yield break;
}
int[] attribs = new int[]
@ -206,73 +211,61 @@ namespace OpenTK.Platform.Windows
int[] attribs_values = new int[]
{
(int)WGL_ARB_pixel_format.AccelerationArb, (int)WGL_ARB_pixel_format.FullAccelerationArb,
(int)WGL_ARB_pixel_format.DrawToWindowArb, 1,
(int)WGL_ARB_pixel_format.RedBitsArb, color.Red,
(int)WGL_ARB_pixel_format.GreenBitsArb, color.Green,
(int)WGL_ARB_pixel_format.BlueBitsArb, color.Blue,
(int)WGL_ARB_pixel_format.AlphaBitsArb, color.Alpha,
(int)WGL_ARB_pixel_format.ColorBitsArb, color.BitsPerPixel - color.Alpha, // Should not contain alpha bpp (see spec)
(int)WGL_ARB_pixel_format.DepthBitsArb, depth,
(int)WGL_ARB_pixel_format.StencilBitsArb, stencil,
(int)WGL_ARB_multisample.SampleBuffersArb, samples > 0 ? 1 : 0,
(int)WGL_ARB_multisample.SamplesArb, samples,
(int)WGL_ARB_pixel_format.AccumRedBitsArb, accum.Red,
(int)WGL_ARB_pixel_format.AccumGreenBitsArb, accum.Green,
(int)WGL_ARB_pixel_format.AccumBlueBitsArb, accum.Blue,
(int)WGL_ARB_pixel_format.AccumAlphaBitsArb, accum.Alpha,
(int)WGL_ARB_pixel_format.AccumBitsArb, accum.BitsPerPixel, // Spec doesn't mention wether alpha bpp should be included...
(int)WGL_ARB_pixel_format.DoubleBufferArb, buffers > 1 ? 1 : 0,
(int)WGL_ARB_pixel_format.StereoArb, stereo ? 1 : 0,
(int)WGL_ARB_pixel_format.AccelerationArb,
(int)WGL_ARB_pixel_format.FullAccelerationArb,
0, 0
};
int[] pixel = new int[1], num_formats = new int[1];
bool success = Wgl.Arb.ChoosePixelFormat(window.DeviceContext, attribs_values, null, 1, pixel, num_formats);
if (!success || num_formats[0] == 0 || pixel[0] == 0)
{
// Try again without an accumulator. Many modern cards cannot accelerate multisampled formats with accumulator buffers.
int index_of_accum = Array.IndexOf(attribs_values, (int)WGL_ARB_pixel_format.AccumRedBitsArb);
attribs_values[index_of_accum + 1] = attribs_values[index_of_accum + 3] =
attribs_values[index_of_accum + 5] = attribs_values[index_of_accum + 7] =
attribs_values[index_of_accum + 9] = 0;
Wgl.Arb.ChoosePixelFormat(window.DeviceContext, attribs_values, null, 1, pixel, num_formats);
}
if (!success || num_formats[0] == 0 || pixel[0] == 0)
{
Debug.WriteLine("failed (no suitable pixel format).");
return null;
}
int[] num_formats = new int[1];
Wgl.Arb.ChoosePixelFormat(window.DeviceContext, attribs_values, null, 0, null, num_formats);
int[] pixel = new int[num_formats[0]];
// Find out what we really got as a format:
success = Wgl.Arb.GetPixelFormatAttrib(window.DeviceContext, pixel[0], 0, attribs.Length - 1, attribs, values);
if (!success)
if (Wgl.Arb.ChoosePixelFormat(window.DeviceContext, attribs_values, null, pixel.Length, pixel, num_formats))
{
Debug.WriteLine("failed (pixel format attributes could not be determined).");
return null;
foreach (int p in pixel)
{
// Find out what we really got as a format:
if (!Wgl.Arb.GetPixelFormatAttrib(window.DeviceContext, p, 0, attribs.Length - 1, attribs, values))
{
Debug.Print("[Warning] Failed to detect attributes for PixelFormat:{0}.", p);
continue;
}
GraphicsMode mode = new GraphicsMode(new IntPtr(p),
new ColorFormat(values[1], values[2], values[3], values[4]),
values[6],
values[7],
values[8] != 0 ? values[9] : 0,
new ColorFormat(values[10], values[11], values[12], values[13]),
values[15] == 1 ? 2 : 1,
values[16] == 1 ? true : false);
yield return mode;
}
}
GraphicsMode mode = new GraphicsMode(new IntPtr(pixel[0]),
new ColorDepth(values[1], values[2], values[3], values[4]),
values[6],
values[7],
values[8] != 0 ? values[9] : 0,
new ColorDepth(values[10], values[11], values[12], values[13]),
values[15] == 1 ? 2 : 1,
values[16] == 1 ? true : false);
Debug.WriteLine("success!");
return mode;
}
}
#endregion
#region ModeSelector
bool ModeSelector(GraphicsMode current, ColorFormat color, int depth, int stencil, int samples,
ColorFormat accum, int buffers, bool stereo)
{
bool result =
(color != ColorFormat.Empty ? current.ColorFormat >= color : true) &&
(depth != 0 ? current.Depth >= depth : true) &&
(stencil != 0 ? current.Stencil >= stencil : true) &&
(samples != 0 ? current.Samples >= samples : true) &&
(accum != ColorFormat.Empty ? current.AccumulatorFormat >= accum : true) &&
(buffers != 0 ? current.Buffers >= buffers : true) &&
current.Stereo == stereo;
return result;
}
#endregion
#endregion
}
}