Show More
Commit Description:
Add timers for Simulation and various engines...
Commit Description:
Add timers for Simulation and various engines Starting to add additional timers for different stages of the process of updating in order to get more insight into what is slowing it down. The update takes 9ms, which is much longer than it used to. Engine-specific timers are coming later.
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
}
}