Opentk/Source/OpenTK/Platform/Windows/WinGraphicsMode.cs
Stefanos A. 956bbe6491 Fixed DescribePixelFormatPFD
When using the PFD codepath, we now call DescribePixelFormat to retrieve
an exact interpretation of the pixel format selected by the driver.
2013-12-21 23:35:55 +01:00

370 lines
14 KiB
C#

#region License
//
// The Open Toolkit Library License
//
// 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
// 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.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
using OpenTK.Graphics;
namespace OpenTK.Platform.Windows
{
class WinGraphicsMode : IGraphicsMode
{
enum AccelerationType
{
// Software acceleration
None = 0,
// Partial acceleration (Direct3D emulation)
MCD,
// Full acceleration
ICD,
}
static readonly object SyncRoot = new object();
readonly IntPtr Device;
readonly List<GraphicsMode> modes = new List<GraphicsMode>();
#region Constructors
public WinGraphicsMode(IntPtr device)
{
if (device == IntPtr.Zero)
throw new ArgumentException();
Device = device;
}
#endregion
#region IGraphicsMode Members
public GraphicsMode SelectGraphicsMode(ColorFormat color, int depth, int stencil, int samples,
ColorFormat accum, int buffers, bool stereo)
{
GraphicsMode mode = new GraphicsMode(color, depth, stencil, samples,accum, buffers, stereo);
GraphicsMode created_mode = ChoosePixelFormatARB(Device, mode);
// If ChoosePixelFormatARB failed, iterate through all acceleration types in turn (ICD, MCD, None)
// This should fix issue #2224, which causes OpenTK to fail on VMs without hardware acceleration.
created_mode = created_mode ?? ChoosePixelFormatPFD(Device, mode, AccelerationType.ICD);
created_mode = created_mode ?? ChoosePixelFormatPFD(Device, mode, AccelerationType.MCD);
created_mode = created_mode ?? ChoosePixelFormatPFD(Device, mode, AccelerationType.None);
if (created_mode == null)
{
throw new GraphicsModeException("The requested GraphicsMode is not supported");
}
return created_mode;
}
#endregion
#region Private Methods
#region ChoosePixelFormatARB
// Queries pixel formats through the WGL_ARB_pixel_format extension
// This method only returns accelerated formats. If no format offers
// hardware acceleration (e.g. we are running in a VM or in a remote desktop
// connection), this method will return 0 formats and we will fall back to
// ChoosePixelFormatPFD.
GraphicsMode ChoosePixelFormatARB(IntPtr device, GraphicsMode mode)
{
GraphicsMode created_mode = null;
if (Wgl.Delegates.wglChoosePixelFormatARB != null)
{
List<int> attributes = new List<int>();
attributes.Add((int)WGL_ARB_pixel_format.AccelerationArb);
attributes.Add((int)WGL_ARB_pixel_format.FullAccelerationArb);
if (mode.ColorFormat.BitsPerPixel > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.RedBitsArb);
attributes.Add(mode.ColorFormat.Red);
attributes.Add((int)WGL_ARB_pixel_format.GreenBitsArb);
attributes.Add(mode.ColorFormat.Green);
attributes.Add((int)WGL_ARB_pixel_format.BlueBitsArb);
attributes.Add(mode.ColorFormat.Blue);
attributes.Add((int)WGL_ARB_pixel_format.AlphaBitsArb);
attributes.Add(mode.ColorFormat.Alpha);
attributes.Add((int)WGL_ARB_pixel_format.ColorBitsArb);
attributes.Add(mode.ColorFormat.BitsPerPixel);
}
if (mode.Depth > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.DepthBitsArb);
attributes.Add(mode.Depth);
}
if (mode.Stencil > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.StencilBitsArb);
attributes.Add(mode.Stencil);
}
if (mode.AccumulatorFormat.BitsPerPixel > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.AccumRedBitsArb);
attributes.Add(mode.AccumulatorFormat.Red);
attributes.Add((int)WGL_ARB_pixel_format.AccumGreenBitsArb);
attributes.Add(mode.AccumulatorFormat.Green);
attributes.Add((int)WGL_ARB_pixel_format.AccumBlueBitsArb);
attributes.Add(mode.AccumulatorFormat.Blue);
attributes.Add((int)WGL_ARB_pixel_format.AccumAlphaBitsArb);
attributes.Add(mode.AccumulatorFormat.Alpha);
attributes.Add((int)WGL_ARB_pixel_format.AccumBitsArb);
attributes.Add(mode.AccumulatorFormat.BitsPerPixel);
}
if (mode.Samples > 0)
{
attributes.Add((int)WGL_ARB_multisample.SampleBuffersArb);
attributes.Add(1);
attributes.Add((int)WGL_ARB_multisample.SamplesArb);
attributes.Add(mode.Samples);
}
if (mode.Buffers > 0)
{
attributes.Add((int)WGL_ARB_pixel_format.DoubleBufferArb);
attributes.Add(mode.Buffers);
}
if (mode.Stereo)
{
attributes.Add((int)WGL_ARB_pixel_format.StereoArb);
attributes.Add(1);
}
attributes.Add(0);
attributes.Add(0);
int[] format = new int[1];
int count;
if (Wgl.Arb.ChoosePixelFormat(device, attributes.ToArray(), null, format.Length, format, out count))
{
created_mode = DescribePixelFormatARB(device, format[0]);
}
else
{
Debug.Print("[WGL] ChoosePixelFormatARB failed with {0}", Marshal.GetLastWin32Error());
}
}
else
{
Debug.Print("[WGL] ChoosePixelFormatARB not supported");
}
return created_mode;
}
#endregion
#region ChoosePixelFormatPFD
GraphicsMode ChoosePixelFormatPFD(IntPtr device, GraphicsMode mode, AccelerationType requested_acceleration_type)
{
PixelFormatDescriptor pfd = new PixelFormatDescriptor();
pfd.Size = (short)BlittableValueType<PixelFormatDescriptor>.Stride;
if (mode.ColorFormat.BitsPerPixel > 0)
{
pfd.RedBits = (byte)mode.ColorFormat.Red;
pfd.GreenBits = (byte)mode.ColorFormat.Green;
pfd.BlueBits = (byte)mode.ColorFormat.Blue;
pfd.AlphaBits = (byte)mode.ColorFormat.Alpha;
pfd.ColorBits = (byte)mode.ColorFormat.BitsPerPixel;
}
if (mode.Depth > 0)
{
pfd.DepthBits = (byte)mode.Depth;
}
else
{
pfd.Flags |= PixelFormatDescriptorFlags.DEPTH_DONTCARE;
}
if (mode.Stencil > 0)
{
pfd.StencilBits = (byte)mode.Stencil;
}
if (mode.AccumulatorFormat.BitsPerPixel > 0)
{
pfd.AccumRedBits = (byte)mode.AccumulatorFormat.Red;
pfd.AccumGreenBits = (byte)mode.AccumulatorFormat.Green;
pfd.AccumBlueBits = (byte)mode.AccumulatorFormat.Blue;
pfd.AccumAlphaBits = (byte)mode.AccumulatorFormat.Alpha;
pfd.AccumBits = (byte)mode.AccumulatorFormat.BitsPerPixel;
}
if (mode.Buffers > 1)
{
pfd.Flags |= PixelFormatDescriptorFlags.DOUBLEBUFFER;
}
else if (mode.Buffers == 0)
{
pfd.Flags |= PixelFormatDescriptorFlags.DOUBLEBUFFER_DONTCARE;
}
if (mode.Stereo)
{
pfd.Flags |= PixelFormatDescriptorFlags.DRAW_TO_WINDOW;
pfd.Flags |= PixelFormatDescriptorFlags.SUPPORT_OPENGL;
}
// Make sure we don't turn off Aero on Vista and newer.
if (Environment.OSVersion.Version.Major >= 6)
{
pfd.Flags |= PixelFormatDescriptorFlags.SUPPORT_COMPOSITION;
}
GraphicsMode created_mode = null;
int pixelformat = Functions.ChoosePixelFormat(device, ref pfd);
if (pixelformat > 0)
{
AccelerationType acceleration_type = AccelerationType.ICD;
if ((pfd.Flags & PixelFormatDescriptorFlags.GENERIC_FORMAT) != 0)
{
if ((pfd.Flags & PixelFormatDescriptorFlags.GENERIC_ACCELERATED) != 0)
{
acceleration_type = AccelerationType.MCD;
}
else
{
acceleration_type = AccelerationType.None;
}
}
if (acceleration_type == requested_acceleration_type)
{
created_mode = DescribePixelFormatPFD(device, ref pfd, pixelformat);
}
}
return created_mode;
}
#endregion
#region DescribePixelFormatPFD
static GraphicsMode DescribePixelFormatPFD(IntPtr device, ref PixelFormatDescriptor pfd, int pixelformat)
{
GraphicsMode created_mode = null;
if (Functions.DescribePixelFormat(device, pixelformat, pfd.Size, ref pfd) > 0)
{
created_mode = new GraphicsMode(
new IntPtr(pixelformat),
new ColorFormat(pfd.RedBits, pfd.GreenBits, pfd.BlueBits, pfd.AlphaBits),
pfd.DepthBits,
pfd.StencilBits,
0, // MSAA not supported when using PixelFormatDescriptor
new ColorFormat(pfd.AccumRedBits, pfd.AccumGreenBits, pfd.AccumBlueBits, pfd.AccumAlphaBits),
(pfd.Flags & PixelFormatDescriptorFlags.DOUBLEBUFFER) != 0 ? 2 : 1,
(pfd.Flags & PixelFormatDescriptorFlags.STEREO) != 0);
}
return created_mode;
}
#endregion
#region DescribePixelFormatARB
GraphicsMode DescribePixelFormatARB(IntPtr device, int pixelformat)
{
GraphicsMode created_mode = null;
// See http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt for more details
if (Wgl.Delegates.wglGetPixelFormatAttribivARB != null)
{
// Define the list of attributes we are interested in.
// The results will be stored in the 'values' array below.
int[] attribs = new int[]
{
(int)WGL_ARB_pixel_format.AccelerationArb,
(int)WGL_ARB_pixel_format.RedBitsArb,
(int)WGL_ARB_pixel_format.GreenBitsArb,
(int)WGL_ARB_pixel_format.BlueBitsArb,
(int)WGL_ARB_pixel_format.AlphaBitsArb,
(int)WGL_ARB_pixel_format.ColorBitsArb,
(int)WGL_ARB_pixel_format.DepthBitsArb,
(int)WGL_ARB_pixel_format.StencilBitsArb,
(int)WGL_ARB_multisample.SampleBuffersArb,
(int)WGL_ARB_multisample.SamplesArb,
(int)WGL_ARB_pixel_format.AccumRedBitsArb,
(int)WGL_ARB_pixel_format.AccumGreenBitsArb,
(int)WGL_ARB_pixel_format.AccumBlueBitsArb,
(int)WGL_ARB_pixel_format.AccumAlphaBitsArb,
(int)WGL_ARB_pixel_format.AccumBitsArb,
(int)WGL_ARB_pixel_format.DoubleBufferArb,
(int)WGL_ARB_pixel_format.StereoArb,
0
};
// Allocate storage for the results of GetPixelFormatAttrib queries
int[] values = new int[attribs.Length];
// Get the format attributes for this pixel format
if (!Wgl.Arb.GetPixelFormatAttrib(device, pixelformat, 0, attribs.Length - 1, attribs, values))
{
Debug.Print("[Warning] Failed to detect attributes for PixelFormat: {0}.", pixelformat);
}
// Skip formats that don't offer full hardware acceleration
WGL_ARB_pixel_format acceleration = (WGL_ARB_pixel_format)attribs[0];
if (acceleration == WGL_ARB_pixel_format.FullAccelerationArb)
{
// Construct a new GraphicsMode to describe this format
created_mode = new GraphicsMode(new IntPtr(pixelformat),
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);
}
}
return created_mode;
}
#endregion
#endregion
}
}