Show More
Commit Description:
Various UI improvements.
Commit Description:
Various UI improvements.
File last commit:
Show/Diff file:
Action:
FNA/src/Graphics/GraphicsDevice.cs
1559 lines | 35.7 KiB | text/x-csharp | CSharpLexer
#region License
/* FNA - XNA4 Reimplementation for Desktop Platforms
* Copyright 2009-2020 Ethan Lee and the MonoGame Team
*
* Released under the Microsoft Public License.
* See LICENSE for details.
*/
#endregion
#region WIIU_GAMEPAD Option
// #define WIIU_GAMEPAD
/* This is something I added for myself, because I am a complete goof.
* You should NEVER enable this in your shipping build.
* Let your hacker customers self-build FNA, they'll know what to do.
* -flibit
*/
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
#endregion
namespace Microsoft.Xna.Framework.Graphics
{
public class GraphicsDevice : IDisposable
{
#region Internal Constants
// Per XNA4 General Spec
internal const int MAX_TEXTURE_SAMPLERS = 16;
// Per XNA4 HiDef Spec
internal const int MAX_VERTEX_ATTRIBUTES = 16;
internal const int MAX_RENDERTARGET_BINDINGS = 4;
internal const int MAX_VERTEXTEXTURE_SAMPLERS = 4;
#endregion
#region Public GraphicsDevice State Properties
public bool IsDisposed
{
get;
private set;
}
public GraphicsDeviceStatus GraphicsDeviceStatus
{
get
{
return GraphicsDeviceStatus.Normal;
}
}
public GraphicsAdapter Adapter
{
get;
private set;
}
public GraphicsProfile GraphicsProfile
{
get;
private set;
}
public PresentationParameters PresentationParameters
{
get;
private set;
}
#endregion
#region Public Graphics Display Properties
public DisplayMode DisplayMode
{
get
{
if (PresentationParameters.IsFullScreen)
{
return new DisplayMode(
GLDevice.Backbuffer.Width,
GLDevice.Backbuffer.Height,
SurfaceFormat.Color
);
}
return Adapter.CurrentDisplayMode;
}
}
#endregion
#region Public GL State Properties
public TextureCollection Textures
{
get;
private set;
}
public SamplerStateCollection SamplerStates
{
get;
private set;
}
public TextureCollection VertexTextures
{
get;
private set;
}
public SamplerStateCollection VertexSamplerStates
{
get;
private set;
}
public BlendState BlendState
{
get
{
return nextBlend;
}
set
{
nextBlend = value;
}
}
public DepthStencilState DepthStencilState
{
get
{
return nextDepthStencil;
}
set
{
nextDepthStencil = value;
}
}
public RasterizerState RasterizerState
{
get;
set;
}
/* We have to store this internally because we flip the Rectangle for
* when we aren't rendering to a target. I'd love to remove this.
* -flibit
*/
private Rectangle INTERNAL_scissorRectangle;
public Rectangle ScissorRectangle
{
get
{
return INTERNAL_scissorRectangle;
}
set
{
INTERNAL_scissorRectangle = value;
GLDevice.SetScissorRect(value);
}
}
/* We have to store this internally because we flip the Viewport for
* when we aren't rendering to a target. I'd love to remove this.
* -flibit
*/
private Viewport INTERNAL_viewport;
public Viewport Viewport
{
get
{
return INTERNAL_viewport;
}
set
{
INTERNAL_viewport = value;
GLDevice.SetViewport(value);
}
}
public Color BlendFactor
{
get
{
return GLDevice.BlendFactor;
}
set
{
/* FIXME: Does this affect the value found in
* BlendState?
* -flibit
*/
GLDevice.BlendFactor = value;
}
}
public int MultiSampleMask
{
get
{
return GLDevice.MultiSampleMask;
}
set
{
/* FIXME: Does this affect the value found in
* BlendState?
* -flibit
*/
GLDevice.MultiSampleMask = value;
}
}
public int ReferenceStencil
{
get
{
return GLDevice.ReferenceStencil;
}
set
{
/* FIXME: Does this affect the value found in
* DepthStencilState?
* -flibit
*/
GLDevice.ReferenceStencil = value;
}
}
#endregion
#region Public Buffer Object Properties
public IndexBuffer Indices
{
get;
set;
}
#endregion
#region Internal GL Device
internal readonly IGLDevice GLDevice;
#endregion
#region Internal Pipeline Cache
internal readonly PipelineCache PipelineCache;
#endregion
#region Private State Shadowing Variables
private BlendState currentBlend;
private BlendState nextBlend;
private DepthStencilState currentDepthStencil;
private DepthStencilState nextDepthStencil;
#endregion
#region Private Vertex Sampler Offset Variable
private int vertexSamplerStart;
#endregion
#region Internal Sampler Change Queue
private readonly bool[] modifiedSamplers = new bool[MAX_TEXTURE_SAMPLERS];
private readonly bool[] modifiedVertexSamplers = new bool[MAX_VERTEXTEXTURE_SAMPLERS];
#endregion
#region Private Disposal Variables
/* Use WeakReference for the global resources list as we do not
* know when a resource may be disposed and collected. We do not
* want to prevent a resource from being collected by holding a
* strong reference to it in this list.
*/
private readonly List<WeakReference> resources = new List<WeakReference>();
private readonly object resourcesLock = new object();
#endregion
#region Private Clear Variables
/* On Intel Integrated graphics, there is a fast hw unit for doing
* clears to colors where all components are either 0 or 255.
* Despite XNA4 using Purple here, we use black (in Release) to avoid
* performance warnings on Intel/Mesa.
* -sulix
*/
#if DEBUG
private static readonly Vector4 DiscardColor = new Color(68, 34, 136, 255).ToVector4();
#else
private static readonly Vector4 DiscardColor = new Vector4(0.0f, 0.0f, 0.0f, 1.0f);
#endif
#endregion
#region Private RenderTarget Variables
private readonly RenderTargetBinding[] renderTargetBindings = new RenderTargetBinding[MAX_RENDERTARGET_BINDINGS];
private int renderTargetCount = 0;
// Used to prevent allocs on SetRenderTarget()
private readonly RenderTargetBinding[] singleTargetCache = new RenderTargetBinding[1];
#endregion
#region Private Buffer Object Variables
private readonly VertexBufferBinding[] vertexBufferBindings = new VertexBufferBinding[MAX_VERTEX_ATTRIBUTES];
private int vertexBufferCount = 0;
private bool vertexBuffersUpdated = false;
#endregion
#region GraphicsDevice Events
#pragma warning disable 0067
// We never lose devices, but lol XNA4 compliance -flibit
public event EventHandler<EventArgs> DeviceLost;
#pragma warning restore 0067
public event EventHandler<EventArgs> DeviceReset;
public event EventHandler<EventArgs> DeviceResetting;
public event EventHandler<ResourceCreatedEventArgs> ResourceCreated;
public event EventHandler<ResourceDestroyedEventArgs> ResourceDestroyed;
public event EventHandler<EventArgs> Disposing;
// TODO: Hook this up to GraphicsResource
internal void OnResourceCreated(object resource)
{
if (ResourceCreated != null)
{
ResourceCreated(this, new ResourceCreatedEventArgs(resource));
}
}
// TODO: Hook this up to GraphicsResource
internal void OnResourceDestroyed(string name, object tag)
{
if (ResourceDestroyed != null)
{
ResourceDestroyed(this, new ResourceDestroyedEventArgs(name, tag));
}
}
#endregion
#region Constructor, Deconstructor, Dispose Methods
/// <summary>
/// Initializes a new instance of the <see cref="GraphicsDevice" /> class.
/// </summary>
/// <param name="adapter">The graphics adapter.</param>
/// <param name="graphicsProfile">The graphics profile.</param>
/// <param name="presentationParameters">The presentation options.</param>
/// <exception cref="ArgumentNullException">
/// <paramref name="presentationParameters"/> is <see langword="null"/>.
/// </exception>
public GraphicsDevice(
GraphicsAdapter adapter,
GraphicsProfile graphicsProfile,
PresentationParameters presentationParameters
) {
if (presentationParameters == null)
{
throw new ArgumentNullException("presentationParameters");
}
// Set the properties from the constructor parameters.
Adapter = adapter;
PresentationParameters = presentationParameters;
GraphicsProfile = graphicsProfile;
PresentationParameters.MultiSampleCount = MathHelper.ClosestMSAAPower(
PresentationParameters.MultiSampleCount
);
// Set up the IGLDevice
GLDevice = FNAPlatform.CreateGLDevice(PresentationParameters, adapter);
// The mouse needs to know this for faux-backbuffer mouse scaling.
Input.Mouse.INTERNAL_BackBufferWidth = PresentationParameters.BackBufferWidth;
Input.Mouse.INTERNAL_BackBufferHeight = PresentationParameters.BackBufferHeight;
// The Touch Panel needs this too, for the same reason.
Input.Touch.TouchPanel.DisplayWidth = PresentationParameters.BackBufferWidth;
Input.Touch.TouchPanel.DisplayHeight = PresentationParameters.BackBufferHeight;
// Force set the default render states.
BlendState = BlendState.Opaque;
DepthStencilState = DepthStencilState.Default;
RasterizerState = RasterizerState.CullCounterClockwise;
// Initialize the Texture/Sampler state containers
int maxTextures = Math.Min(GLDevice.MaxTextureSlots, MAX_TEXTURE_SAMPLERS);
int maxVertexTextures = MathHelper.Clamp(
GLDevice.MaxTextureSlots - MAX_TEXTURE_SAMPLERS,
0,
MAX_VERTEXTEXTURE_SAMPLERS
);
vertexSamplerStart = GLDevice.MaxTextureSlots - maxVertexTextures;
Textures = new TextureCollection(
maxTextures,
modifiedSamplers
);
SamplerStates = new SamplerStateCollection(
maxTextures,
modifiedSamplers
);
VertexTextures = new TextureCollection(
maxVertexTextures,
modifiedVertexSamplers
);
VertexSamplerStates = new SamplerStateCollection(
maxVertexTextures,
modifiedVertexSamplers
);
// Set the default viewport and scissor rect.
Viewport = new Viewport(PresentationParameters.Bounds);
ScissorRectangle = Viewport.Bounds;
// Set the initial swap interval
GLDevice.SetPresentationInterval(
PresentationParameters.PresentationInterval
);
// Allocate the pipeline cache to be used by Effects
PipelineCache = new PipelineCache(this);
#if WIIU_GAMEPAD
wiiuStream = DRC.drc_new_streamer();
if (wiiuStream == IntPtr.Zero)
{
FNALoggerEXT.LogError("Failed to alloc GamePad stream!");
return;
}
if (DRC.drc_start_streamer(wiiuStream) < 1) // ???
{
FNALoggerEXT.LogError("Failed to start GamePad stream!");
DRC.drc_delete_streamer(wiiuStream);
wiiuStream = IntPtr.Zero;
return;
}
DRC.drc_enable_system_input_feeder(wiiuStream);
wiiuPixelData = new byte[
PresentationParameters.BackBufferWidth *
PresentationParameters.BackBufferHeight *
4
];
#endif
}
~GraphicsDevice()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!IsDisposed)
{
if (disposing)
{
// We're about to dispose, notify the application.
if (Disposing != null)
{
Disposing(this, EventArgs.Empty);
}
/* Dispose of all remaining graphics resources before
* disposing of the GraphicsDevice.
*/
lock (resourcesLock)
{
foreach (WeakReference resource in resources.ToArray())
{
object target = resource.Target;
if (target != null)
{
(target as IDisposable).Dispose();
}
}
resources.Clear();
}
// Dispose of the GL Device/Context
GLDevice.Dispose();
#if WIIU_GAMEPAD
if (wiiuStream != IntPtr.Zero)
{
DRC.drc_stop_streamer(wiiuStream);
DRC.drc_delete_streamer(wiiuStream);
wiiuStream = IntPtr.Zero;
}
#endif
}
IsDisposed = true;
}
}
#endregion
#region Internal Resource Management Methods
internal void AddResourceReference(WeakReference resourceReference)
{
lock (resourcesLock)
{
resources.Add(resourceReference);
}
}
internal void RemoveResourceReference(WeakReference resourceReference)
{
lock (resourcesLock)
{
resources.Remove(resourceReference);
}
}
#endregion
#region Public Present Method
public void Present()
{
GLDevice.SwapBuffers(
null,
null,
PresentationParameters.DeviceWindowHandle
);
#if WIIU_GAMEPAD
if (wiiuStream != IntPtr.Zero)
{
GetBackBufferData(wiiuPixelData);
DRC.drc_push_vid_frame(
wiiuStream,
wiiuPixelData,
(uint) wiiuPixelData.Length,
(ushort) GLDevice.Backbuffer.Width,
(ushort) GLDevice.Backbuffer.Height,
DRC.drc_pixel_format.DRC_RGBA,
DRC.drc_flipping_mode.DRC_NO_FLIP
);
}
#endif
}
public void Present(
Rectangle? sourceRectangle,
Rectangle? destinationRectangle,
IntPtr overrideWindowHandle
) {
if (overrideWindowHandle == IntPtr.Zero)
{
overrideWindowHandle = PresentationParameters.DeviceWindowHandle;
}
GLDevice.SwapBuffers(
sourceRectangle,
destinationRectangle,
overrideWindowHandle
);
#if WIIU_GAMEPAD
if (wiiuStream != IntPtr.Zero)
{
GetBackBufferData(wiiuPixelData);
DRC.drc_push_vid_frame(
wiiuStream,
wiiuPixelData,
(uint) wiiuPixelData.Length,
(ushort) GLDevice.Backbuffer.Width,
(ushort) GLDevice.Backbuffer.Height,
DRC.drc_pixel_format.DRC_RGBA,
DRC.drc_flipping_mode.DRC_NO_FLIP
);
}
#endif
}
#endregion
#region Public Reset Methods
public void Reset()
{
Reset(PresentationParameters, Adapter);
}
public void Reset(PresentationParameters presentationParameters)
{
Reset(presentationParameters, Adapter);
}
public void Reset(
PresentationParameters presentationParameters,
GraphicsAdapter graphicsAdapter
) {
if (presentationParameters == null)
{
throw new ArgumentNullException("presentationParameters");
}
PresentationParameters = presentationParameters;
Adapter = graphicsAdapter;
// Verify MSAA before we really start...
PresentationParameters.MultiSampleCount = Math.Min(
MathHelper.ClosestMSAAPower(
PresentationParameters.MultiSampleCount
),
GLDevice.MaxMultiSampleCount
);
// We're about to reset, let the application know.
if (DeviceResetting != null)
{
DeviceResetting(this, EventArgs.Empty);
}
/* FIXME: Why are we not doing this...? -flibit
lock (resourcesLock)
{
foreach (WeakReference resource in resources)
{
object target = resource.Target;
if (target != null)
{
(target as GraphicsResource).GraphicsDeviceResetting();
}
}
// Remove references to resources that have been garbage collected.
resources.RemoveAll(wr => !wr.IsAlive);
}
*/
/* Reset the backbuffer first, before doing anything else.
* The GLDevice needs to know what we're up to right away.
* -flibit
*/
GLDevice.ResetBackbuffer(PresentationParameters);
// The mouse needs to know this for faux-backbuffer mouse scaling.
Input.Mouse.INTERNAL_BackBufferWidth = PresentationParameters.BackBufferWidth;
Input.Mouse.INTERNAL_BackBufferHeight = PresentationParameters.BackBufferHeight;
// The Touch Panel needs this too, for the same reason.
Input.Touch.TouchPanel.DisplayWidth = PresentationParameters.BackBufferWidth;
Input.Touch.TouchPanel.DisplayHeight = PresentationParameters.BackBufferHeight;
#if WIIU_GAMEPAD
wiiuPixelData = new byte[
PresentationParameters.BackBufferWidth *
PresentationParameters.BackBufferHeight *
4
];
#endif
// Now, update the viewport
Viewport = new Viewport(
0,
0,
PresentationParameters.BackBufferWidth,
PresentationParameters.BackBufferHeight
);
// Update the scissor rectangle to our new default target size
ScissorRectangle = new Rectangle(
0,
0,
PresentationParameters.BackBufferWidth,
PresentationParameters.BackBufferHeight
);
// Finally, update the swap interval
GLDevice.SetPresentationInterval(
PresentationParameters.PresentationInterval
);
// We just reset, let the application know.
if (DeviceReset != null)
{
DeviceReset(this, EventArgs.Empty);
}
}
#endregion
#region Public Clear Methods
public void Clear(Color color)
{
Clear(
ClearOptions.Target | ClearOptions.DepthBuffer | ClearOptions.Stencil,
color.ToVector4(),
Viewport.MaxDepth,
0
);
}
public void Clear(ClearOptions options, Color color, float depth, int stencil)
{
Clear(
options,
color.ToVector4(),
depth,
stencil
);
}
public void Clear(ClearOptions options, Vector4 color, float depth, int stencil)
{
DepthFormat dsFormat;
if (renderTargetCount == 0)
{
/* FIXME: PresentationParameters.DepthStencilFormat is probably
* a more accurate value here, but the Backbuffer may disagree.
* -flibit
*/
dsFormat = GLDevice.Backbuffer.DepthFormat;
}
else
{
dsFormat = (renderTargetBindings[0].RenderTarget as IRenderTarget).DepthStencilFormat;
}
if (dsFormat == DepthFormat.None)
{
options &= ClearOptions.Target;
}
else if (dsFormat != DepthFormat.Depth24Stencil8)
{
options &= ~ClearOptions.Stencil;
}
GLDevice.Clear(
options,
color,
depth,
stencil
);
}
#endregion
#region Public Backbuffer Methods
public void GetBackBufferData<T>(T[] data) where T : struct
{
GetBackBufferData(null, data, 0, data.Length);
}
public void GetBackBufferData<T>(
T[] data,
int startIndex,
int elementCount
) where T : struct {
GetBackBufferData(null, data, startIndex, elementCount);
}
public void GetBackBufferData<T>(
Rectangle? rect,
T[] data,
int startIndex,
int elementCount
) where T : struct {
int x, y, w, h;
if (rect == null)
{
x = 0;
y = 0;
w = GLDevice.Backbuffer.Width;
h = GLDevice.Backbuffer.Height;
}
else
{
x = rect.Value.X;
y = rect.Value.Y;
w = rect.Value.Width;
h = rect.Value.Height;
}
int elementSizeInBytes = Marshal.SizeOf(typeof(T));
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
GLDevice.ReadBackbuffer(
handle.AddrOfPinnedObject(),
data.Length * elementSizeInBytes,
startIndex,
elementCount,
elementSizeInBytes,
x,
y,
w,
h
);
handle.Free();
}
#endregion
#region Public RenderTarget Methods
public void SetRenderTarget(RenderTarget2D renderTarget)
{
if (renderTarget == null)
{
SetRenderTargets(null);
}
else
{
singleTargetCache[0] = new RenderTargetBinding(renderTarget);
SetRenderTargets(singleTargetCache);
}
}
public void SetRenderTarget(RenderTargetCube renderTarget, CubeMapFace cubeMapFace)
{
if (renderTarget == null)
{
SetRenderTargets(null);
}
else
{
singleTargetCache[0] = new RenderTargetBinding(renderTarget, cubeMapFace);
SetRenderTargets(singleTargetCache);
}
}
public void SetRenderTargets(params RenderTargetBinding[] renderTargets)
{
// Checking for redundant SetRenderTargets...
if (renderTargets == null && renderTargetCount == 0)
{
return;
}
else if (renderTargets != null && renderTargets.Length == renderTargetCount)
{
bool isRedundant = true;
for (int i = 0; i < renderTargets.Length; i += 1)
{
if ( renderTargets[i].RenderTarget != renderTargetBindings[i].RenderTarget ||
renderTargets[i].CubeMapFace != renderTargetBindings[i].CubeMapFace )
{
isRedundant = false;
break;
}
}
if (isRedundant)
{
return;
}
}
int newWidth;
int newHeight;
RenderTargetUsage clearTarget;
if (renderTargets == null || renderTargets.Length == 0)
{
GLDevice.SetRenderTargets(null, null, DepthFormat.None);
// Set the viewport/scissor to the size of the backbuffer.
newWidth = PresentationParameters.BackBufferWidth;
newHeight = PresentationParameters.BackBufferHeight;
clearTarget = PresentationParameters.RenderTargetUsage;
// Resolve previous targets, if needed
for (int i = 0; i < renderTargetCount; i += 1)
{
GLDevice.ResolveTarget(renderTargetBindings[i]);
}
Array.Clear(renderTargetBindings, 0, renderTargetBindings.Length);
renderTargetCount = 0;
}
else
{
IRenderTarget target = renderTargets[0].RenderTarget as IRenderTarget;
GLDevice.SetRenderTargets(
renderTargets,
target.DepthStencilBuffer,
target.DepthStencilFormat
);
// Set the viewport/scissor to the size of the first render target.
newWidth = target.Width;
newHeight = target.Height;
clearTarget = target.RenderTargetUsage;
// Resolve previous targets, if needed
for (int i = 0; i < renderTargetCount; i += 1)
{
// We only need to resolve if the target is no longer bound.
bool stillBound = false;
for (int j = 0; j < renderTargets.Length; j += 1)
{
if (renderTargetBindings[i].RenderTarget == renderTargets[j].RenderTarget)
{
stillBound = true;
break;
}
}
if (stillBound)
{
continue;
}
GLDevice.ResolveTarget(renderTargetBindings[i]);
}
Array.Clear(renderTargetBindings, 0, renderTargetBindings.Length);
Array.Copy(renderTargets, renderTargetBindings, renderTargets.Length);
renderTargetCount = renderTargets.Length;
}
// Apply new GL state, clear target if requested
Viewport = new Viewport(0, 0, newWidth, newHeight);
ScissorRectangle = new Rectangle(0, 0, newWidth, newHeight);
if (clearTarget == RenderTargetUsage.DiscardContents)
{
Clear(
ClearOptions.Target | ClearOptions.DepthBuffer | ClearOptions.Stencil,
DiscardColor,
Viewport.MaxDepth,
0
);
}
}
public RenderTargetBinding[] GetRenderTargets()
{
// Return a correctly sized copy our internal array.
RenderTargetBinding[] bindings = new RenderTargetBinding[renderTargetCount];
Array.Copy(renderTargetBindings, bindings, renderTargetCount);
return bindings;
}
#endregion
#region Public Buffer Object Methods
public void SetVertexBuffer(VertexBuffer vertexBuffer)
{
SetVertexBuffer(vertexBuffer, 0);
}
public void SetVertexBuffer(VertexBuffer vertexBuffer, int vertexOffset)
{
if (vertexBuffer == null)
{
if (vertexBufferCount == 0)
{
return;
}
for (int i = 0; i < vertexBufferCount; i += 1)
{
vertexBufferBindings[i] = VertexBufferBinding.None;
}
vertexBufferCount = 0;
vertexBuffersUpdated = true;
return;
}
if ( !ReferenceEquals(vertexBufferBindings[0].VertexBuffer, vertexBuffer) ||
vertexBufferBindings[0].VertexOffset != vertexOffset )
{
vertexBufferBindings[0] = new VertexBufferBinding(
vertexBuffer,
vertexOffset
);
vertexBuffersUpdated = true;
}
if (vertexBufferCount > 1)
{
for (int i = 1; i < vertexBufferCount; i += 1)
{
vertexBufferBindings[i] = VertexBufferBinding.None;
}
vertexBuffersUpdated = true;
}
vertexBufferCount = 1;
}
public void SetVertexBuffers(params VertexBufferBinding[] vertexBuffers)
{
if (vertexBuffers == null)
{
if (vertexBufferCount == 0)
{
return;
}
for (int j = 0; j < vertexBufferCount; j += 1)
{
vertexBufferBindings[j] = VertexBufferBinding.None;
}
vertexBufferCount = 0;
vertexBuffersUpdated = true;
return;
}
if (vertexBuffers.Length > vertexBufferBindings.Length)
{
throw new ArgumentOutOfRangeException(
"vertexBuffers",
String.Format(
"Max Vertex Buffers supported is {0}",
vertexBufferBindings.Length
)
);
}
int i = 0;
while (i < vertexBuffers.Length)
{
if ( !ReferenceEquals(vertexBufferBindings[i].VertexBuffer, vertexBuffers[i].VertexBuffer) ||
vertexBufferBindings[i].VertexOffset != vertexBuffers[i].VertexOffset ||
vertexBufferBindings[i].InstanceFrequency != vertexBuffers[i].InstanceFrequency )
{
vertexBufferBindings[i] = vertexBuffers[i];
vertexBuffersUpdated = true;
}
i += 1;
}
if (vertexBuffers.Length < vertexBufferCount)
{
while (i < vertexBufferCount)
{
vertexBufferBindings[i] = VertexBufferBinding.None;
i += 1;
}
vertexBuffersUpdated = true;
}
vertexBufferCount = vertexBuffers.Length;
}
public VertexBufferBinding[] GetVertexBuffers()
{
VertexBufferBinding[] result = new VertexBufferBinding[vertexBufferCount];
Array.Copy(
vertexBufferBindings,
result,
vertexBufferCount
);
return result;
}
#endregion
#region DrawPrimitives: VertexBuffer, IndexBuffer
/// <summary>
/// Draw geometry by indexing into the vertex buffer.
/// </summary>
/// <param name="primitiveType">
/// The type of primitives in the index buffer.
/// </param>
/// <param name="baseVertex">
/// Used to offset the vertex range indexed from the vertex buffer.
/// </param>
/// <param name="minVertexIndex">
/// A hint of the lowest vertex indexed relative to baseVertex.
/// </param>
/// <param name="numVertices">
/// A hint of the maximum vertex indexed.
/// </param>
/// <param name="startIndex">
/// The index within the index buffer to start drawing from.
/// </param>
/// <param name="primitiveCount">
/// The number of primitives to render from the index buffer.
/// </param>
public void DrawIndexedPrimitives(
PrimitiveType primitiveType,
int baseVertex,
int minVertexIndex,
int numVertices,
int startIndex,
int primitiveCount
) {
ApplyState();
// Set up the vertex buffers
GLDevice.ApplyVertexAttributes(
vertexBufferBindings,
vertexBufferCount,
vertexBuffersUpdated,
baseVertex
);
vertexBuffersUpdated = false;
GLDevice.DrawIndexedPrimitives(
primitiveType,
baseVertex,
minVertexIndex,
numVertices,
startIndex,
primitiveCount,
Indices.buffer,
Indices.IndexElementSize
);
}
public void DrawInstancedPrimitives(
PrimitiveType primitiveType,
int baseVertex,
int minVertexIndex,
int numVertices,
int startIndex,
int primitiveCount,
int instanceCount
) {
// If this device doesn't have the support, just explode now before it's too late.
if (!GLDevice.SupportsHardwareInstancing)
{
throw new NoSuitableGraphicsDeviceException("Your hardware does not support hardware instancing!");
}
ApplyState();
// Set up the vertex buffers
GLDevice.ApplyVertexAttributes(
vertexBufferBindings,
vertexBufferCount,
vertexBuffersUpdated,
baseVertex
);
vertexBuffersUpdated = false;
GLDevice.DrawInstancedPrimitives(
primitiveType,
baseVertex,
minVertexIndex,
numVertices,
startIndex,
primitiveCount,
instanceCount,
Indices.buffer,
Indices.IndexElementSize
);
}
#endregion
#region DrawPrimitives: VertexBuffer, No Indices
public void DrawPrimitives(
PrimitiveType primitiveType,
int vertexStart,
int primitiveCount
) {
ApplyState();
// Set up the vertex buffers
GLDevice.ApplyVertexAttributes(
vertexBufferBindings,
vertexBufferCount,
vertexBuffersUpdated,
0
);
vertexBuffersUpdated = false;
GLDevice.DrawPrimitives(
primitiveType,
vertexStart,
primitiveCount
);
}
#endregion
#region DrawPrimitives: Vertex Arrays, Index Arrays
public void DrawUserIndexedPrimitives<T>(
PrimitiveType primitiveType,
T[] vertexData,
int vertexOffset,
int numVertices,
short[] indexData,
int indexOffset,
int primitiveCount
) where T : struct, IVertexType {
ApplyState();
// Pin the buffers.
GCHandle vbHandle = GCHandle.Alloc(vertexData, GCHandleType.Pinned);
GCHandle ibHandle = GCHandle.Alloc(indexData, GCHandleType.Pinned);
IntPtr vbPtr = vbHandle.AddrOfPinnedObject();
IntPtr ibPtr = ibHandle.AddrOfPinnedObject();
// Setup the vertex declaration to point at the vertex data.
VertexDeclaration vertexDeclaration = VertexDeclarationCache<T>.VertexDeclaration;
vertexDeclaration.GraphicsDevice = this;
GLDevice.ApplyVertexAttributes(
vertexDeclaration,
vbPtr,
vertexOffset
);
GLDevice.DrawUserIndexedPrimitives(
primitiveType,
vbPtr,
vertexOffset,
numVertices,
ibPtr,
indexOffset,
IndexElementSize.SixteenBits,
primitiveCount
);
// Release the handles.
ibHandle.Free();
vbHandle.Free();
}
public void DrawUserIndexedPrimitives<T>(
PrimitiveType primitiveType,
T[] vertexData,
int vertexOffset,
int numVertices,
short[] indexData,
int indexOffset,
int primitiveCount,
VertexDeclaration vertexDeclaration
) where T : struct {
ApplyState();
// Pin the buffers.
GCHandle vbHandle = GCHandle.Alloc(vertexData, GCHandleType.Pinned);
GCHandle ibHandle = GCHandle.Alloc(indexData, GCHandleType.Pinned);
IntPtr vbPtr = vbHandle.AddrOfPinnedObject();
IntPtr ibPtr = ibHandle.AddrOfPinnedObject();
// Setup the vertex declaration to point at the vertex data.
vertexDeclaration.GraphicsDevice = this;
GLDevice.ApplyVertexAttributes(
vertexDeclaration,
vbPtr,
vertexOffset
);
GLDevice.DrawUserIndexedPrimitives(
primitiveType,
vbPtr,
vertexOffset,
numVertices,
ibPtr,
indexOffset,
IndexElementSize.SixteenBits,
primitiveCount
);
// Release the handles.
ibHandle.Free();
vbHandle.Free();
}
public void DrawUserIndexedPrimitives<T>(
PrimitiveType primitiveType,
T[] vertexData,
int vertexOffset,
int numVertices,
int[] indexData,
int indexOffset,
int primitiveCount
) where T : struct, IVertexType {
ApplyState();
// Pin the buffers.
GCHandle vbHandle = GCHandle.Alloc(vertexData, GCHandleType.Pinned);
GCHandle ibHandle = GCHandle.Alloc(indexData, GCHandleType.Pinned);
IntPtr vbPtr = vbHandle.AddrOfPinnedObject();
IntPtr ibPtr = ibHandle.AddrOfPinnedObject();
// Setup the vertex declaration to point at the vertex data.
VertexDeclaration vertexDeclaration = VertexDeclarationCache<T>.VertexDeclaration;
vertexDeclaration.GraphicsDevice = this;
GLDevice.ApplyVertexAttributes(
vertexDeclaration,
vbPtr,
vertexOffset
);
GLDevice.DrawUserIndexedPrimitives(
primitiveType,
vbPtr,
vertexOffset,
numVertices,
ibPtr,
indexOffset,
IndexElementSize.ThirtyTwoBits,
primitiveCount
);
// Release the handles.
ibHandle.Free();
vbHandle.Free();
}
public void DrawUserIndexedPrimitives<T>(
PrimitiveType primitiveType,
T[] vertexData,
int vertexOffset,
int numVertices,
int[] indexData,
int indexOffset,
int primitiveCount,
VertexDeclaration vertexDeclaration
) where T : struct {
ApplyState();
// Pin the buffers.
GCHandle vbHandle = GCHandle.Alloc(vertexData, GCHandleType.Pinned);
GCHandle ibHandle = GCHandle.Alloc(indexData, GCHandleType.Pinned);
IntPtr vbPtr = vbHandle.AddrOfPinnedObject();
IntPtr ibPtr = ibHandle.AddrOfPinnedObject();
// Setup the vertex declaration to point at the vertex data.
vertexDeclaration.GraphicsDevice = this;
GLDevice.ApplyVertexAttributes(
vertexDeclaration,
vbPtr,
vertexOffset
);
GLDevice.DrawUserIndexedPrimitives(
primitiveType,
vbPtr,
vertexOffset,
numVertices,
ibPtr,
indexOffset,
IndexElementSize.ThirtyTwoBits,
primitiveCount
);
// Release the handles.
ibHandle.Free();
vbHandle.Free();
}
#endregion
#region DrawPrimitives: Vertex Arrays, No Indices
public void DrawUserPrimitives<T>(
PrimitiveType primitiveType,
T[] vertexData,
int vertexOffset,
int primitiveCount
) where T : struct, IVertexType {
ApplyState();
// Pin the buffers.
GCHandle vbHandle = GCHandle.Alloc(vertexData, GCHandleType.Pinned);
IntPtr vbPtr = vbHandle.AddrOfPinnedObject();
// Setup the vertex declaration to point at the vertex data.
VertexDeclaration vertexDeclaration = VertexDeclarationCache<T>.VertexDeclaration;
vertexDeclaration.GraphicsDevice = this;
GLDevice.ApplyVertexAttributes(
vertexDeclaration,
vbPtr,
0
);
GLDevice.DrawUserPrimitives(
primitiveType,
vbPtr,
vertexOffset,
primitiveCount
);
// Release the handles.
vbHandle.Free();
}
public void DrawUserPrimitives<T>(
PrimitiveType primitiveType,
T[] vertexData,
int vertexOffset,
int primitiveCount,
VertexDeclaration vertexDeclaration
) where T : struct {
ApplyState();
// Pin the buffers.
GCHandle vbHandle = GCHandle.Alloc(vertexData, GCHandleType.Pinned);
IntPtr vbPtr = vbHandle.AddrOfPinnedObject();
// Setup the vertex declaration to point at the vertex data.
vertexDeclaration.GraphicsDevice = this;
GLDevice.ApplyVertexAttributes(
vertexDeclaration,
vbPtr,
0
);
GLDevice.DrawUserPrimitives(
primitiveType,
vbPtr,
vertexOffset,
primitiveCount
);
// Release the handles.
vbHandle.Free();
}
#endregion
#region FNA Extensions
public void SetStringMarkerEXT(string text)
{
GLDevice.SetStringMarker(text);
}
#endregion
#region Private State Flush Methods
private void ApplyState()
{
// Update Blend/DepthStencil, if applicable
if (currentBlend != nextBlend)
{
GLDevice.SetBlendState(nextBlend);
currentBlend = nextBlend;
}
if (currentDepthStencil != nextDepthStencil)
{
GLDevice.SetDepthStencilState(nextDepthStencil);
currentDepthStencil = nextDepthStencil;
}
// Always update RasterizerState, as it depends on other device states
GLDevice.ApplyRasterizerState(RasterizerState);
for (int sampler = 0; sampler < modifiedSamplers.Length; sampler += 1)
{
if (!modifiedSamplers[sampler])
{
continue;
}
modifiedSamplers[sampler] = false;
GLDevice.VerifySampler(
sampler,
Textures[sampler],
SamplerStates[sampler]
);
}
for (int sampler = 0; sampler < modifiedVertexSamplers.Length; sampler += 1)
{
if (!modifiedVertexSamplers[sampler])
{
continue;
}
modifiedVertexSamplers[sampler] = false;
/* Believe it or not, this is actually how VertexTextures are
* stored in XNA4! Their D3D9 renderer just uses the last 4
* slots available in the device's sampler array. So that's what
* we get to do.
* -flibit
*/
GLDevice.VerifySampler(
vertexSamplerStart + sampler,
VertexTextures[sampler],
VertexSamplerStates[sampler]
);
}
}
#endregion
#region Wii U GamePad Support, libdrc Interop
#if WIIU_GAMEPAD
private static class DRC
{
// FIXME: Deal with Mac/Windows LibName later.
private const string nativeLibName = "libdrc.so";
public enum drc_pixel_format
{
DRC_RGB,
DRC_RGBA,
DRC_BGR,
DRC_BGRA,
DRC_RGB565
}
public enum drc_flipping_mode
{
DRC_NO_FLIP,
DRC_FLIP_VERTICALLY
}
/* IntPtr refers to a drc_streamer* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr drc_new_streamer();
/* self refers to a drc_streamer* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void drc_delete_streamer(IntPtr self);
/* self refers to a drc_streamer* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int drc_start_streamer(IntPtr self);
/* self refers to a drc_streamer* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void drc_stop_streamer(IntPtr self);
/* self refers to a drc_streamer* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int drc_push_vid_frame(
IntPtr self,
byte[] buffer,
uint size,
ushort width,
ushort height,
drc_pixel_format pixfmt,
drc_flipping_mode flipmode
);
/* self refers to a drc_streamer* */
[DllImport(nativeLibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void drc_enable_system_input_feeder(IntPtr self);
}
private IntPtr wiiuStream;
private byte[] wiiuPixelData;
#endif
#endregion
}
}