1294 lines
52 KiB
C#
1294 lines
52 KiB
C#
// MonoGame - Copyright (C) The MonoGame Team
|
|
// This file is subject to the terms and conditions defined in
|
|
// file 'LICENSE.txt', which is part of this source code package.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
using System.Diagnostics;
|
|
|
|
#if ANGLE
|
|
using OpenTK.Graphics;
|
|
#else
|
|
using MonoGame.OpenGL;
|
|
#endif
|
|
|
|
namespace Microsoft.Xna.Framework.Graphics
|
|
{
|
|
public partial class GraphicsDevice
|
|
{
|
|
#if DESKTOPGL || ANGLE
|
|
internal IGraphicsContext Context { get; private set; }
|
|
#endif
|
|
|
|
#if !GLES
|
|
private DrawBuffersEnum[] _drawBuffers;
|
|
#endif
|
|
|
|
enum ResourceType
|
|
{
|
|
Texture,
|
|
Buffer,
|
|
Shader,
|
|
Program,
|
|
Query,
|
|
Framebuffer
|
|
}
|
|
|
|
struct ResourceHandle
|
|
{
|
|
public ResourceType type;
|
|
public int handle;
|
|
|
|
public static ResourceHandle Texture(int handle)
|
|
{
|
|
return new ResourceHandle() { type = ResourceType.Texture, handle = handle };
|
|
}
|
|
|
|
public static ResourceHandle Buffer(int handle)
|
|
{
|
|
return new ResourceHandle() { type = ResourceType.Buffer, handle = handle };
|
|
}
|
|
|
|
public static ResourceHandle Shader(int handle)
|
|
{
|
|
return new ResourceHandle() { type = ResourceType.Shader, handle = handle };
|
|
}
|
|
|
|
public static ResourceHandle Program(int handle)
|
|
{
|
|
return new ResourceHandle() { type = ResourceType.Program, handle = handle };
|
|
}
|
|
|
|
public static ResourceHandle Query(int handle)
|
|
{
|
|
return new ResourceHandle() { type = ResourceType.Query, handle = handle };
|
|
}
|
|
|
|
public static ResourceHandle Framebuffer(int handle)
|
|
{
|
|
return new ResourceHandle() { type = ResourceType.Framebuffer, handle = handle };
|
|
}
|
|
|
|
public void Free()
|
|
{
|
|
switch (type)
|
|
{
|
|
case ResourceType.Texture:
|
|
GL.DeleteTextures(1, ref handle);
|
|
break;
|
|
case ResourceType.Buffer:
|
|
GL.DeleteBuffers(1, ref handle);
|
|
break;
|
|
case ResourceType.Shader:
|
|
if (GL.IsShader(handle))
|
|
GL.DeleteShader(handle);
|
|
break;
|
|
case ResourceType.Program:
|
|
if (GL.IsProgram(handle))
|
|
{
|
|
GL.DeleteProgram(handle);
|
|
}
|
|
break;
|
|
case ResourceType.Query:
|
|
#if !GLES
|
|
GL.DeleteQueries(1, ref handle);
|
|
#endif
|
|
break;
|
|
case ResourceType.Framebuffer:
|
|
GL.DeleteFramebuffers(1, ref handle);
|
|
break;
|
|
}
|
|
GraphicsExtensions.CheckGLError();
|
|
}
|
|
}
|
|
|
|
List<ResourceHandle> _disposeThisFrame = new List<ResourceHandle>();
|
|
List<ResourceHandle> _disposeNextFrame = new List<ResourceHandle>();
|
|
object _disposeActionsLock = new object();
|
|
|
|
static List<IntPtr> _disposeContexts = new List<IntPtr>();
|
|
static object _disposeContextsLock = new object();
|
|
|
|
private ShaderProgramCache _programCache;
|
|
private ShaderProgram _shaderProgram = null;
|
|
|
|
static readonly float[] _posFixup = new float[4];
|
|
|
|
private static BufferBindingInfo[] _bufferBindingInfos;
|
|
private static bool[] _newEnabledVertexAttributes;
|
|
internal static readonly List<int> _enabledVertexAttributes = new List<int>();
|
|
internal static bool _attribsDirty;
|
|
|
|
internal FramebufferHelper framebufferHelper;
|
|
|
|
internal int glMajorVersion = 0;
|
|
internal int glMinorVersion = 0;
|
|
internal int glFramebuffer = 0;
|
|
internal int MaxVertexAttributes;
|
|
internal int _maxTextureSize = 0;
|
|
|
|
// Keeps track of last applied state to avoid redundant OpenGL calls
|
|
internal bool _lastBlendEnable = false;
|
|
internal BlendState _lastBlendState = new BlendState();
|
|
internal DepthStencilState _lastDepthStencilState = new DepthStencilState();
|
|
internal RasterizerState _lastRasterizerState = new RasterizerState();
|
|
private Vector4 _lastClearColor = Vector4.Zero;
|
|
private float _lastClearDepth = 1.0f;
|
|
private int _lastClearStencil = 0;
|
|
|
|
// Get a hashed value based on the currently bound shaders
|
|
// throws an exception if no shaders are bound
|
|
private int ShaderProgramHash
|
|
{
|
|
get
|
|
{
|
|
if (_vertexShader == null && _pixelShader == null)
|
|
throw new InvalidOperationException("There is no shader bound!");
|
|
if (_vertexShader == null)
|
|
return _pixelShader.HashKey;
|
|
if (_pixelShader == null)
|
|
return _vertexShader.HashKey;
|
|
return _vertexShader.HashKey ^ _pixelShader.HashKey;
|
|
}
|
|
}
|
|
|
|
internal void SetVertexAttributeArray(bool[] attrs)
|
|
{
|
|
for (var x = 0; x < attrs.Length; x++)
|
|
{
|
|
if (attrs[x] && !_enabledVertexAttributes.Contains(x))
|
|
{
|
|
_enabledVertexAttributes.Add(x);
|
|
GL.EnableVertexAttribArray(x);
|
|
GraphicsExtensions.CheckGLError();
|
|
}
|
|
else if (!attrs[x] && _enabledVertexAttributes.Contains(x))
|
|
{
|
|
_enabledVertexAttributes.Remove(x);
|
|
GL.DisableVertexAttribArray(x);
|
|
GraphicsExtensions.CheckGLError();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ApplyAttribs(Shader shader, int baseVertex)
|
|
{
|
|
var programHash = ShaderProgramHash;
|
|
var bindingsChanged = false;
|
|
|
|
for (var slot = 0; slot < _vertexBuffers.Count; slot++)
|
|
{
|
|
var vertexBufferBinding = _vertexBuffers.Get(slot);
|
|
var vertexDeclaration = vertexBufferBinding.VertexBuffer.VertexDeclaration;
|
|
var attrInfo = vertexDeclaration.GetAttributeInfo(shader, programHash);
|
|
|
|
var vertexStride = vertexDeclaration.VertexStride;
|
|
var offset = (IntPtr)(vertexDeclaration.VertexStride * (baseVertex + vertexBufferBinding.VertexOffset));
|
|
|
|
if (!_attribsDirty &&
|
|
_bufferBindingInfos[slot].VertexOffset == offset &&
|
|
ReferenceEquals(_bufferBindingInfos[slot].AttributeInfo, attrInfo) &&
|
|
_bufferBindingInfos[slot].InstanceFrequency == vertexBufferBinding.InstanceFrequency &&
|
|
_bufferBindingInfos[slot].Vbo == vertexBufferBinding.VertexBuffer.vbo)
|
|
continue;
|
|
|
|
bindingsChanged = true;
|
|
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBufferBinding.VertexBuffer.vbo);
|
|
GraphicsExtensions.CheckGLError();
|
|
|
|
// If instancing is not supported, but InstanceFrequency of the buffer is not zero, throw an exception
|
|
if (!GraphicsCapabilities.SupportsInstancing && vertexBufferBinding.InstanceFrequency > 0)
|
|
throw new PlatformNotSupportedException("Instanced geometry drawing requires at least OpenGL 3.2 or GLES 3.2. Try upgrading your graphics drivers.");
|
|
|
|
foreach (var element in attrInfo.Elements)
|
|
{
|
|
GL.VertexAttribPointer(element.AttributeLocation,
|
|
element.NumberOfElements,
|
|
element.VertexAttribPointerType,
|
|
element.Normalized,
|
|
vertexStride,
|
|
(IntPtr)(offset.ToInt64() + element.Offset));
|
|
|
|
// only set the divisor if instancing is supported
|
|
if (GraphicsCapabilities.SupportsInstancing)
|
|
GL.VertexAttribDivisor(element.AttributeLocation, vertexBufferBinding.InstanceFrequency);
|
|
|
|
GraphicsExtensions.CheckGLError();
|
|
}
|
|
|
|
_bufferBindingInfos[slot].VertexOffset = offset;
|
|
_bufferBindingInfos[slot].AttributeInfo = attrInfo;
|
|
_bufferBindingInfos[slot].InstanceFrequency = vertexBufferBinding.InstanceFrequency;
|
|
_bufferBindingInfos[slot].Vbo = vertexBufferBinding.VertexBuffer.vbo;
|
|
}
|
|
|
|
_attribsDirty = false;
|
|
|
|
if (bindingsChanged)
|
|
{
|
|
Array.Clear(_newEnabledVertexAttributes, 0, _newEnabledVertexAttributes.Length);
|
|
for (var slot = 0; slot < _vertexBuffers.Count; slot++)
|
|
{
|
|
foreach (var element in _bufferBindingInfos[slot].AttributeInfo.Elements)
|
|
_newEnabledVertexAttributes[element.AttributeLocation] = true;
|
|
}
|
|
}
|
|
SetVertexAttributeArray(_newEnabledVertexAttributes);
|
|
}
|
|
|
|
private void PlatformSetup()
|
|
{
|
|
_programCache = new ShaderProgramCache(this);
|
|
#if DESKTOPGL || ANGLE
|
|
var windowInfo = new WindowInfo(SdlGameWindow.Instance.Handle);
|
|
|
|
if (Context == null || Context.IsDisposed)
|
|
{
|
|
Context = GL.CreateContext(windowInfo);
|
|
}
|
|
|
|
Context.MakeCurrent(windowInfo);
|
|
Context.SwapInterval = PresentationParameters.PresentationInterval.GetSwapInterval();
|
|
|
|
Context.MakeCurrent(windowInfo);
|
|
#endif
|
|
MaxTextureSlots = 16;
|
|
|
|
GL.GetInteger(GetPName.MaxTextureImageUnits, out MaxTextureSlots);
|
|
GraphicsExtensions.CheckGLError();
|
|
|
|
GL.GetInteger(GetPName.MaxTextureSize, out _maxTextureSize);
|
|
GraphicsExtensions.CheckGLError();
|
|
|
|
GL.GetInteger(GetPName.MaxVertexAttribs, out MaxVertexAttributes);
|
|
GraphicsExtensions.CheckGLError();
|
|
|
|
_maxVertexBufferSlots = MaxVertexAttributes;
|
|
_newEnabledVertexAttributes = new bool[MaxVertexAttributes];
|
|
|
|
|
|
SpriteBatch.NeedsHalfPixelOffset = true;
|
|
|
|
// try getting the context version
|
|
// GL_MAJOR_VERSION and GL_MINOR_VERSION are GL 3.0+ only, so we need to rely on the GL_VERSION string
|
|
// for non GLES this string always starts with the version number in the "major.minor" format, but can be followed by
|
|
// multiple vendor specific characters
|
|
// For GLES this string is formatted as: OpenGL<space>ES<space><version number><space><vendor-specific information>
|
|
#if GLES
|
|
try
|
|
{
|
|
string version = GL.GetString(StringName.Version);
|
|
|
|
if (string.IsNullOrEmpty(version))
|
|
throw new NoSuitableGraphicsDeviceException("Unable to retrieve OpenGL version");
|
|
|
|
string[] versionSplit = version.Split(' ');
|
|
if (versionSplit.Length > 2 && versionSplit[0].Equals("OpenGL") && versionSplit[1].Equals("ES"))
|
|
{
|
|
glMajorVersion = Convert.ToInt32(versionSplit[2].Substring(0, 1));
|
|
glMinorVersion = Convert.ToInt32(versionSplit[2].Substring(2, 1));
|
|
}
|
|
else
|
|
{
|
|
glMajorVersion = 1;
|
|
glMinorVersion = 1;
|
|
}
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
//if it fails we default to 1.1 context
|
|
glMajorVersion = 1;
|
|
glMinorVersion = 1;
|
|
}
|
|
#else
|
|
try
|
|
{
|
|
string version = GL.GetString(StringName.Version);
|
|
|
|
if (string.IsNullOrEmpty(version))
|
|
throw new NoSuitableGraphicsDeviceException("Unable to retrieve OpenGL version");
|
|
|
|
glMajorVersion = Convert.ToInt32(version.Substring(0, 1));
|
|
glMinorVersion = Convert.ToInt32(version.Substring(2, 1));
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
// if it fails, we assume to be on a 1.1 context
|
|
glMajorVersion = 1;
|
|
glMinorVersion = 1;
|
|
}
|
|
#endif
|
|
|
|
#if !GLES
|
|
// Initialize draw buffer attachment array
|
|
int maxDrawBuffers;
|
|
GL.GetInteger(GetPName.MaxDrawBuffers, out maxDrawBuffers);
|
|
GraphicsExtensions.CheckGLError ();
|
|
_drawBuffers = new DrawBuffersEnum[maxDrawBuffers];
|
|
for (int i = 0; i < maxDrawBuffers; i++)
|
|
_drawBuffers[i] = (DrawBuffersEnum)(FramebufferAttachment.ColorAttachment0Ext + i);
|
|
#endif
|
|
}
|
|
|
|
private void PlatformInitialize()
|
|
{
|
|
_viewport = new Viewport(0, 0, PresentationParameters.BackBufferWidth, PresentationParameters.BackBufferHeight);
|
|
|
|
// Ensure the vertex attributes are reset
|
|
_enabledVertexAttributes.Clear();
|
|
|
|
// Free all the cached shader programs.
|
|
_programCache.Clear();
|
|
_shaderProgram = null;
|
|
|
|
framebufferHelper = FramebufferHelper.Create(this);
|
|
|
|
// Force resetting states
|
|
this.PlatformApplyBlend(true);
|
|
this.DepthStencilState.PlatformApplyState(this, true);
|
|
this.RasterizerState.PlatformApplyState(this, true);
|
|
|
|
_bufferBindingInfos = new BufferBindingInfo[_maxVertexBufferSlots];
|
|
for (int i = 0; i < _bufferBindingInfos.Length; i++)
|
|
_bufferBindingInfos[i] = new BufferBindingInfo(null, IntPtr.Zero, 0, -1);
|
|
|
|
Clear(Color.Black);
|
|
PlatformPresent();
|
|
}
|
|
|
|
partial void PlatformReset()
|
|
{
|
|
Clear(Color.Black);
|
|
PlatformPresent();
|
|
}
|
|
|
|
private DepthStencilState clearDepthStencilState = new DepthStencilState { StencilEnable = true };
|
|
|
|
public void PlatformClear(ClearOptions options, Vector4 color, float depth, int stencil)
|
|
{
|
|
// TODO: We need to figure out how to detect if we have a
|
|
// depth stencil buffer or not, and clear options relating
|
|
// to them if not attached.
|
|
|
|
// Unlike with XNA and DirectX... GL.Clear() obeys several
|
|
// different render states:
|
|
//
|
|
// - The color write flags.
|
|
// - The scissor rectangle.
|
|
// - The depth/stencil state.
|
|
//
|
|
// So overwrite these states with what is needed to perform
|
|
// the clear correctly and restore it afterwards.
|
|
//
|
|
var prevScissorRect = ScissorRectangle;
|
|
var prevDepthStencilState = DepthStencilState;
|
|
var prevBlendState = BlendState;
|
|
ScissorRectangle = _viewport.Bounds;
|
|
// DepthStencilState.Default has the Stencil Test disabled;
|
|
// make sure stencil test is enabled before we clear since
|
|
// some drivers won't clear with stencil test disabled
|
|
DepthStencilState = this.clearDepthStencilState;
|
|
BlendState = BlendState.Opaque;
|
|
ApplyState(false);
|
|
|
|
ClearBufferMask bufferMask = 0;
|
|
if ((options & ClearOptions.Target) == ClearOptions.Target)
|
|
{
|
|
if (color != _lastClearColor)
|
|
{
|
|
GL.ClearColor(color.X, color.Y, color.Z, color.W);
|
|
GraphicsExtensions.CheckGLError();
|
|
_lastClearColor = color;
|
|
}
|
|
bufferMask = bufferMask | ClearBufferMask.ColorBufferBit;
|
|
}
|
|
if ((options & ClearOptions.Stencil) == ClearOptions.Stencil)
|
|
{
|
|
if (stencil != _lastClearStencil)
|
|
{
|
|
GL.ClearStencil(stencil);
|
|
GraphicsExtensions.CheckGLError();
|
|
_lastClearStencil = stencil;
|
|
}
|
|
bufferMask = bufferMask | ClearBufferMask.StencilBufferBit;
|
|
}
|
|
|
|
if ((options & ClearOptions.DepthBuffer) == ClearOptions.DepthBuffer)
|
|
{
|
|
if (depth != _lastClearDepth)
|
|
{
|
|
GL.ClearDepth(depth);
|
|
GraphicsExtensions.CheckGLError();
|
|
_lastClearDepth = depth;
|
|
}
|
|
bufferMask = bufferMask | ClearBufferMask.DepthBufferBit;
|
|
}
|
|
|
|
#if MONOMAC
|
|
if (GL.CheckFramebufferStatus(FramebufferTarget.FramebufferExt) == FramebufferErrorCode.FramebufferComplete)
|
|
{
|
|
#endif
|
|
GL.Clear(bufferMask);
|
|
GraphicsExtensions.CheckGLError();
|
|
#if MONOMAC
|
|
}
|
|
#endif
|
|
|
|
// Restore the previous render state.
|
|
ScissorRectangle = prevScissorRect;
|
|
DepthStencilState = prevDepthStencilState;
|
|
BlendState = prevBlendState;
|
|
}
|
|
|
|
private void PlatformDispose()
|
|
{
|
|
// Free all the cached shader programs.
|
|
_programCache.Dispose();
|
|
|
|
#if DESKTOPGL || ANGLE
|
|
Context.Dispose();
|
|
Context = null;
|
|
#endif
|
|
}
|
|
|
|
internal void DisposeTexture(int handle)
|
|
{
|
|
if (!_isDisposed)
|
|
{
|
|
lock (_disposeActionsLock)
|
|
{
|
|
_disposeNextFrame.Add(ResourceHandle.Texture(handle));
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void DisposeBuffer(int handle)
|
|
{
|
|
if (!_isDisposed)
|
|
{
|
|
lock (_disposeActionsLock)
|
|
{
|
|
_disposeNextFrame.Add(ResourceHandle.Buffer(handle));
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void DisposeShader(int handle)
|
|
{
|
|
if (!_isDisposed)
|
|
{
|
|
lock (_disposeActionsLock)
|
|
{
|
|
_disposeNextFrame.Add(ResourceHandle.Shader(handle));
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void DisposeProgram(int handle)
|
|
{
|
|
if (!_isDisposed)
|
|
{
|
|
lock (_disposeActionsLock)
|
|
{
|
|
_disposeNextFrame.Add(ResourceHandle.Program(handle));
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void DisposeQuery(int handle)
|
|
{
|
|
if (!_isDisposed)
|
|
{
|
|
lock (_disposeActionsLock)
|
|
{
|
|
_disposeNextFrame.Add(ResourceHandle.Query(handle));
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void DisposeFramebuffer(int handle)
|
|
{
|
|
if (!_isDisposed)
|
|
{
|
|
lock (_disposeActionsLock)
|
|
{
|
|
_disposeNextFrame.Add(ResourceHandle.Framebuffer(handle));
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DESKTOPGL || ANGLE
|
|
static internal void DisposeContext(IntPtr resource)
|
|
{
|
|
lock (_disposeContextsLock)
|
|
{
|
|
_disposeContexts.Add(resource);
|
|
}
|
|
}
|
|
|
|
static internal void DisposeContexts()
|
|
{
|
|
lock (_disposeContextsLock)
|
|
{
|
|
int count = _disposeContexts.Count;
|
|
for (int i = 0; i < count; ++i)
|
|
Sdl.GL.DeleteContext(_disposeContexts[i]);
|
|
_disposeContexts.Clear();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
public void PlatformPresent()
|
|
{
|
|
#if DESKTOPGL || ANGLE
|
|
Context.SwapBuffers();
|
|
#endif
|
|
GraphicsExtensions.CheckGLError();
|
|
|
|
// Dispose of any GL resources that were disposed in another thread
|
|
int count = _disposeThisFrame.Count;
|
|
for (int i = 0; i < count; ++i)
|
|
_disposeThisFrame[i].Free();
|
|
_disposeThisFrame.Clear();
|
|
|
|
lock (_disposeActionsLock)
|
|
{
|
|
// Swap lists so resources added during this draw will be released after the next draw
|
|
var temp = _disposeThisFrame;
|
|
_disposeThisFrame = _disposeNextFrame;
|
|
_disposeNextFrame = temp;
|
|
}
|
|
}
|
|
|
|
private void PlatformSetViewport(ref Viewport value)
|
|
{
|
|
if (IsRenderTargetBound)
|
|
GL.Viewport(value.X, value.Y, value.Width, value.Height);
|
|
else
|
|
GL.Viewport(value.X, PresentationParameters.BackBufferHeight - value.Y - value.Height, value.Width, value.Height);
|
|
GraphicsExtensions.LogGLError("GraphicsDevice.Viewport_set() GL.Viewport");
|
|
|
|
GL.DepthRange(value.MinDepth, value.MaxDepth);
|
|
GraphicsExtensions.LogGLError("GraphicsDevice.Viewport_set() GL.DepthRange");
|
|
|
|
// In OpenGL we have to re-apply the special "posFixup"
|
|
// vertex shader uniform if the viewport changes.
|
|
_vertexShaderDirty = true;
|
|
|
|
}
|
|
|
|
private void PlatformApplyDefaultRenderTarget()
|
|
{
|
|
this.framebufferHelper.BindFramebuffer(this.glFramebuffer);
|
|
|
|
// Reset the raster state because we flip vertices
|
|
// when rendering offscreen and hence the cull direction.
|
|
_rasterizerStateDirty = true;
|
|
|
|
// Textures will need to be rebound to render correctly in the new render target.
|
|
Textures.Dirty();
|
|
}
|
|
|
|
private class RenderTargetBindingArrayComparer : IEqualityComparer<RenderTargetBinding[]>
|
|
{
|
|
public bool Equals(RenderTargetBinding[] first, RenderTargetBinding[] second)
|
|
{
|
|
if (object.ReferenceEquals(first, second))
|
|
return true;
|
|
|
|
if (first == null || second == null)
|
|
return false;
|
|
|
|
if (first.Length != second.Length)
|
|
return false;
|
|
|
|
for (var i = 0; i < first.Length; ++i)
|
|
{
|
|
if ((first[i].RenderTarget != second[i].RenderTarget) || (first[i].ArraySlice != second[i].ArraySlice))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public int GetHashCode(RenderTargetBinding[] array)
|
|
{
|
|
if (array != null)
|
|
{
|
|
unchecked
|
|
{
|
|
int hash = 17;
|
|
foreach (var item in array)
|
|
{
|
|
if (item.RenderTarget != null)
|
|
hash = hash * 23 + item.RenderTarget.GetHashCode();
|
|
hash = hash * 23 + item.ArraySlice.GetHashCode();
|
|
}
|
|
return hash;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// FBO cache, we create 1 FBO per RenderTargetBinding combination
|
|
private Dictionary<RenderTargetBinding[], int> glFramebuffers = new Dictionary<RenderTargetBinding[], int>(new RenderTargetBindingArrayComparer());
|
|
// FBO cache used to resolve MSAA rendertargets, we create 1 FBO per RenderTargetBinding combination
|
|
private Dictionary<RenderTargetBinding[], int> glResolveFramebuffers = new Dictionary<RenderTargetBinding[], int>(new RenderTargetBindingArrayComparer());
|
|
|
|
internal void PlatformCreateRenderTarget(IRenderTarget renderTarget, int width, int height, bool mipMap, SurfaceFormat preferredFormat, DepthFormat preferredDepthFormat, int preferredMultiSampleCount, RenderTargetUsage usage)
|
|
{
|
|
var color = 0;
|
|
var depth = 0;
|
|
var stencil = 0;
|
|
|
|
if (preferredMultiSampleCount > 0 && this.framebufferHelper.SupportsBlitFramebuffer)
|
|
{
|
|
this.framebufferHelper.GenRenderbuffer(out color);
|
|
this.framebufferHelper.BindRenderbuffer(color);
|
|
this.framebufferHelper.RenderbufferStorageMultisample(preferredMultiSampleCount, (int)RenderbufferStorage.Rgba8, width, height);
|
|
}
|
|
|
|
if (preferredDepthFormat != DepthFormat.None)
|
|
{
|
|
var depthInternalFormat = RenderbufferStorage.DepthComponent16;
|
|
var stencilInternalFormat = (RenderbufferStorage)0;
|
|
switch (preferredDepthFormat)
|
|
{
|
|
case DepthFormat.Depth16:
|
|
depthInternalFormat = RenderbufferStorage.DepthComponent16;
|
|
break;
|
|
#if GLES
|
|
case DepthFormat.Depth24:
|
|
if (GraphicsCapabilities.SupportsDepth24)
|
|
depthInternalFormat = RenderbufferStorage.DepthComponent24Oes;
|
|
else if (GraphicsCapabilities.SupportsDepthNonLinear)
|
|
depthInternalFormat = (RenderbufferStorage)0x8E2C;
|
|
else
|
|
depthInternalFormat = RenderbufferStorage.DepthComponent16;
|
|
break;
|
|
case DepthFormat.Depth24Stencil8:
|
|
if (GraphicsCapabilities.SupportsPackedDepthStencil)
|
|
depthInternalFormat = RenderbufferStorage.Depth24Stencil8Oes;
|
|
else
|
|
{
|
|
if (GraphicsCapabilities.SupportsDepth24)
|
|
depthInternalFormat = RenderbufferStorage.DepthComponent24Oes;
|
|
else if (GraphicsCapabilities.SupportsDepthNonLinear)
|
|
depthInternalFormat = (RenderbufferStorage)0x8E2C;
|
|
else
|
|
depthInternalFormat = RenderbufferStorage.DepthComponent16;
|
|
stencilInternalFormat = RenderbufferStorage.StencilIndex8;
|
|
break;
|
|
}
|
|
break;
|
|
#else
|
|
case DepthFormat.Depth24:
|
|
depthInternalFormat = RenderbufferStorage.DepthComponent24;
|
|
break;
|
|
case DepthFormat.Depth24Stencil8:
|
|
depthInternalFormat = RenderbufferStorage.Depth24Stencil8;
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
if (depthInternalFormat != 0)
|
|
{
|
|
this.framebufferHelper.GenRenderbuffer(out depth);
|
|
this.framebufferHelper.BindRenderbuffer(depth);
|
|
this.framebufferHelper.RenderbufferStorageMultisample(preferredMultiSampleCount, (int)depthInternalFormat, width, height);
|
|
if (preferredDepthFormat == DepthFormat.Depth24Stencil8)
|
|
{
|
|
stencil = depth;
|
|
if (stencilInternalFormat != 0)
|
|
{
|
|
this.framebufferHelper.GenRenderbuffer(out stencil);
|
|
this.framebufferHelper.BindRenderbuffer(stencil);
|
|
this.framebufferHelper.RenderbufferStorageMultisample(preferredMultiSampleCount, (int)stencilInternalFormat, width, height);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (color != 0)
|
|
renderTarget.GLColorBuffer = color;
|
|
else
|
|
renderTarget.GLColorBuffer = renderTarget.GLTexture;
|
|
renderTarget.GLDepthBuffer = depth;
|
|
renderTarget.GLStencilBuffer = stencil;
|
|
}
|
|
|
|
internal void PlatformDeleteRenderTarget(IRenderTarget renderTarget)
|
|
{
|
|
var color = 0;
|
|
var depth = 0;
|
|
var stencil = 0;
|
|
var colorIsRenderbuffer = false;
|
|
|
|
color = renderTarget.GLColorBuffer;
|
|
depth = renderTarget.GLDepthBuffer;
|
|
stencil = renderTarget.GLStencilBuffer;
|
|
colorIsRenderbuffer = color != renderTarget.GLTexture;
|
|
|
|
if (color != 0)
|
|
{
|
|
if (colorIsRenderbuffer)
|
|
this.framebufferHelper.DeleteRenderbuffer(color);
|
|
if (stencil != 0 && stencil != depth)
|
|
this.framebufferHelper.DeleteRenderbuffer(stencil);
|
|
if (depth != 0)
|
|
this.framebufferHelper.DeleteRenderbuffer(depth);
|
|
|
|
var bindingsToDelete = new List<RenderTargetBinding[]>();
|
|
foreach (var bindings in this.glFramebuffers.Keys)
|
|
{
|
|
foreach (var binding in bindings)
|
|
{
|
|
if (binding.RenderTarget == renderTarget)
|
|
{
|
|
bindingsToDelete.Add(bindings);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var bindings in bindingsToDelete)
|
|
{
|
|
var fbo = 0;
|
|
if (this.glFramebuffers.TryGetValue(bindings, out fbo))
|
|
{
|
|
this.framebufferHelper.DeleteFramebuffer(fbo);
|
|
this.glFramebuffers.Remove(bindings);
|
|
}
|
|
if (this.glResolveFramebuffers.TryGetValue(bindings, out fbo))
|
|
{
|
|
this.framebufferHelper.DeleteFramebuffer(fbo);
|
|
this.glResolveFramebuffers.Remove(bindings);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PlatformResolveRenderTargets()
|
|
{
|
|
if (this._currentRenderTargetCount == 0)
|
|
return;
|
|
|
|
var renderTargetBinding = this._currentRenderTargetBindings[0];
|
|
var renderTarget = renderTargetBinding.RenderTarget as IRenderTarget;
|
|
if (renderTarget.MultiSampleCount > 0 && this.framebufferHelper.SupportsBlitFramebuffer)
|
|
{
|
|
var glResolveFramebuffer = 0;
|
|
if (!this.glResolveFramebuffers.TryGetValue(this._currentRenderTargetBindings, out glResolveFramebuffer))
|
|
{
|
|
this.framebufferHelper.GenFramebuffer(out glResolveFramebuffer);
|
|
this.framebufferHelper.BindFramebuffer(glResolveFramebuffer);
|
|
for (var i = 0; i < this._currentRenderTargetCount; ++i)
|
|
{
|
|
var rt = this._currentRenderTargetBindings[i].RenderTarget as IRenderTarget;
|
|
this.framebufferHelper.FramebufferTexture2D((int)(FramebufferAttachment.ColorAttachment0 + i), (int) rt.GetFramebufferTarget(renderTargetBinding), rt.GLTexture);
|
|
}
|
|
this.glResolveFramebuffers.Add((RenderTargetBinding[])this._currentRenderTargetBindings.Clone(), glResolveFramebuffer);
|
|
}
|
|
else
|
|
{
|
|
this.framebufferHelper.BindFramebuffer(glResolveFramebuffer);
|
|
}
|
|
// The only fragment operations which affect the resolve are the pixel ownership test, the scissor test, and dithering.
|
|
if (this._lastRasterizerState.ScissorTestEnable)
|
|
{
|
|
GL.Disable(EnableCap.ScissorTest);
|
|
GraphicsExtensions.CheckGLError();
|
|
}
|
|
var glFramebuffer = this.glFramebuffers[this._currentRenderTargetBindings];
|
|
this.framebufferHelper.BindReadFramebuffer(glFramebuffer);
|
|
for (var i = 0; i < this._currentRenderTargetCount; ++i)
|
|
{
|
|
renderTargetBinding = this._currentRenderTargetBindings[i];
|
|
renderTarget = renderTargetBinding.RenderTarget as IRenderTarget;
|
|
this.framebufferHelper.BlitFramebuffer(i, renderTarget.Width, renderTarget.Height);
|
|
}
|
|
if (renderTarget.RenderTargetUsage == RenderTargetUsage.DiscardContents && this.framebufferHelper.SupportsInvalidateFramebuffer)
|
|
this.framebufferHelper.InvalidateReadFramebuffer();
|
|
if (this._lastRasterizerState.ScissorTestEnable)
|
|
{
|
|
GL.Enable(EnableCap.ScissorTest);
|
|
GraphicsExtensions.CheckGLError();
|
|
}
|
|
}
|
|
for (var i = 0; i < this._currentRenderTargetCount; ++i)
|
|
{
|
|
renderTargetBinding = this._currentRenderTargetBindings[i];
|
|
renderTarget = renderTargetBinding.RenderTarget as IRenderTarget;
|
|
if (renderTarget.LevelCount > 1)
|
|
{
|
|
GL.BindTexture((TextureTarget)renderTarget.GLTarget, renderTarget.GLTexture);
|
|
GraphicsExtensions.CheckGLError();
|
|
this.framebufferHelper.GenerateMipmap((int)renderTarget.GLTarget);
|
|
}
|
|
}
|
|
}
|
|
|
|
private IRenderTarget PlatformApplyRenderTargets()
|
|
{
|
|
var glFramebuffer = 0;
|
|
if (!this.glFramebuffers.TryGetValue(this._currentRenderTargetBindings, out glFramebuffer))
|
|
{
|
|
this.framebufferHelper.GenFramebuffer(out glFramebuffer);
|
|
this.framebufferHelper.BindFramebuffer(glFramebuffer);
|
|
var renderTargetBinding = this._currentRenderTargetBindings[0];
|
|
var renderTarget = renderTargetBinding.RenderTarget as IRenderTarget;
|
|
this.framebufferHelper.FramebufferRenderbuffer((int)FramebufferAttachment.DepthAttachment, renderTarget.GLDepthBuffer, 0);
|
|
this.framebufferHelper.FramebufferRenderbuffer((int)FramebufferAttachment.StencilAttachment, renderTarget.GLStencilBuffer, 0);
|
|
for (var i = 0; i < this._currentRenderTargetCount; ++i)
|
|
{
|
|
renderTargetBinding = this._currentRenderTargetBindings[i];
|
|
renderTarget = renderTargetBinding.RenderTarget as IRenderTarget;
|
|
var attachement = (int)(FramebufferAttachment.ColorAttachment0 + i);
|
|
if (renderTarget.GLColorBuffer != renderTarget.GLTexture)
|
|
this.framebufferHelper.FramebufferRenderbuffer(attachement, renderTarget.GLColorBuffer, 0);
|
|
else
|
|
this.framebufferHelper.FramebufferTexture2D(attachement, (int)renderTarget.GetFramebufferTarget(renderTargetBinding), renderTarget.GLTexture, 0, renderTarget.MultiSampleCount);
|
|
}
|
|
|
|
#if DEBUG
|
|
this.framebufferHelper.CheckFramebufferStatus();
|
|
#endif
|
|
this.glFramebuffers.Add((RenderTargetBinding[])_currentRenderTargetBindings.Clone(), glFramebuffer);
|
|
}
|
|
else
|
|
{
|
|
this.framebufferHelper.BindFramebuffer(glFramebuffer);
|
|
}
|
|
#if !GLES
|
|
GL.DrawBuffers(this._currentRenderTargetCount, this._drawBuffers);
|
|
#endif
|
|
|
|
// Reset the raster state because we flip vertices
|
|
// when rendering offscreen and hence the cull direction.
|
|
_rasterizerStateDirty = true;
|
|
|
|
// Textures will need to be rebound to render correctly in the new render target.
|
|
Textures.Dirty();
|
|
|
|
return _currentRenderTargetBindings[0].RenderTarget as IRenderTarget;
|
|
}
|
|
|
|
private static GLPrimitiveType PrimitiveTypeGL(PrimitiveType primitiveType)
|
|
{
|
|
switch (primitiveType)
|
|
{
|
|
case PrimitiveType.LineList:
|
|
return GLPrimitiveType.Lines;
|
|
case PrimitiveType.LineStrip:
|
|
return GLPrimitiveType.LineStrip;
|
|
case PrimitiveType.TriangleList:
|
|
return GLPrimitiveType.Triangles;
|
|
case PrimitiveType.TriangleStrip:
|
|
return GLPrimitiveType.TriangleStrip;
|
|
}
|
|
|
|
throw new ArgumentException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Activates the Current Vertex/Pixel shader pair into a program.
|
|
/// </summary>
|
|
private unsafe void ActivateShaderProgram()
|
|
{
|
|
// Lookup the shader program.
|
|
var shaderProgram = _programCache.GetProgram(VertexShader, PixelShader);
|
|
if (shaderProgram.Program == -1)
|
|
return;
|
|
// Set the new program if it has changed.
|
|
if (_shaderProgram != shaderProgram)
|
|
{
|
|
GL.UseProgram(shaderProgram.Program);
|
|
GraphicsExtensions.CheckGLError();
|
|
_shaderProgram = shaderProgram;
|
|
}
|
|
|
|
var posFixupLoc = shaderProgram.GetUniformLocation("posFixup");
|
|
if (posFixupLoc == -1)
|
|
return;
|
|
|
|
// Apply vertex shader fix:
|
|
// The following two lines are appended to the end of vertex shaders
|
|
// to account for rendering differences between OpenGL and DirectX:
|
|
//
|
|
// gl_Position.y = gl_Position.y * posFixup.y;
|
|
// gl_Position.xy += posFixup.zw * gl_Position.ww;
|
|
//
|
|
// (the following paraphrased from wine, wined3d/state.c and wined3d/glsl_shader.c)
|
|
//
|
|
// - We need to flip along the y-axis in case of offscreen rendering.
|
|
// - D3D coordinates refer to pixel centers while GL coordinates refer
|
|
// to pixel corners.
|
|
// - D3D has a top-left filling convention. We need to maintain this
|
|
// even after the y-flip mentioned above.
|
|
// In order to handle the last two points, we translate by
|
|
// (63.0 / 128.0) / VPw and (63.0 / 128.0) / VPh. This is equivalent to
|
|
// translating slightly less than half a pixel. We want the difference to
|
|
// be large enough that it doesn't get lost due to rounding inside the
|
|
// driver, but small enough to prevent it from interfering with any
|
|
// anti-aliasing.
|
|
//
|
|
// OpenGL coordinates specify the center of the pixel while d3d coords specify
|
|
// the corner. The offsets are stored in z and w in posFixup. posFixup.y contains
|
|
// 1.0 or -1.0 to turn the rendering upside down for offscreen rendering. PosFixup.x
|
|
// contains 1.0 to allow a mad.
|
|
|
|
_posFixup[0] = 1.0f;
|
|
_posFixup[1] = 1.0f;
|
|
_posFixup[2] = (63.0f/64.0f)/Viewport.Width;
|
|
_posFixup[3] = -(63.0f/64.0f)/Viewport.Height;
|
|
|
|
//If we have a render target bound (rendering offscreen)
|
|
if (IsRenderTargetBound)
|
|
{
|
|
//flip vertically
|
|
_posFixup[1] *= -1.0f;
|
|
_posFixup[3] *= -1.0f;
|
|
}
|
|
|
|
fixed (float* floatPtr = _posFixup)
|
|
{
|
|
GL.Uniform4(posFixupLoc, 1, floatPtr);
|
|
}
|
|
GraphicsExtensions.CheckGLError();
|
|
}
|
|
|
|
internal void PlatformBeginApplyState()
|
|
{
|
|
Threading.EnsureUIThread();
|
|
}
|
|
|
|
private void PlatformApplyBlend(bool force = false)
|
|
{
|
|
_actualBlendState.PlatformApplyState(this, force);
|
|
ApplyBlendFactor(force);
|
|
}
|
|
|
|
private void ApplyBlendFactor(bool force)
|
|
{
|
|
if (force || BlendFactor != _lastBlendState.BlendFactor)
|
|
{
|
|
GL.BlendColor(
|
|
this.BlendFactor.R/255.0f,
|
|
this.BlendFactor.G/255.0f,
|
|
this.BlendFactor.B/255.0f,
|
|
this.BlendFactor.A/255.0f);
|
|
GraphicsExtensions.CheckGLError();
|
|
_lastBlendState.BlendFactor = this.BlendFactor;
|
|
}
|
|
}
|
|
|
|
internal void PlatformApplyState(bool applyShaders)
|
|
{
|
|
if ( _scissorRectangleDirty )
|
|
{
|
|
var scissorRect = _scissorRectangle;
|
|
if (!IsRenderTargetBound)
|
|
scissorRect.Y = PresentationParameters.BackBufferHeight - (scissorRect.Y + scissorRect.Height);
|
|
GL.Scissor(scissorRect.X, scissorRect.Y, scissorRect.Width, scissorRect.Height);
|
|
GraphicsExtensions.CheckGLError();
|
|
_scissorRectangleDirty = false;
|
|
}
|
|
|
|
// If we're not applying shaders then early out now.
|
|
if (!applyShaders)
|
|
return;
|
|
|
|
if (_indexBufferDirty)
|
|
{
|
|
if (_indexBuffer != null)
|
|
{
|
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBuffer.ibo);
|
|
GraphicsExtensions.CheckGLError();
|
|
}
|
|
_indexBufferDirty = false;
|
|
}
|
|
|
|
if (_vertexShader == null)
|
|
throw new InvalidOperationException("A vertex shader must be set!");
|
|
if (_pixelShader == null)
|
|
throw new InvalidOperationException("A pixel shader must be set!");
|
|
|
|
if (_vertexShaderDirty || _pixelShaderDirty)
|
|
{
|
|
ActivateShaderProgram();
|
|
|
|
if (_vertexShaderDirty)
|
|
{
|
|
unchecked
|
|
{
|
|
_graphicsMetrics._vertexShaderCount++;
|
|
}
|
|
}
|
|
|
|
if (_pixelShaderDirty)
|
|
{
|
|
unchecked
|
|
{
|
|
_graphicsMetrics._pixelShaderCount++;
|
|
}
|
|
}
|
|
|
|
_vertexShaderDirty = _pixelShaderDirty = false;
|
|
}
|
|
|
|
_vertexConstantBuffers.SetConstantBuffers(this, _shaderProgram);
|
|
_pixelConstantBuffers.SetConstantBuffers(this, _shaderProgram);
|
|
|
|
Textures.SetTextures(this);
|
|
SamplerStates.PlatformSetSamplers(this);
|
|
}
|
|
|
|
private void PlatformDrawIndexedPrimitives(PrimitiveType primitiveType, int baseVertex, int startIndex, int primitiveCount)
|
|
{
|
|
ApplyState(true);
|
|
|
|
var shortIndices = _indexBuffer.IndexElementSize == IndexElementSize.SixteenBits;
|
|
|
|
var indexElementType = shortIndices ? DrawElementsType.UnsignedShort : DrawElementsType.UnsignedInt;
|
|
var indexElementSize = shortIndices ? 2 : 4;
|
|
var indexOffsetInBytes = (IntPtr)(startIndex * indexElementSize);
|
|
var indexElementCount = GetElementCountArray(primitiveType, primitiveCount);
|
|
var target = PrimitiveTypeGL(primitiveType);
|
|
|
|
ApplyAttribs(_vertexShader, baseVertex);
|
|
|
|
GL.DrawElements(target,
|
|
indexElementCount,
|
|
indexElementType,
|
|
indexOffsetInBytes);
|
|
GraphicsExtensions.CheckGLError();
|
|
}
|
|
|
|
private void PlatformDrawUserPrimitives<T>(PrimitiveType primitiveType, T[] vertexData, int vertexOffset, VertexDeclaration vertexDeclaration, int vertexCount) where T : struct
|
|
{
|
|
ApplyState(true);
|
|
|
|
// Unbind current VBOs.
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
|
GraphicsExtensions.CheckGLError();
|
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
|
|
GraphicsExtensions.CheckGLError();
|
|
_indexBufferDirty = true;
|
|
|
|
// Pin the buffers.
|
|
var vbHandle = GCHandle.Alloc(vertexData, GCHandleType.Pinned);
|
|
|
|
// Setup the vertex declaration to point at the VB data.
|
|
vertexDeclaration.GraphicsDevice = this;
|
|
vertexDeclaration.Apply(_vertexShader, vbHandle.AddrOfPinnedObject(), ShaderProgramHash);
|
|
|
|
//Draw
|
|
GL.DrawArrays(PrimitiveTypeGL(primitiveType),
|
|
vertexOffset,
|
|
vertexCount);
|
|
GraphicsExtensions.CheckGLError();
|
|
|
|
// Release the handles.
|
|
vbHandle.Free();
|
|
}
|
|
|
|
private void PlatformDrawPrimitives(PrimitiveType primitiveType, int vertexStart, int vertexCount)
|
|
{
|
|
ApplyState(true);
|
|
|
|
ApplyAttribs(_vertexShader, 0);
|
|
|
|
if (vertexStart < 0)
|
|
vertexStart = 0;
|
|
|
|
GL.DrawArrays(PrimitiveTypeGL(primitiveType),
|
|
vertexStart,
|
|
vertexCount);
|
|
GraphicsExtensions.CheckGLError();
|
|
}
|
|
|
|
private void PlatformDrawUserIndexedPrimitives<T>(PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int numVertices, short[] indexData, int indexOffset, int primitiveCount, VertexDeclaration vertexDeclaration) where T : struct
|
|
{
|
|
ApplyState(true);
|
|
|
|
// Unbind current VBOs.
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
|
GraphicsExtensions.CheckGLError();
|
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
|
|
GraphicsExtensions.CheckGLError();
|
|
_indexBufferDirty = true;
|
|
|
|
// Pin the buffers.
|
|
var vbHandle = GCHandle.Alloc(vertexData, GCHandleType.Pinned);
|
|
var ibHandle = GCHandle.Alloc(indexData, GCHandleType.Pinned);
|
|
|
|
var vertexAddr = (IntPtr)(vbHandle.AddrOfPinnedObject().ToInt64() + vertexDeclaration.VertexStride * vertexOffset);
|
|
|
|
// Setup the vertex declaration to point at the VB data.
|
|
vertexDeclaration.GraphicsDevice = this;
|
|
vertexDeclaration.Apply(_vertexShader, vertexAddr, ShaderProgramHash);
|
|
|
|
//Draw
|
|
GL.DrawElements( PrimitiveTypeGL(primitiveType),
|
|
GetElementCountArray(primitiveType, primitiveCount),
|
|
DrawElementsType.UnsignedShort,
|
|
(IntPtr)(ibHandle.AddrOfPinnedObject().ToInt64() + (indexOffset * sizeof(short))));
|
|
GraphicsExtensions.CheckGLError();
|
|
|
|
// Release the handles.
|
|
ibHandle.Free();
|
|
vbHandle.Free();
|
|
}
|
|
|
|
private void PlatformDrawUserIndexedPrimitives<T>(PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int numVertices, int[] indexData, int indexOffset, int primitiveCount, VertexDeclaration vertexDeclaration) where T : struct
|
|
{
|
|
ApplyState(true);
|
|
|
|
// Unbind current VBOs.
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
|
GraphicsExtensions.CheckGLError();
|
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
|
|
GraphicsExtensions.CheckGLError();
|
|
_indexBufferDirty = true;
|
|
|
|
// Pin the buffers.
|
|
var vbHandle = GCHandle.Alloc(vertexData, GCHandleType.Pinned);
|
|
var ibHandle = GCHandle.Alloc(indexData, GCHandleType.Pinned);
|
|
|
|
var vertexAddr = (IntPtr)(vbHandle.AddrOfPinnedObject().ToInt64() + vertexDeclaration.VertexStride * vertexOffset);
|
|
|
|
// Setup the vertex declaration to point at the VB data.
|
|
vertexDeclaration.GraphicsDevice = this;
|
|
vertexDeclaration.Apply(_vertexShader, vertexAddr, ShaderProgramHash);
|
|
|
|
//Draw
|
|
GL.DrawElements( PrimitiveTypeGL(primitiveType),
|
|
GetElementCountArray(primitiveType, primitiveCount),
|
|
DrawElementsType.UnsignedInt,
|
|
(IntPtr)(ibHandle.AddrOfPinnedObject().ToInt64() + (indexOffset * sizeof(int))));
|
|
GraphicsExtensions.CheckGLError();
|
|
|
|
// Release the handles.
|
|
ibHandle.Free();
|
|
vbHandle.Free();
|
|
}
|
|
|
|
private void PlatformDrawInstancedPrimitives(PrimitiveType primitiveType, int baseVertex, int startIndex, int primitiveCount, int instanceCount)
|
|
{
|
|
if (!GraphicsCapabilities.SupportsInstancing)
|
|
throw new PlatformNotSupportedException("Instanced geometry drawing requires at least OpenGL 3.2 or GLES 3.2. Try upgrading your graphics card drivers.");
|
|
ApplyState(true);
|
|
|
|
var shortIndices = _indexBuffer.IndexElementSize == IndexElementSize.SixteenBits;
|
|
|
|
var indexElementType = shortIndices ? DrawElementsType.UnsignedShort : DrawElementsType.UnsignedInt;
|
|
var indexElementSize = shortIndices ? 2 : 4;
|
|
var indexOffsetInBytes = (IntPtr)(startIndex * indexElementSize);
|
|
var indexElementCount = GetElementCountArray(primitiveType, primitiveCount);
|
|
var target = PrimitiveTypeGL(primitiveType);
|
|
|
|
ApplyAttribs(_vertexShader, baseVertex);
|
|
|
|
GL.DrawElementsInstanced(target,
|
|
indexElementCount,
|
|
indexElementType,
|
|
indexOffsetInBytes,
|
|
instanceCount);
|
|
GraphicsExtensions.CheckGLError();
|
|
}
|
|
|
|
private void PlatformGetBackBufferData<T>(Rectangle? rectangle, T[] data, int startIndex, int count) where T : struct
|
|
{
|
|
var rect = rectangle ?? new Rectangle(0, 0, PresentationParameters.BackBufferWidth, PresentationParameters.BackBufferHeight);
|
|
var tSize = Marshal.SizeOf(typeof(T));
|
|
var flippedY = PresentationParameters.BackBufferHeight - rect.Y - rect.Height;
|
|
GL.ReadPixels(rect.X, flippedY, rect.Width, rect.Height, PixelFormat.Rgba, PixelType.UnsignedByte, data);
|
|
|
|
// buffer is returned upside down, so we swap the rows around when copying over
|
|
var rowSize = rect.Width*PresentationParameters.BackBufferFormat.GetSize() / tSize;
|
|
var row = new T[rowSize];
|
|
for (var dy = 0; dy < rect.Height/2; dy++)
|
|
{
|
|
var topRow = startIndex + dy*rowSize;
|
|
var bottomRow = startIndex + (rect.Height - dy - 1)*rowSize;
|
|
// copy the bottom row to buffer
|
|
Array.Copy(data, bottomRow, row, 0, rowSize);
|
|
// copy top row to bottom row
|
|
Array.Copy(data, topRow, data, bottomRow, rowSize);
|
|
// copy buffer to top row
|
|
Array.Copy(row, 0, data, topRow, rowSize);
|
|
count -= rowSize;
|
|
}
|
|
}
|
|
|
|
private static Rectangle PlatformGetTitleSafeArea(int x, int y, int width, int height)
|
|
{
|
|
return new Rectangle(x, y, width, height);
|
|
}
|
|
|
|
internal void PlatformSetMultiSamplingToMaximum(PresentationParameters presentationParameters, out int quality)
|
|
{
|
|
presentationParameters.MultiSampleCount = 4;
|
|
quality = 0;
|
|
}
|
|
|
|
internal void OnPresentationChanged()
|
|
{
|
|
#if DESKTOPGL || ANGLE
|
|
Context.MakeCurrent(new WindowInfo(SdlGameWindow.Instance.Handle));
|
|
Context.SwapInterval = PresentationParameters.PresentationInterval.GetSwapInterval();
|
|
#endif
|
|
|
|
ApplyRenderTargets(null);
|
|
}
|
|
|
|
// Holds information for caching
|
|
private class BufferBindingInfo
|
|
{
|
|
public VertexDeclaration.VertexDeclarationAttributeInfo AttributeInfo;
|
|
public IntPtr VertexOffset;
|
|
public int InstanceFrequency;
|
|
public int Vbo;
|
|
|
|
public BufferBindingInfo(VertexDeclaration.VertexDeclarationAttributeInfo attributeInfo, IntPtr vertexOffset, int instanceFrequency, int vbo)
|
|
{
|
|
AttributeInfo = attributeInfo;
|
|
VertexOffset = vertexOffset;
|
|
InstanceFrequency = instanceFrequency;
|
|
Vbo = vbo;
|
|
}
|
|
}
|
|
|
|
#if DESKTOPGL
|
|
private void GetModeSwitchedSize(out int width, out int height)
|
|
{
|
|
var mode = new Sdl.Display.Mode
|
|
{
|
|
Width = PresentationParameters.BackBufferWidth,
|
|
Height = PresentationParameters.BackBufferHeight,
|
|
Format = 0,
|
|
RefreshRate = 0,
|
|
DriverData = IntPtr.Zero
|
|
};
|
|
Sdl.Display.Mode closest;
|
|
Sdl.Display.GetClosestDisplayMode(0, mode, out closest);
|
|
width = closest.Width;
|
|
height = closest.Height;
|
|
}
|
|
|
|
private void GetDisplayResolution(out int width, out int height)
|
|
{
|
|
Sdl.Display.Mode mode;
|
|
Sdl.Display.GetCurrentDisplayMode(0, out mode);
|
|
width = mode.Width;
|
|
height = mode.Height;
|
|
}
|
|
#endif
|
|
}
|
|
}
|