Permalink
Fetching contributors…
Cannot retrieve contributors at this time
4630 lines (4165 sloc) 111 KB
#region License
/* FNA - XNA4 Reimplementation for Desktop Platforms
* Copyright 2009-2017 Ethan Lee and the MonoGame Team
*
* Released under the Microsoft Public License.
* See LICENSE for details.
*/
#endregion
#region THREADED_GL Option
// #define THREADED_GL
/* Ah, so I see you've run into some issues with threaded GL...
*
* This class is designed to handle rendering coming from multiple threads, but
* if you're too wreckless with how many threads are calling the GL, this will
* hang.
*
* With THREADED_GL we instead allow you to run threaded rendering using
* multiple GL contexts. This is more flexible, but much more dangerous.
*
* Basically, if you have to enable this, you should feel very bad.
* -flibit
*/
#endregion
#region DISABLE_THREADING Option
// #define DISABLE_THREADING
/* Perhaps you read the above option and thought to yourself:
* "Wow, only an idiot would need threads for their graphics code!"
*
* For those of you who are particularly well-behaved with your renderer and
* don't ever call anything on a thread at all, you can enable this define and
* cut out a _ton_ of garbage generation that's caused by our attempt to force
* things to the main thread.
*
* Enjoy the boost, you've earned it.
* -flibit
*/
#endregion
#region Using Statements
using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Threading;
using SDL2;
#endregion
namespace Microsoft.Xna.Framework.Graphics
{
internal partial class OpenGLDevice : IGLDevice
{
#region OpenGL Texture Container Class
private class OpenGLTexture : IGLTexture
{
public uint Handle
{
get;
private set;
}
public GLenum Target
{
get;
private set;
}
public bool HasMipmaps
{
get;
private set;
}
public TextureAddressMode WrapS;
public TextureAddressMode WrapT;
public TextureAddressMode WrapR;
public TextureFilter Filter;
public float Anistropy;
public int MaxMipmapLevel;
public float LODBias;
public OpenGLTexture(
uint handle,
GLenum target,
int levelCount
) {
Handle = handle;
Target = target;
HasMipmaps = levelCount > 1;
WrapS = TextureAddressMode.Wrap;
WrapT = TextureAddressMode.Wrap;
WrapR = TextureAddressMode.Wrap;
Filter = TextureFilter.Linear;
Anistropy = 4.0f;
MaxMipmapLevel = 0;
LODBias = 0.0f;
}
// We can't set a SamplerState Texture to null, so use this.
private OpenGLTexture()
{
Handle = 0;
Target = GLenum.GL_TEXTURE_2D; // FIXME: Assumption! -flibit
}
public static readonly OpenGLTexture NullTexture = new OpenGLTexture();
}
#endregion
#region OpenGL Renderbuffer Container Class
private class OpenGLRenderbuffer : IGLRenderbuffer
{
public uint Handle
{
get;
private set;
}
public OpenGLRenderbuffer(uint handle)
{
Handle = handle;
}
}
#endregion
#region OpenGL Buffer Container Class
private class OpenGLBuffer : IGLBuffer
{
public uint Handle
{
get;
private set;
}
public IntPtr BufferSize
{
get;
private set;
}
public GLenum Dynamic
{
get;
private set;
}
public OpenGLBuffer(
uint handle,
IntPtr bufferSize,
GLenum dynamic
) {
Handle = handle;
BufferSize = bufferSize;
Dynamic = dynamic;
}
private OpenGLBuffer()
{
Handle = 0;
}
public static readonly OpenGLBuffer NullBuffer = new OpenGLBuffer();
}
#endregion
#region OpenGL Effect Container Class
private class OpenGLEffect : IGLEffect
{
public IntPtr EffectData
{
get;
private set;
}
public IntPtr GLEffectData
{
get;
private set;
}
public OpenGLEffect(IntPtr effect, IntPtr glEffect)
{
EffectData = effect;
GLEffectData = glEffect;
}
}
#endregion
#region OpenGL Query Container Class
private class OpenGLQuery : IGLQuery
{
public uint Handle
{
get;
private set;
}
public OpenGLQuery(uint handle)
{
Handle = handle;
}
}
#endregion
#region Blending State Variables
public Color BlendFactor
{
get
{
return blendColor;
}
set
{
if (value != blendColor)
{
blendColor = value;
glBlendColor(
blendColor.R / 255.0f,
blendColor.G / 255.0f,
blendColor.B / 255.0f,
blendColor.A / 255.0f
);
}
}
}
public int MultiSampleMask
{
get
{
return multisampleMask;
}
set
{
if (value != multisampleMask && supportsMultisampling)
{
if (value == -1)
{
glDisable(GLenum.GL_SAMPLE_MASK);
}
else
{
if (multisampleMask == -1)
{
glEnable(GLenum.GL_SAMPLE_MASK);
}
// FIXME: index...? -flibit
glSampleMaski(0, (uint) value);
}
multisampleMask = value;
}
}
}
private bool alphaBlendEnable = false;
private Color blendColor = Color.Transparent;
private BlendFunction blendOp = BlendFunction.Add;
private BlendFunction blendOpAlpha = BlendFunction.Add;
private Blend srcBlend = Blend.One;
private Blend dstBlend = Blend.Zero;
private Blend srcBlendAlpha = Blend.One;
private Blend dstBlendAlpha = Blend.Zero;
private ColorWriteChannels colorWriteEnable = ColorWriteChannels.All;
private ColorWriteChannels colorWriteEnable1 = ColorWriteChannels.All;
private ColorWriteChannels colorWriteEnable2 = ColorWriteChannels.All;
private ColorWriteChannels colorWriteEnable3 = ColorWriteChannels.All;
private int multisampleMask = -1; // AKA 0xFFFFFFFF
#endregion
#region Depth State Variables
private bool zEnable = false;
private bool zWriteEnable = false;
private CompareFunction depthFunc = CompareFunction.Less;
#endregion
#region Stencil State Variables
public int ReferenceStencil
{
get
{
return stencilRef;
}
set
{
if (value != stencilRef)
{
stencilRef = value;
if (separateStencilEnable)
{
glStencilFuncSeparate(
GLenum.GL_FRONT,
XNAToGL.CompareFunc[(int) stencilFunc],
stencilRef,
stencilMask
);
glStencilFuncSeparate(
GLenum.GL_BACK,
XNAToGL.CompareFunc[(int) ccwStencilFunc],
stencilRef,
stencilMask
);
}
else
{
glStencilFunc(
XNAToGL.CompareFunc[(int) stencilFunc],
stencilRef,
stencilMask
);
}
}
}
}
private bool stencilEnable = false;
private int stencilWriteMask = -1; // AKA 0xFFFFFFFF, ugh -flibit
private bool separateStencilEnable = false;
private int stencilRef = 0;
private int stencilMask = -1; // AKA 0xFFFFFFFF, ugh -flibit
private CompareFunction stencilFunc = CompareFunction.Always;
private StencilOperation stencilFail = StencilOperation.Keep;
private StencilOperation stencilZFail = StencilOperation.Keep;
private StencilOperation stencilPass = StencilOperation.Keep;
private CompareFunction ccwStencilFunc = CompareFunction.Always;
private StencilOperation ccwStencilFail = StencilOperation.Keep;
private StencilOperation ccwStencilZFail = StencilOperation.Keep;
private StencilOperation ccwStencilPass = StencilOperation.Keep;
#endregion
#region Rasterizer State Variables
private bool scissorTestEnable = false;
private CullMode cullFrontFace = CullMode.None;
private FillMode fillMode = FillMode.Solid;
private float depthBias = 0.0f;
private float slopeScaleDepthBias = 0.0f;
private bool multiSampleEnable = true;
#endregion
#region Viewport State Variables
/* These two aren't actually empty rects by default in OpenGL,
* but we don't _really_ know the starting window size, so
* force apply this when the GraphicsDevice is initialized.
* -flibit
*/
private Rectangle scissorRectangle = new Rectangle(
0,
0,
0,
0
);
private Rectangle viewport = new Rectangle(
0,
0,
0,
0
);
private float depthRangeMin = 0.0f;
private float depthRangeMax = 1.0f;
#endregion
#region Texture Collection Variables
private OpenGLTexture[] Textures;
#endregion
#region Buffer Binding Cache Variables
private uint currentVertexBuffer = 0;
private uint currentIndexBuffer = 0;
// ld, or LastDrawn, effect/vertex attributes
private int ldBaseVertex = -1;
private VertexDeclaration ldVertexDeclaration = null;
private IntPtr ldPointer = IntPtr.Zero;
private IntPtr ldEffect = IntPtr.Zero;
private IntPtr ldTechnique = IntPtr.Zero;
private uint ldPass = 0;
#endregion
#region Render Target Cache Variables
private uint currentReadFramebuffer = 0;
private uint currentDrawFramebuffer = 0;
private uint targetFramebuffer = 0;
private uint resolveFramebufferRead = 0;
private uint resolveFramebufferDraw = 0;
private readonly uint[] currentAttachments;
private readonly GLenum[] currentAttachmentTypes;
private int currentDrawBuffers;
private readonly GLenum[] drawBuffersArray;
private uint currentRenderbuffer;
private DepthFormat currentDepthStencilFormat;
private readonly uint[] attachments;
private readonly GLenum[] attachmentTypes;
#endregion
#region Clear Cache Variables
private Vector4 currentClearColor = new Vector4(0, 0, 0, 0);
private float currentClearDepth = 1.0f;
private int currentClearStencil = 0;
#endregion
#region Private OpenGL Context Variable
private IntPtr glContext;
#endregion
#region Faux-Backbuffer Variables
public IGLBackbuffer Backbuffer
{
get;
private set;
}
private GLenum backbufferScaleMode;
#endregion
#region OpenGL Device Capabilities
public bool SupportsDxt1
{
get;
private set;
}
public bool SupportsS3tc
{
get;
private set;
}
public bool SupportsHardwareInstancing
{
get;
private set;
}
public int MaxTextureSlots
{
get;
private set;
}
public int MaxMultiSampleCount
{
get;
private set;
}
private bool supportsMultisampling;
private bool supportsFauxBackbuffer;
private bool supportsBaseVertex;
#endregion
#region Private Vertex Attribute Cache
private class VertexAttribute
{
public uint CurrentBuffer;
public IntPtr CurrentPointer;
public VertexElementFormat CurrentFormat;
public bool CurrentNormalized;
public int CurrentStride;
public VertexAttribute()
{
CurrentBuffer = 0;
CurrentPointer = IntPtr.Zero;
CurrentFormat = VertexElementFormat.Single;
CurrentNormalized = false;
CurrentStride = 0;
}
}
private VertexAttribute[] attributes;
private bool[] attributeEnabled;
private bool[] previousAttributeEnabled;
private int[] attributeDivisor;
private int[] previousAttributeDivisor;
#endregion
#region Private MojoShader Interop
private string shaderProfile;
private IntPtr shaderContext;
private IntPtr currentEffect = IntPtr.Zero;
private IntPtr currentTechnique = IntPtr.Zero;
private uint currentPass = 0;
private int flipViewport = 1;
private bool effectApplied = false;
private static IntPtr glGetProcAddress(string name, IntPtr d)
{
return SDL.SDL_GL_GetProcAddress(name);
}
private static MojoShader.MOJOSHADER_glGetProcAddress GLGetProcAddress = glGetProcAddress;
#endregion
#region Private Graphics Object Disposal Queues
private ConcurrentQueue<IGLTexture> GCTextures = new ConcurrentQueue<IGLTexture>();
private ConcurrentQueue<IGLRenderbuffer> GCDepthBuffers = new ConcurrentQueue<IGLRenderbuffer>();
private ConcurrentQueue<IGLBuffer> GCVertexBuffers = new ConcurrentQueue<IGLBuffer>();
private ConcurrentQueue<IGLBuffer> GCIndexBuffers = new ConcurrentQueue<IGLBuffer>();
private ConcurrentQueue<IGLEffect> GCEffects = new ConcurrentQueue<IGLEffect>();
private ConcurrentQueue<IGLQuery> GCQueries = new ConcurrentQueue<IGLQuery>();
#endregion
#region Private Profile-specific Variables
private bool useES3;
private bool useCoreProfile;
private uint vao;
#endregion
#region memcpy Export
/* This is used a lot for GetData/Read calls... -flibit */
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void memcpy(IntPtr dst, IntPtr src, IntPtr len);
#endregion
#region Public Constructor
public OpenGLDevice(
PresentationParameters presentationParameters,
GraphicsAdapter adapter
) {
// Create OpenGL context
glContext = SDL.SDL_GL_CreateContext(
presentationParameters.DeviceWindowHandle
);
// Check for a possible ES context
int flags;
int es3Flag = (int) SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_ES;
SDL.SDL_GL_GetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, out flags);
useES3 = (flags & es3Flag) == es3Flag;
// Check for a possible Core context
int coreFlag = (int) SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE;
useCoreProfile = (flags & coreFlag) == coreFlag;
// Init threaded GL crap where applicable
InitThreadedGL(
presentationParameters.DeviceWindowHandle
);
// Initialize entry points
LoadGLEntryPoints();
shaderProfile = MojoShader.MOJOSHADER_glBestProfile(
GLGetProcAddress,
IntPtr.Zero,
null,
null,
IntPtr.Zero
);
shaderContext = MojoShader.MOJOSHADER_glCreateContext(
shaderProfile,
GLGetProcAddress,
IntPtr.Zero,
null,
null,
IntPtr.Zero
);
MojoShader.MOJOSHADER_glMakeContextCurrent(shaderContext);
// Some users might want pixely upscaling...
backbufferScaleMode = Environment.GetEnvironmentVariable(
"FNA_OPENGL_BACKBUFFER_SCALE_NEAREST"
) == "1" ? GLenum.GL_NEAREST : GLenum.GL_LINEAR;
// Print GL information
FNALoggerEXT.LogInfo("IGLDevice: OpenGLDevice");
FNALoggerEXT.LogInfo("OpenGL Device: " + glGetString(GLenum.GL_RENDERER));
FNALoggerEXT.LogInfo("OpenGL Driver: " + glGetString(GLenum.GL_VERSION));
FNALoggerEXT.LogInfo("OpenGL Vendor: " + glGetString(GLenum.GL_VENDOR));
FNALoggerEXT.LogInfo("MojoShader Profile: " + shaderProfile);
// Load the extension list, initialize extension-dependent components
string extensions;
if (useCoreProfile)
{
extensions = string.Empty;
int numExtensions;
glGetIntegerv(GLenum.GL_NUM_EXTENSIONS, out numExtensions);
for (uint i = 0; i < numExtensions; i += 1)
{
extensions += glGetStringi(GLenum.GL_EXTENSIONS, i) + " ";
}
}
else
{
extensions = glGetString(GLenum.GL_EXTENSIONS);
}
SupportsS3tc = (
extensions.Contains("GL_EXT_texture_compression_s3tc") ||
extensions.Contains("GL_OES_texture_compression_S3TC") ||
extensions.Contains("GL_EXT_texture_compression_dxt3") ||
extensions.Contains("GL_EXT_texture_compression_dxt5")
);
SupportsDxt1 = (
SupportsS3tc ||
extensions.Contains("GL_EXT_texture_compression_dxt1")
);
/* Check the max multisample count, override parameters if necessary */
int maxSamples = 0;
if (supportsMultisampling)
{
glGetIntegerv(GLenum.GL_MAX_SAMPLES, out maxSamples);
}
MaxMultiSampleCount = maxSamples;
presentationParameters.MultiSampleCount = Math.Min(
presentationParameters.MultiSampleCount,
MaxMultiSampleCount
);
// Initialize the faux-backbuffer
if (UseFauxBackbuffer(presentationParameters, adapter.CurrentDisplayMode))
{
if (!supportsFauxBackbuffer)
{
throw new NoSuitableGraphicsDeviceException(
"Your hardware does not support the faux-backbuffer!" +
"\n\nKeep the window/backbuffer resolution the same."
);
}
Backbuffer = new OpenGLBackbuffer(
this,
presentationParameters.BackBufferWidth,
presentationParameters.BackBufferHeight,
presentationParameters.DepthStencilFormat,
presentationParameters.MultiSampleCount
);
}
else
{
Backbuffer = new NullBackbuffer(
presentationParameters.BackBufferWidth,
presentationParameters.BackBufferHeight
);
}
// Initialize texture collection array
int numSamplers;
glGetIntegerv(GLenum.GL_MAX_TEXTURE_IMAGE_UNITS, out numSamplers);
numSamplers = Math.Min(
numSamplers,
GraphicsDevice.MAX_TEXTURE_SAMPLERS + GraphicsDevice.MAX_VERTEXTEXTURE_SAMPLERS
);
Textures = new OpenGLTexture[numSamplers];
for (int i = 0; i < numSamplers; i += 1)
{
Textures[i] = OpenGLTexture.NullTexture;
}
MaxTextureSlots = numSamplers;
// Initialize vertex attribute state arrays
int numAttributes;
glGetIntegerv(GLenum.GL_MAX_VERTEX_ATTRIBS, out numAttributes);
numAttributes = Math.Min(
numAttributes,
GraphicsDevice.MAX_VERTEX_ATTRIBUTES
);
attributes = new VertexAttribute[numAttributes];
attributeEnabled = new bool[numAttributes];
previousAttributeEnabled = new bool[numAttributes];
attributeDivisor = new int[numAttributes];
previousAttributeDivisor = new int[numAttributes];
for (int i = 0; i < numAttributes; i += 1)
{
attributes[i] = new VertexAttribute();
attributeEnabled[i] = false;
previousAttributeEnabled[i] = false;
attributeDivisor[i] = 0;
previousAttributeDivisor[i] = 0;
}
// Initialize render target FBO and state arrays
int numAttachments;
glGetIntegerv(GLenum.GL_MAX_DRAW_BUFFERS, out numAttachments);
numAttachments = Math.Min(
numAttachments,
GraphicsDevice.MAX_RENDERTARGET_BINDINGS
);
attachments = new uint[numAttachments];
attachmentTypes = new GLenum[numAttachments];
currentAttachments = new uint[numAttachments];
currentAttachmentTypes = new GLenum[numAttachments];
drawBuffersArray = new GLenum[numAttachments];
for (int i = 0; i < numAttachments; i += 1)
{
currentAttachments[i] = 0;
currentAttachmentTypes[i] = GLenum.GL_TEXTURE_2D;
drawBuffersArray[i] = GLenum.GL_COLOR_ATTACHMENT0 + i;
}
currentDrawBuffers = 0;
currentRenderbuffer = 0;
currentDepthStencilFormat = DepthFormat.None;
glGenFramebuffers(1, out targetFramebuffer);
glGenFramebuffers(1, out resolveFramebufferRead);
glGenFramebuffers(1, out resolveFramebufferDraw);
// Generate and bind a VAO, to shut Core up
if (useCoreProfile)
{
glGenVertexArrays(1, out vao);
glBindVertexArray(vao);
}
}
#endregion
#region Dispose Method
public void Dispose()
{
if (useCoreProfile)
{
glBindVertexArray(0);
glDeleteVertexArrays(1, ref vao);
}
glDeleteFramebuffers(1, ref resolveFramebufferRead);
resolveFramebufferRead = 0;
glDeleteFramebuffers(1, ref resolveFramebufferDraw);
resolveFramebufferDraw = 0;
glDeleteFramebuffers(1, ref targetFramebuffer);
targetFramebuffer = 0;
if (Backbuffer is OpenGLBackbuffer)
{
(Backbuffer as OpenGLBackbuffer).Dispose();
}
Backbuffer = null;
MojoShader.MOJOSHADER_glMakeContextCurrent(IntPtr.Zero);
MojoShader.MOJOSHADER_glDestroyContext(shaderContext);
#if THREADED_GL
SDL.SDL_GL_DeleteContext(BackgroundContext.context);
#endif
SDL.SDL_GL_DeleteContext(glContext);
}
#endregion
#region Window Backbuffer Reset Method
public void ResetBackbuffer(
PresentationParameters presentationParameters,
GraphicsAdapter adapter,
bool renderTargetBound
) {
if (UseFauxBackbuffer(presentationParameters, adapter.CurrentDisplayMode))
{
if (Backbuffer is NullBackbuffer)
{
if (!supportsFauxBackbuffer)
{
throw new NoSuitableGraphicsDeviceException(
"Your hardware does not support the faux-backbuffer!" +
"\n\nKeep the window/backbuffer resolution the same."
);
}
Backbuffer = new OpenGLBackbuffer(
this,
presentationParameters.BackBufferWidth,
presentationParameters.BackBufferHeight,
presentationParameters.DepthStencilFormat,
presentationParameters.MultiSampleCount
);
}
else
{
Backbuffer.ResetFramebuffer(
presentationParameters,
renderTargetBound
);
}
}
else
{
if (Backbuffer is OpenGLBackbuffer)
{
(Backbuffer as OpenGLBackbuffer).Dispose();
Backbuffer = new NullBackbuffer(
presentationParameters.BackBufferWidth,
presentationParameters.BackBufferHeight
);
}
else
{
Backbuffer.ResetFramebuffer(
presentationParameters,
renderTargetBound
);
}
}
}
#endregion
#region Window SwapBuffers Method
public void SwapBuffers(
Rectangle? sourceRectangle,
Rectangle? destinationRectangle,
IntPtr overrideWindowHandle
) {
/* Only the faux-backbuffer supports presenting
* specific regions given to Present().
* -flibit
*/
if (Backbuffer is OpenGLBackbuffer)
{
int srcX, srcY, srcW, srcH;
int dstX, dstY, dstW, dstH;
if (sourceRectangle.HasValue)
{
srcX = sourceRectangle.Value.X;
srcY = sourceRectangle.Value.Y;
srcW = sourceRectangle.Value.Width;
srcH = sourceRectangle.Value.Height;
}
else
{
srcX = 0;
srcY = 0;
srcW = Backbuffer.Width;
srcH = Backbuffer.Height;
}
if (destinationRectangle.HasValue)
{
dstX = destinationRectangle.Value.X;
dstY = destinationRectangle.Value.Y;
dstW = destinationRectangle.Value.Width;
dstH = destinationRectangle.Value.Height;
}
else
{
dstX = 0;
dstY = 0;
SDL.SDL_GL_GetDrawableSize(
overrideWindowHandle,
out dstW,
out dstH
);
}
if (scissorTestEnable)
{
glDisable(GLenum.GL_SCISSOR_TEST);
}
if ( Backbuffer.MultiSampleCount > 0 &&
(srcX != dstX || srcY != dstY || srcW != dstW || srcH != dstH) )
{
/* We have to resolve the renderbuffer to a texture first.
* For whatever reason, we can't blit a multisample renderbuffer
* to the backbuffer. Not sure why, but oh well.
* -flibit
*/
OpenGLBackbuffer glBack = Backbuffer as OpenGLBackbuffer;
if (glBack.Texture == 0)
{
glGenTextures(1, out glBack.Texture);
glBindTexture(GLenum.GL_TEXTURE_2D, glBack.Texture);
glTexImage2D(
GLenum.GL_TEXTURE_2D,
0,
(int) GLenum.GL_RGBA,
glBack.Width,
glBack.Height,
0,
GLenum.GL_RGBA,
GLenum.GL_UNSIGNED_BYTE,
IntPtr.Zero
);
glBindTexture(Textures[0].Target, Textures[0].Handle);
}
BindFramebuffer(resolveFramebufferDraw);
glFramebufferTexture2D(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0,
GLenum.GL_TEXTURE_2D,
glBack.Texture,
0
);
BindReadFramebuffer(glBack.Handle);
glBlitFramebuffer(
0, 0, glBack.Width, glBack.Height,
0, 0, glBack.Width, glBack.Height,
GLenum.GL_COLOR_BUFFER_BIT,
GLenum.GL_LINEAR
);
BindReadFramebuffer(resolveFramebufferDraw);
}
else
{
BindReadFramebuffer((Backbuffer as OpenGLBackbuffer).Handle);
}
BindDrawFramebuffer(0);
glBlitFramebuffer(
srcX, srcY, srcW, srcH,
dstX, dstY, dstW, dstH,
GLenum.GL_COLOR_BUFFER_BIT,
backbufferScaleMode
);
BindFramebuffer(0);
if (scissorTestEnable)
{
glEnable(GLenum.GL_SCISSOR_TEST);
}
SDL.SDL_GL_SwapWindow(
overrideWindowHandle
);
BindFramebuffer((Backbuffer as OpenGLBackbuffer).Handle);
}
else
{
// Nothing left to do, just swap!
SDL.SDL_GL_SwapWindow(
overrideWindowHandle
);
}
#if !DISABLE_THREADING && !THREADED_GL
RunActions();
#endif
IGLTexture gcTexture;
while (GCTextures.TryDequeue(out gcTexture))
{
DeleteTexture(gcTexture);
}
IGLRenderbuffer gcDepthBuffer;
while (GCDepthBuffers.TryDequeue(out gcDepthBuffer))
{
DeleteRenderbuffer(gcDepthBuffer);
}
IGLBuffer gcBuffer;
while (GCVertexBuffers.TryDequeue(out gcBuffer))
{
DeleteVertexBuffer(gcBuffer);
}
while (GCIndexBuffers.TryDequeue(out gcBuffer))
{
DeleteIndexBuffer(gcBuffer);
}
IGLEffect gcEffect;
while (GCEffects.TryDequeue(out gcEffect))
{
DeleteEffect(gcEffect);
}
IGLQuery gcQuery;
while (GCQueries.TryDequeue(out gcQuery))
{
DeleteQuery(gcQuery);
}
}
#endregion
#region GL Object Disposal Wrappers
public void AddDisposeTexture(IGLTexture texture)
{
if (IsOnMainThread())
{
DeleteTexture(texture);
}
else
{
GCTextures.Enqueue(texture);
}
}
public void AddDisposeRenderbuffer(IGLRenderbuffer renderbuffer)
{
if (IsOnMainThread())
{
DeleteRenderbuffer(renderbuffer);
}
else
{
GCDepthBuffers.Enqueue(renderbuffer);
}
}
public void AddDisposeVertexBuffer(IGLBuffer buffer)
{
if (IsOnMainThread())
{
DeleteVertexBuffer(buffer);
}
else
{
GCVertexBuffers.Enqueue(buffer);
}
}
public void AddDisposeIndexBuffer(IGLBuffer buffer)
{
if (IsOnMainThread())
{
DeleteIndexBuffer(buffer);
}
else
{
GCIndexBuffers.Enqueue(buffer);
}
}
public void AddDisposeEffect(IGLEffect effect)
{
if (IsOnMainThread())
{
DeleteEffect(effect);
}
else
{
GCEffects.Enqueue(effect);
}
}
public void AddDisposeQuery(IGLQuery query)
{
if (IsOnMainThread())
{
DeleteQuery(query);
}
else
{
GCQueries.Enqueue(query);
}
}
#endregion
#region String Marker Method
public void SetStringMarker(string text)
{
#if DEBUG
byte[] chars = System.Text.Encoding.ASCII.GetBytes(text);
glStringMarkerGREMEDY(chars.Length, chars);
#endif
}
#endregion
#region Drawing Methods
public void DrawIndexedPrimitives(
PrimitiveType primitiveType,
int baseVertex,
int minVertexIndex,
int numVertices,
int startIndex,
int primitiveCount,
IndexBuffer indices
) {
// Bind the index buffer
BindIndexBuffer(indices.buffer);
// Draw!
glDrawRangeElementsBaseVertex(
XNAToGL.Primitive[(int) primitiveType],
minVertexIndex,
minVertexIndex + numVertices - 1,
XNAToGL.PrimitiveVerts(primitiveType, primitiveCount),
XNAToGL.IndexType[(int) indices.IndexElementSize],
(IntPtr) (startIndex * XNAToGL.IndexSize[(int) indices.IndexElementSize]),
baseVertex
);
}
public void DrawInstancedPrimitives(
PrimitiveType primitiveType,
int baseVertex,
int minVertexIndex,
int numVertices,
int startIndex,
int primitiveCount,
int instanceCount,
IndexBuffer indices
) {
// Note that minVertexIndex and numVertices are NOT used!
// Bind the index buffer
BindIndexBuffer(indices.buffer);
// Draw!
glDrawElementsInstancedBaseVertex(
XNAToGL.Primitive[(int) primitiveType],
XNAToGL.PrimitiveVerts(primitiveType, primitiveCount),
XNAToGL.IndexType[(int) indices.IndexElementSize],
(IntPtr) (startIndex * XNAToGL.IndexSize[(int) indices.IndexElementSize]),
instanceCount,
baseVertex
);
}
public void DrawPrimitives(
PrimitiveType primitiveType,
int vertexStart,
int primitiveCount
) {
// Draw!
glDrawArrays(
XNAToGL.Primitive[(int) primitiveType],
vertexStart,
XNAToGL.PrimitiveVerts(primitiveType, primitiveCount)
);
}
public void DrawUserIndexedPrimitives(
PrimitiveType primitiveType,
IntPtr vertexData,
int vertexOffset,
int numVertices,
IntPtr indexData,
int indexOffset,
IndexElementSize indexElementSize,
int primitiveCount
) {
// Unbind current index buffer.
BindIndexBuffer(OpenGLBuffer.NullBuffer);
// Draw!
glDrawRangeElements(
XNAToGL.Primitive[(int) primitiveType],
0,
numVertices - 1,
XNAToGL.PrimitiveVerts(primitiveType, primitiveCount),
XNAToGL.IndexType[(int) indexElementSize],
(IntPtr) (
indexData.ToInt64() +
(indexOffset * XNAToGL.IndexSize[(int) indexElementSize])
)
);
}
public void DrawUserPrimitives(
PrimitiveType primitiveType,
IntPtr vertexData,
int vertexOffset,
int primitiveCount
) {
// Draw!
glDrawArrays(
XNAToGL.Primitive[(int) primitiveType],
vertexOffset,
XNAToGL.PrimitiveVerts(primitiveType, primitiveCount)
);
}
#endregion
#region State Management Methods
public void SetViewport(Viewport vp, bool renderTargetBound)
{
// Flip viewport when target is not bound
if (!renderTargetBound)
{
vp.Y = Backbuffer.Height - vp.Y - vp.Height;
}
if (vp.Bounds != viewport)
{
viewport = vp.Bounds;
glViewport(
viewport.X,
viewport.Y,
viewport.Width,
viewport.Height
);
}
if (vp.MinDepth != depthRangeMin || vp.MaxDepth != depthRangeMax)
{
depthRangeMin = vp.MinDepth;
depthRangeMax = vp.MaxDepth;
glDepthRange((double) depthRangeMin, (double) depthRangeMax);
}
}
public void SetScissorRect(
Rectangle scissorRect,
bool renderTargetBound
) {
// Flip rectangle when target is not bound
if (!renderTargetBound)
{
scissorRect.Y = viewport.Height - scissorRect.Y - scissorRect.Height;
}
if (scissorRect != scissorRectangle)
{
scissorRectangle = scissorRect;
glScissor(
scissorRectangle.X,
scissorRectangle.Y,
scissorRectangle.Width,
scissorRectangle.Height
);
}
}
public void SetBlendState(BlendState blendState)
{
bool newEnable = (
!( blendState.ColorSourceBlend == Blend.One &&
blendState.ColorDestinationBlend == Blend.Zero &&
blendState.AlphaSourceBlend == Blend.One &&
blendState.AlphaDestinationBlend == Blend.Zero )
);
if (newEnable != alphaBlendEnable)
{
alphaBlendEnable = newEnable;
ToggleGLState(GLenum.GL_BLEND, alphaBlendEnable);
}
if (alphaBlendEnable)
{
if (blendState.BlendFactor != blendColor)
{
blendColor = blendState.BlendFactor;
glBlendColor(
blendColor.R / 255.0f,
blendColor.G / 255.0f,
blendColor.B / 255.0f,
blendColor.A / 255.0f
);
}
if ( blendState.ColorSourceBlend != srcBlend ||
blendState.ColorDestinationBlend != dstBlend ||
blendState.AlphaSourceBlend != srcBlendAlpha ||
blendState.AlphaDestinationBlend != dstBlendAlpha )
{
srcBlend = blendState.ColorSourceBlend;
dstBlend = blendState.ColorDestinationBlend;
srcBlendAlpha = blendState.AlphaSourceBlend;
dstBlendAlpha = blendState.AlphaDestinationBlend;
glBlendFuncSeparate(
XNAToGL.BlendMode[(int) srcBlend],
XNAToGL.BlendMode[(int) dstBlend],
XNAToGL.BlendMode[(int) srcBlendAlpha],
XNAToGL.BlendMode[(int) dstBlendAlpha]
);
}
if ( blendState.ColorBlendFunction != blendOp ||
blendState.AlphaBlendFunction != blendOpAlpha )
{
blendOp = blendState.ColorBlendFunction;
blendOpAlpha = blendState.AlphaBlendFunction;
glBlendEquationSeparate(
XNAToGL.BlendEquation[(int) blendOp],
XNAToGL.BlendEquation[(int) blendOpAlpha]
);
}
}
if (blendState.ColorWriteChannels != colorWriteEnable)
{
colorWriteEnable = blendState.ColorWriteChannels;
glColorMask(
(colorWriteEnable & ColorWriteChannels.Red) != 0,
(colorWriteEnable & ColorWriteChannels.Green) != 0,
(colorWriteEnable & ColorWriteChannels.Blue) != 0,
(colorWriteEnable & ColorWriteChannels.Alpha) != 0
);
}
/* FIXME: So how exactly do we factor in
* COLORWRITEENABLE for buffer 0? Do we just assume that
* the default is just buffer 0, and all other calls
* update the other write masks afterward? Or do we
* assume that COLORWRITEENABLE only touches 0, and the
* other 3 buffers are left alone unless we don't have
* EXT_draw_buffers2?
* -flibit
*/
if (blendState.ColorWriteChannels1 != colorWriteEnable1)
{
colorWriteEnable1 = blendState.ColorWriteChannels1;
glColorMaskIndexedEXT(
1,
(colorWriteEnable1 & ColorWriteChannels.Red) != 0,
(colorWriteEnable1 & ColorWriteChannels.Green) != 0,
(colorWriteEnable1 & ColorWriteChannels.Blue) != 0,
(colorWriteEnable1 & ColorWriteChannels.Alpha) != 0
);
}
if (blendState.ColorWriteChannels2 != colorWriteEnable2)
{
colorWriteEnable2 = blendState.ColorWriteChannels2;
glColorMaskIndexedEXT(
2,
(colorWriteEnable2 & ColorWriteChannels.Red) != 0,
(colorWriteEnable2 & ColorWriteChannels.Green) != 0,
(colorWriteEnable2 & ColorWriteChannels.Blue) != 0,
(colorWriteEnable2 & ColorWriteChannels.Alpha) != 0
);
}
if (blendState.ColorWriteChannels3 != colorWriteEnable3)
{
colorWriteEnable3 = blendState.ColorWriteChannels3;
glColorMaskIndexedEXT(
3,
(colorWriteEnable3 & ColorWriteChannels.Red) != 0,
(colorWriteEnable3 & ColorWriteChannels.Green) != 0,
(colorWriteEnable3 & ColorWriteChannels.Blue) != 0,
(colorWriteEnable3 & ColorWriteChannels.Alpha) != 0
);
}
if (blendState.MultiSampleMask != multisampleMask && supportsMultisampling)
{
if (blendState.MultiSampleMask == -1)
{
glDisable(GLenum.GL_SAMPLE_MASK);
}
else
{
if (multisampleMask == -1)
{
glEnable(GLenum.GL_SAMPLE_MASK);
}
// FIXME: index...? -flibit
glSampleMaski(0, (uint) blendState.MultiSampleMask);
}
multisampleMask = blendState.MultiSampleMask;
}
}
public void SetDepthStencilState(DepthStencilState depthStencilState)
{
if (depthStencilState.DepthBufferEnable != zEnable)
{
zEnable = depthStencilState.DepthBufferEnable;
ToggleGLState(GLenum.GL_DEPTH_TEST, zEnable);
}
if (zEnable)
{
if (depthStencilState.DepthBufferWriteEnable != zWriteEnable)
{
zWriteEnable = depthStencilState.DepthBufferWriteEnable;
glDepthMask(zWriteEnable);
}
if (depthStencilState.DepthBufferFunction != depthFunc)
{
depthFunc = depthStencilState.DepthBufferFunction;
glDepthFunc(XNAToGL.CompareFunc[(int) depthFunc]);
}
}
if (depthStencilState.StencilEnable != stencilEnable)
{
stencilEnable = depthStencilState.StencilEnable;
ToggleGLState(GLenum.GL_STENCIL_TEST, stencilEnable);
}
if (stencilEnable)
{
if (depthStencilState.StencilWriteMask != stencilWriteMask)
{
stencilWriteMask = depthStencilState.StencilWriteMask;
glStencilMask(stencilWriteMask);
}
// TODO: Can we split StencilFunc/StencilOp up nicely? -flibit
if ( depthStencilState.TwoSidedStencilMode != separateStencilEnable ||
depthStencilState.ReferenceStencil != stencilRef ||
depthStencilState.StencilMask != stencilMask ||
depthStencilState.StencilFunction != stencilFunc ||
depthStencilState.CounterClockwiseStencilFunction != ccwStencilFunc ||
depthStencilState.StencilFail != stencilFail ||
depthStencilState.StencilDepthBufferFail != stencilZFail ||
depthStencilState.StencilPass != stencilPass ||
depthStencilState.CounterClockwiseStencilFail != ccwStencilFail ||
depthStencilState.CounterClockwiseStencilDepthBufferFail != ccwStencilZFail ||
depthStencilState.CounterClockwiseStencilPass != ccwStencilPass )
{
separateStencilEnable = depthStencilState.TwoSidedStencilMode;
stencilRef = depthStencilState.ReferenceStencil;
stencilMask = depthStencilState.StencilMask;
stencilFunc = depthStencilState.StencilFunction;
stencilFail = depthStencilState.StencilFail;
stencilZFail = depthStencilState.StencilDepthBufferFail;
stencilPass = depthStencilState.StencilPass;
if (separateStencilEnable)
{
ccwStencilFunc = depthStencilState.CounterClockwiseStencilFunction;
ccwStencilFail = depthStencilState.CounterClockwiseStencilFail;
ccwStencilZFail = depthStencilState.CounterClockwiseStencilDepthBufferFail;
ccwStencilPass = depthStencilState.CounterClockwiseStencilPass;
glStencilFuncSeparate(
GLenum.GL_FRONT,
XNAToGL.CompareFunc[(int) stencilFunc],
stencilRef,
stencilMask
);
glStencilFuncSeparate(
GLenum.GL_BACK,
XNAToGL.CompareFunc[(int) ccwStencilFunc],
stencilRef,
stencilMask
);
glStencilOpSeparate(
GLenum.GL_FRONT,
XNAToGL.GLStencilOp[(int) stencilFail],
XNAToGL.GLStencilOp[(int) stencilZFail],
XNAToGL.GLStencilOp[(int) stencilPass]
);
glStencilOpSeparate(
GLenum.GL_BACK,
XNAToGL.GLStencilOp[(int) ccwStencilFail],
XNAToGL.GLStencilOp[(int) ccwStencilZFail],
XNAToGL.GLStencilOp[(int) ccwStencilPass]
);
}
else
{
glStencilFunc(
XNAToGL.CompareFunc[(int) stencilFunc],
stencilRef,
stencilMask
);
glStencilOp(
XNAToGL.GLStencilOp[(int) stencilFail],
XNAToGL.GLStencilOp[(int) stencilZFail],
XNAToGL.GLStencilOp[(int) stencilPass]
);
}
}
}
}
public void ApplyRasterizerState(
RasterizerState rasterizerState,
bool renderTargetBound
) {
if (rasterizerState.ScissorTestEnable != scissorTestEnable)
{
scissorTestEnable = rasterizerState.ScissorTestEnable;
ToggleGLState(GLenum.GL_SCISSOR_TEST, scissorTestEnable);
}
CullMode actualMode;
if (renderTargetBound)
{
actualMode = rasterizerState.CullMode;
}
else
{
// When not rendering offscreen the faces change order.
if (rasterizerState.CullMode == CullMode.None)
{
actualMode = rasterizerState.CullMode;
}
else
{
actualMode = (
rasterizerState.CullMode == CullMode.CullClockwiseFace ?
CullMode.CullCounterClockwiseFace :
CullMode.CullClockwiseFace
);
}
}
if (actualMode != cullFrontFace)
{
if ((actualMode == CullMode.None) != (cullFrontFace == CullMode.None))
{
ToggleGLState(GLenum.GL_CULL_FACE, actualMode != CullMode.None);
}
cullFrontFace = actualMode;
if (cullFrontFace != CullMode.None)
{
glFrontFace(XNAToGL.FrontFace[(int) cullFrontFace]);
}
}
if (rasterizerState.FillMode != fillMode)
{
fillMode = rasterizerState.FillMode;
glPolygonMode(
GLenum.GL_FRONT_AND_BACK,
XNAToGL.GLFillMode[(int) fillMode]
);
}
// FIXME: Floating point equality comparisons used for speed -flibit
float realDepthBias = rasterizerState.DepthBias * XNAToGL.DepthBiasScale[
renderTargetBound ?
(int) currentDepthStencilFormat :
(int) Backbuffer.DepthFormat
];
if ( realDepthBias != depthBias ||
rasterizerState.SlopeScaleDepthBias != slopeScaleDepthBias )
{
if ( realDepthBias == 0.0f &&
rasterizerState.SlopeScaleDepthBias == 0.0f)
{
// We're changing to disabled bias, disable!
glDisable(GLenum.GL_POLYGON_OFFSET_FILL);
}
else
{
if (depthBias == 0.0f && slopeScaleDepthBias == 0.0f)
{
// We're changing away from disabled bias, enable!
glEnable(GLenum.GL_POLYGON_OFFSET_FILL);
}
glPolygonOffset(
rasterizerState.SlopeScaleDepthBias,
realDepthBias
);
}
depthBias = realDepthBias;
slopeScaleDepthBias = rasterizerState.SlopeScaleDepthBias;
}
/* FIXME: This doesn't actually work on like 99% of setups!
* For whatever reason people decided that they didn't have to obey
* GL_MULTISAMPLE's value when it was disabled.
*
* If they could do it for D3D9 I fail to see why they couldn't for
* OpenGL. Idiots.
*
* -flibit
*/
if (rasterizerState.MultiSampleAntiAlias != multiSampleEnable)
{
multiSampleEnable = rasterizerState.MultiSampleAntiAlias;
ToggleGLState(GLenum.GL_MULTISAMPLE, multiSampleEnable);
}
}
public void VerifySampler(int index, Texture texture, SamplerState sampler)
{
if (texture == null)
{
if (Textures[index] != OpenGLTexture.NullTexture)
{
if (index != 0)
{
glActiveTexture(GLenum.GL_TEXTURE0 + index);
}
glBindTexture(Textures[index].Target, 0);
if (index != 0)
{
// Keep this state sane. -flibit
glActiveTexture(GLenum.GL_TEXTURE0);
}
Textures[index] = OpenGLTexture.NullTexture;
}
return;
}
OpenGLTexture tex = texture.texture as OpenGLTexture;
if ( tex == Textures[index] &&
sampler.AddressU == tex.WrapS &&
sampler.AddressV == tex.WrapT &&
sampler.AddressW == tex.WrapR &&
sampler.Filter == tex.Filter &&
sampler.MaxAnisotropy == tex.Anistropy &&
sampler.MaxMipLevel == tex.MaxMipmapLevel &&
sampler.MipMapLevelOfDetailBias == tex.LODBias )
{
// Nothing's changing, forget it.
return;
}
// Set the active texture slot
if (index != 0)
{
glActiveTexture(GLenum.GL_TEXTURE0 + index);
}
// Bind the correct texture
if (tex != Textures[index])
{
if (tex.Target != Textures[index].Target)
{
// If we're changing targets, unbind the old texture first!
glBindTexture(Textures[index].Target, 0);
}
glBindTexture(tex.Target, tex.Handle);
Textures[index] = tex;
}
// Apply the sampler states to the GL texture
if (sampler.AddressU != tex.WrapS)
{
tex.WrapS = sampler.AddressU;
glTexParameteri(
tex.Target,
GLenum.GL_TEXTURE_WRAP_S,
XNAToGL.Wrap[(int) tex.WrapS]
);
}
if (sampler.AddressV != tex.WrapT)
{
tex.WrapT = sampler.AddressV;
glTexParameteri(
tex.Target,
GLenum.GL_TEXTURE_WRAP_T,
XNAToGL.Wrap[(int) tex.WrapT]
);
}
if (sampler.AddressW != tex.WrapR)
{
tex.WrapR = sampler.AddressW;
glTexParameteri(
tex.Target,
GLenum.GL_TEXTURE_WRAP_R,
XNAToGL.Wrap[(int) tex.WrapR]
);
}
if ( sampler.Filter != tex.Filter ||
sampler.MaxAnisotropy != tex.Anistropy )
{
tex.Filter = sampler.Filter;
tex.Anistropy = sampler.MaxAnisotropy;
glTexParameteri(
tex.Target,
GLenum.GL_TEXTURE_MAG_FILTER,
XNAToGL.MagFilter[(int) tex.Filter]
);
glTexParameteri(
tex.Target,
GLenum.GL_TEXTURE_MIN_FILTER,
tex.HasMipmaps ?
XNAToGL.MinMipFilter[(int) tex.Filter] :
XNAToGL.MinFilter[(int) tex.Filter]
);
glTexParameterf(
tex.Target,
GLenum.GL_TEXTURE_MAX_ANISOTROPY_EXT,
(tex.Filter == TextureFilter.Anisotropic) ?
Math.Max(tex.Anistropy, 1.0f) :
1.0f
);
}
if (sampler.MaxMipLevel != tex.MaxMipmapLevel)
{
tex.MaxMipmapLevel = sampler.MaxMipLevel;
glTexParameteri(
tex.Target,
GLenum.GL_TEXTURE_BASE_LEVEL,
tex.MaxMipmapLevel
);
}
if (sampler.MipMapLevelOfDetailBias != tex.LODBias)
{
System.Diagnostics.Debug.Assert(!useES3);
tex.LODBias = sampler.MipMapLevelOfDetailBias;
glTexParameterf(
tex.Target,
GLenum.GL_TEXTURE_LOD_BIAS,
tex.LODBias
);
}
if (index != 0)
{
// Keep this state sane. -flibit
glActiveTexture(GLenum.GL_TEXTURE0);
}
}
#endregion
#region Effect Methods
public IGLEffect CreateEffect(byte[] effectCode)
{
IntPtr effect = IntPtr.Zero;
IntPtr glEffect = IntPtr.Zero;
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
effect = MojoShader.MOJOSHADER_parseEffect(
shaderProfile,
effectCode,
(uint) effectCode.Length,
null,
0,
null,
0,
null,
null,
IntPtr.Zero
);
#if DEBUG
unsafe
{
MojoShader.MOJOSHADER_effect *effectPtr = (MojoShader.MOJOSHADER_effect*) effect;
MojoShader.MOJOSHADER_error* err = (MojoShader.MOJOSHADER_error*) effectPtr->errors;
for (int i = 0; i < effectPtr->error_count; i += 1)
{
// From the SDL2# LPToUtf8StringMarshaler
byte* endPtr = (byte*) err[i].error;
while (*endPtr != 0)
{
endPtr++;
}
byte[] bytes = new byte[endPtr - (byte*) err[i].error];
Marshal.Copy(err[i].error, bytes, 0, bytes.Length);
FNALoggerEXT.LogError(
"MOJOSHADER_parseEffect Error: " +
System.Text.Encoding.UTF8.GetString(bytes)
);
}
}
#endif
glEffect = MojoShader.MOJOSHADER_glCompileEffect(effect);
if (glEffect == IntPtr.Zero)
{
throw new InvalidOperationException(
MojoShader.MOJOSHADER_glGetError()
);
}
#if !DISABLE_THREADING
});
#endif
return new OpenGLEffect(effect, glEffect);
}
private void DeleteEffect(IGLEffect effect)
{
IntPtr glEffectData = (effect as OpenGLEffect).GLEffectData;
if (glEffectData == currentEffect)
{
MojoShader.MOJOSHADER_glEffectEndPass(currentEffect);
MojoShader.MOJOSHADER_glEffectEnd(currentEffect);
currentEffect = IntPtr.Zero;
currentTechnique = IntPtr.Zero;
currentPass = 0;
}
MojoShader.MOJOSHADER_glDeleteEffect(glEffectData);
MojoShader.MOJOSHADER_freeEffect(effect.EffectData);
}
public IGLEffect CloneEffect(IGLEffect cloneSource)
{
IntPtr effect = IntPtr.Zero;
IntPtr glEffect = IntPtr.Zero;
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
effect = MojoShader.MOJOSHADER_cloneEffect(cloneSource.EffectData);
glEffect = MojoShader.MOJOSHADER_glCompileEffect(effect);
if (glEffect == IntPtr.Zero)
{
throw new InvalidOperationException(
MojoShader.MOJOSHADER_glGetError()
);
}
#if !DISABLE_THREADING
});
#endif
return new OpenGLEffect(effect, glEffect);
}
public void ApplyEffect(
IGLEffect effect,
IntPtr technique,
uint pass,
ref MojoShader.MOJOSHADER_effectStateChanges stateChanges
) {
effectApplied = true;
IntPtr glEffectData = (effect as OpenGLEffect).GLEffectData;
if (glEffectData == currentEffect)
{
if (technique == currentTechnique && pass == currentPass)
{
MojoShader.MOJOSHADER_glEffectCommitChanges(currentEffect);
return;
}
MojoShader.MOJOSHADER_glEffectEndPass(currentEffect);
MojoShader.MOJOSHADER_glEffectBeginPass(currentEffect, pass);
currentTechnique = technique;
currentPass = pass;
return;
}
else if (currentEffect != IntPtr.Zero)
{
MojoShader.MOJOSHADER_glEffectEndPass(currentEffect);
MojoShader.MOJOSHADER_glEffectEnd(currentEffect);
}
uint whatever;
MojoShader.MOJOSHADER_glEffectBegin(
glEffectData,
out whatever,
0,
ref stateChanges
);
MojoShader.MOJOSHADER_glEffectBeginPass(
glEffectData,
pass
);
currentEffect = glEffectData;
currentTechnique = technique;
currentPass = pass;
}
public void BeginPassRestore(
IGLEffect effect,
ref MojoShader.MOJOSHADER_effectStateChanges changes
) {
IntPtr glEffectData = (effect as OpenGLEffect).GLEffectData;
uint whatever;
MojoShader.MOJOSHADER_glEffectBegin(
glEffectData,
out whatever,
1,
ref changes
);
MojoShader.MOJOSHADER_glEffectBeginPass(
glEffectData,
0
);
effectApplied = true;
}
public void EndPassRestore(IGLEffect effect)
{
IntPtr glEffectData = (effect as OpenGLEffect).GLEffectData;
MojoShader.MOJOSHADER_glEffectEndPass(glEffectData);
MojoShader.MOJOSHADER_glEffectEnd(glEffectData);
effectApplied = true;
}
#endregion
#region glVertexAttribPointer/glVertexAttribDivisor Methods
public void ApplyVertexAttributes(
VertexBufferBinding[] bindings,
int numBindings,
bool bindingsUpdated,
int baseVertex
) {
if (supportsBaseVertex)
{
baseVertex = 0;
}
if ( bindingsUpdated ||
baseVertex != ldBaseVertex ||
currentEffect != ldEffect ||
currentTechnique != ldTechnique ||
currentPass != ldPass ||
effectApplied )
{
/* There's this weird case where you can have multiple vertbuffers,
* but they will have overlapping attributes. It seems like the
* first buffer gets priority, so start with the last one so the
* first buffer's attributes are what's bound at the end.
* -flibit
*/
for (int i = numBindings - 1; i >= 0; i -= 1)
{
BindVertexBuffer(bindings[i].VertexBuffer.buffer);
VertexDeclaration vertexDeclaration = bindings[i].VertexBuffer.VertexDeclaration;
IntPtr basePtr = (IntPtr) (
vertexDeclaration.VertexStride *
(bindings[i].VertexOffset + baseVertex)
);
foreach (VertexElement element in vertexDeclaration.elements)
{
int attribLoc = MojoShader.MOJOSHADER_glGetVertexAttribLocation(
XNAToGL.VertexAttribUsage[(int) element.VertexElementUsage],
element.UsageIndex
);
if (attribLoc == -1)
{
// Stream not in use!
continue;
}
attributeEnabled[attribLoc] = true;
VertexAttribute attr = attributes[attribLoc];
uint buffer = (bindings[i].VertexBuffer.buffer as OpenGLBuffer).Handle;
IntPtr ptr = basePtr + element.Offset;
VertexElementFormat format = element.VertexElementFormat;
bool normalized = XNAToGL.VertexAttribNormalized(element);
if ( attr.CurrentBuffer != buffer ||
attr.CurrentPointer != ptr ||
attr.CurrentFormat != element.VertexElementFormat ||
attr.CurrentNormalized != normalized ||
attr.CurrentStride != vertexDeclaration.VertexStride )
{
glVertexAttribPointer(
attribLoc,
XNAToGL.VertexAttribSize[(int) format],
XNAToGL.VertexAttribType[(int) format],
normalized,
vertexDeclaration.VertexStride,
ptr
);
attr.CurrentBuffer = buffer;
attr.CurrentPointer = ptr;
attr.CurrentFormat = format;
attr.CurrentNormalized = normalized;
attr.CurrentStride = vertexDeclaration.VertexStride;
}
if (SupportsHardwareInstancing)
{
attributeDivisor[attribLoc] = bindings[i].InstanceFrequency;
}
}
}
FlushGLVertexAttributes();
ldBaseVertex = baseVertex;
ldEffect = currentEffect;
ldTechnique = currentTechnique;
ldPass = currentPass;
effectApplied = false;
ldVertexDeclaration = null;
ldPointer = IntPtr.Zero;
}
MojoShader.MOJOSHADER_glProgramReady();
MojoShader.MOJOSHADER_glProgramViewportFlip(flipViewport);
}
public void ApplyVertexAttributes(
VertexDeclaration vertexDeclaration,
IntPtr ptr,
int vertexOffset
) {
BindVertexBuffer(OpenGLBuffer.NullBuffer);
IntPtr basePtr = ptr + (vertexDeclaration.VertexStride * vertexOffset);
if ( vertexDeclaration != ldVertexDeclaration ||
basePtr != ldPointer ||
currentEffect != ldEffect ||
currentTechnique != ldTechnique ||
currentPass != ldPass ||
effectApplied )
{
foreach (VertexElement element in vertexDeclaration.elements)
{
int attribLoc = MojoShader.MOJOSHADER_glGetVertexAttribLocation(
XNAToGL.VertexAttribUsage[(int) element.VertexElementUsage],
element.UsageIndex
);
if (attribLoc == -1)
{
// Stream not used!
continue;
}
attributeEnabled[attribLoc] = true;
VertexAttribute attr = attributes[attribLoc];
IntPtr finalPtr = basePtr + element.Offset;
bool normalized = XNAToGL.VertexAttribNormalized(element);
if ( attr.CurrentBuffer != 0 ||
attr.CurrentPointer != finalPtr ||
attr.CurrentFormat != element.VertexElementFormat ||
attr.CurrentNormalized != normalized ||
attr.CurrentStride != vertexDeclaration.VertexStride )
{
glVertexAttribPointer(
attribLoc,
XNAToGL.VertexAttribSize[(int) element.VertexElementFormat],
XNAToGL.VertexAttribType[(int) element.VertexElementFormat],
normalized,
vertexDeclaration.VertexStride,
finalPtr
);
attr.CurrentBuffer = 0;
attr.CurrentPointer = finalPtr;
attr.CurrentFormat = element.VertexElementFormat;
attr.CurrentNormalized = normalized;
attr.CurrentStride = vertexDeclaration.VertexStride;
}
attributeDivisor[attribLoc] = 0;
}
FlushGLVertexAttributes();
ldVertexDeclaration = vertexDeclaration;
ldPointer = ptr;
ldEffect = currentEffect;
ldTechnique = currentTechnique;
ldPass = currentPass;
effectApplied = false;
ldBaseVertex = -1;
}
MojoShader.MOJOSHADER_glProgramReady();
MojoShader.MOJOSHADER_glProgramViewportFlip(flipViewport);
}
private void FlushGLVertexAttributes()
{
for (int i = 0; i < attributes.Length; i += 1)
{
if (attributeEnabled[i])
{
attributeEnabled[i] = false;
if (!previousAttributeEnabled[i])
{
glEnableVertexAttribArray(i);
previousAttributeEnabled[i] = true;
}
}
else if (previousAttributeEnabled[i])
{
glDisableVertexAttribArray(i);
previousAttributeEnabled[i] = false;
}
int divisor = attributeDivisor[i];
if (divisor != previousAttributeDivisor[i])
{
glVertexAttribDivisor(i, divisor);
previousAttributeDivisor[i] = divisor;
}
}
}
#endregion
#region glGenBuffers Methods
public IGLBuffer GenVertexBuffer(
bool dynamic,
int vertexCount,
int vertexStride
) {
OpenGLBuffer result = null;
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
uint handle;
glGenBuffers(1, out handle);
result = new OpenGLBuffer(
handle,
(IntPtr) (vertexStride * vertexCount),
dynamic ? GLenum.GL_STREAM_DRAW : GLenum.GL_STATIC_DRAW
);
BindVertexBuffer(result);
glBufferData(
GLenum.GL_ARRAY_BUFFER,
result.BufferSize,
IntPtr.Zero,
result.Dynamic
);
#if !DISABLE_THREADING
});
#endif
return result;
}
public IGLBuffer GenIndexBuffer(
bool dynamic,
int indexCount,
IndexElementSize indexElementSize
) {
OpenGLBuffer result = null;
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
uint handle;
glGenBuffers(1, out handle);
result = new OpenGLBuffer(
handle,
(IntPtr) (indexCount * XNAToGL.IndexSize[(int) indexElementSize]),
dynamic ? GLenum.GL_STREAM_DRAW : GLenum.GL_STATIC_DRAW
);
BindIndexBuffer(result);
glBufferData(
GLenum.GL_ELEMENT_ARRAY_BUFFER,
result.BufferSize,
IntPtr.Zero,
result.Dynamic
);
#if !DISABLE_THREADING
});
#endif
return result;
}
#endregion
#region glBindBuffer Methods
private void BindVertexBuffer(IGLBuffer buffer)
{
uint handle = (buffer as OpenGLBuffer).Handle;
if (handle != currentVertexBuffer)
{
glBindBuffer(GLenum.GL_ARRAY_BUFFER, handle);
currentVertexBuffer = handle;
}
}
private void BindIndexBuffer(IGLBuffer buffer)
{
uint handle = (buffer as OpenGLBuffer).Handle;
if (handle != currentIndexBuffer)
{
glBindBuffer(GLenum.GL_ELEMENT_ARRAY_BUFFER, handle);
currentIndexBuffer = handle;
}
}
#endregion
#region glSetBufferData Methods
public void SetVertexBufferData(
IGLBuffer buffer,
int offsetInBytes,
IntPtr data,
int startIndex,
int elementCount,
int elementSizeInBytes,
SetDataOptions options
) {
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
BindVertexBuffer(buffer);
if (options == SetDataOptions.Discard)
{
glBufferData(
GLenum.GL_ARRAY_BUFFER,
buffer.BufferSize,
IntPtr.Zero,
(buffer as OpenGLBuffer).Dynamic
);
}
glBufferSubData(
GLenum.GL_ARRAY_BUFFER,
(IntPtr) offsetInBytes,
(IntPtr) (elementSizeInBytes * elementCount),
data + (startIndex * elementSizeInBytes)
);
#if !DISABLE_THREADING
});
#endif
}
public void SetIndexBufferData(
IGLBuffer buffer,
int offsetInBytes,
IntPtr data,
int startIndex,
int elementCount,
int elementSizeInBytes,
SetDataOptions options
) {
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
BindIndexBuffer(buffer);
if (options == SetDataOptions.Discard)
{
glBufferData(
GLenum.GL_ELEMENT_ARRAY_BUFFER,
buffer.BufferSize,
IntPtr.Zero,
(buffer as OpenGLBuffer).Dynamic
);
}
glBufferSubData(
GLenum.GL_ELEMENT_ARRAY_BUFFER,
(IntPtr) offsetInBytes,
(IntPtr) (elementSizeInBytes * elementCount),
data + (startIndex * elementSizeInBytes)
);
#if !DISABLE_THREADING
});
#endif
}
#endregion
#region glGetBufferData Methods
public void GetVertexBufferData(
IGLBuffer buffer,
int offsetInBytes,
IntPtr data,
int startIndex,
int elementCount,
int elementSizeInBytes,
int vertexStride
) {
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
BindVertexBuffer(buffer);
glGetBufferSubData(
GLenum.GL_ARRAY_BUFFER,
(IntPtr) offsetInBytes,
(IntPtr) (elementCount * vertexStride),
data + (startIndex * elementSizeInBytes)
);
#if !DISABLE_THREADING
});
#endif
}
public void GetIndexBufferData(
IGLBuffer buffer,
int offsetInBytes,
IntPtr data,
int startIndex,
int elementCount,
int elementSizeInBytes
) {
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
BindIndexBuffer(buffer);
glGetBufferSubData(
GLenum.GL_ELEMENT_ARRAY_BUFFER,
(IntPtr) offsetInBytes,
(IntPtr) (elementCount * elementSizeInBytes),
data + (startIndex * elementSizeInBytes)
);
#if !DISABLE_THREADING
});
#endif
}
#endregion
#region glDeleteBuffers Methods
private void DeleteVertexBuffer(IGLBuffer buffer)
{
uint handle = (buffer as OpenGLBuffer).Handle;
if (handle == currentVertexBuffer)
{
glBindBuffer(GLenum.GL_ARRAY_BUFFER, 0);
currentVertexBuffer = 0;
}
for (int i = 0; i < attributes.Length; i += 1)
{
if (handle == attributes[i].CurrentBuffer)
{
// Force the next vertex attrib update!
attributes[i].CurrentBuffer = uint.MaxValue;
}
}
glDeleteBuffers(1, ref handle);
}
private void DeleteIndexBuffer(IGLBuffer buffer)
{
uint handle = (buffer as OpenGLBuffer).Handle;
if (handle == currentIndexBuffer)
{
glBindBuffer(GLenum.GL_ELEMENT_ARRAY_BUFFER, 0);
currentIndexBuffer = 0;
}
glDeleteBuffers(1, ref handle);
}
#endregion
#region glCreateTexture Methods
private OpenGLTexture CreateTexture(
GLenum target,
int levelCount
) {
uint handle;
glGenTextures(1, out handle);
OpenGLTexture result = new OpenGLTexture(
handle,
target,
levelCount
);
BindTexture(result);
glTexParameteri(
result.Target,
GLenum.GL_TEXTURE_WRAP_S,
XNAToGL.Wrap[(int) result.WrapS]
);
glTexParameteri(
result.Target,
GLenum.GL_TEXTURE_WRAP_T,
XNAToGL.Wrap[(int) result.WrapT]
);
glTexParameteri(
result.Target,
GLenum.GL_TEXTURE_WRAP_R,
XNAToGL.Wrap[(int) result.WrapR]
);
glTexParameteri(
result.Target,
GLenum.GL_TEXTURE_MAG_FILTER,
XNAToGL.MagFilter[(int) result.Filter]
);
glTexParameteri(
result.Target,
GLenum.GL_TEXTURE_MIN_FILTER,
result.HasMipmaps ?
XNAToGL.MinMipFilter[(int) result.Filter] :
XNAToGL.MinFilter[(int) result.Filter]
);
glTexParameterf(
result.Target,
GLenum.GL_TEXTURE_MAX_ANISOTROPY_EXT,
(result.Filter == TextureFilter.Anisotropic) ? Math.Max(result.Anistropy, 1.0f) : 1.0f
);
glTexParameteri(
result.Target,
GLenum.GL_TEXTURE_BASE_LEVEL,
result.MaxMipmapLevel
);
if (!useES3)
{
glTexParameterf(
result.Target,
GLenum.GL_TEXTURE_LOD_BIAS,
result.LODBias
);
}
return result;
}
public IGLTexture CreateTexture2D(
SurfaceFormat format,
int width,
int height,
int levelCount
) {
OpenGLTexture result = null;
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
result = CreateTexture(
GLenum.GL_TEXTURE_2D,
levelCount
);
GLenum glFormat = XNAToGL.TextureFormat[(int) format];
GLenum glInternalFormat = XNAToGL.TextureInternalFormat[(int) format];
if (glFormat == GLenum.GL_COMPRESSED_TEXTURE_FORMATS)
{
for (int i = 0; i < levelCount; i += 1)
{
int levelWidth = Math.Max(width >> i, 1);
int levelHeight = Math.Max(height >> i, 1);
glCompressedTexImage2D(
GLenum.GL_TEXTURE_2D,
i,
(int) glInternalFormat,
levelWidth,
levelHeight,
0,
((levelWidth + 3) / 4) * ((levelHeight + 3) / 4) * Texture.GetFormatSize(format),
IntPtr.Zero
);
}
}
else
{
GLenum glType = XNAToGL.TextureDataType[(int) format];
for (int i = 0; i < levelCount; i += 1)
{
glTexImage2D(
GLenum.GL_TEXTURE_2D,
i,
(int) glInternalFormat,
Math.Max(width >> i, 1),
Math.Max(height >> i, 1),
0,
glFormat,
glType,
IntPtr.Zero
);
}
}
#if !DISABLE_THREADING
});
#endif
return result;
}
public IGLTexture CreateTexture3D(
SurfaceFormat format,
int width,
int height,
int depth,
int levelCount
) {
OpenGLTexture result = null;
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
result = CreateTexture(
GLenum.GL_TEXTURE_3D,
levelCount
);
GLenum glFormat = XNAToGL.TextureFormat[(int) format];
GLenum glInternalFormat = XNAToGL.TextureInternalFormat[(int) format];
GLenum glType = XNAToGL.TextureDataType[(int) format];
for (int i = 0; i < levelCount; i += 1)
{
glTexImage3D(
GLenum.GL_TEXTURE_3D,
i,
(int) glInternalFormat,
Math.Max(width >> i, 1),
Math.Max(height >> i, 1),
Math.Max(depth >> i, 1),
0,
glFormat,
glType,
IntPtr.Zero
);
}
#if !DISABLE_THREADING
});
#endif
return result;
}
public IGLTexture CreateTextureCube(
SurfaceFormat format,
int size,
int levelCount
) {
OpenGLTexture result = null;
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
result = CreateTexture(
GLenum.GL_TEXTURE_CUBE_MAP,
levelCount
);
GLenum glFormat = XNAToGL.TextureFormat[(int) format];
GLenum glInternalFormat = XNAToGL.TextureInternalFormat[(int) format];
if (glFormat == GLenum.GL_COMPRESSED_TEXTURE_FORMATS)
{
for (int i = 0; i < 6; i += 1)
{
for (int l = 0; l < levelCount; l += 1)
{
int levelSize = Math.Max(size >> l, 1);
glCompressedTexImage2D(
GLenum.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
l,
(int) glInternalFormat,
levelSize,
levelSize,
0,
((levelSize + 3) / 4) * ((levelSize + 3) / 4) * Texture.GetFormatSize(format),
IntPtr.Zero
);
}
}
}
else
{
GLenum glType = XNAToGL.TextureDataType[(int) format];
for (int i = 0; i < 6; i += 1)
{
for (int l = 0; l < levelCount; l += 1)
{
int levelSize = Math.Max(size >> l, 1);
glTexImage2D(
GLenum.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
l,
(int) glInternalFormat,
levelSize,
levelSize,
0,
glFormat,
glType,
IntPtr.Zero
);
}
}
}
#if !DISABLE_THREADING
});
#endif
return result;
}
#endregion
#region glTexSubImage Methods
public void SetTextureData2D(
IGLTexture texture,
SurfaceFormat format,
int x,
int y,
int w,
int h,
int level,
IntPtr data,
int startIndex,
int elementCount,
int elementSizeInBytes
) {
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
BindTexture(texture);
GLenum glFormat = XNAToGL.TextureFormat[(int) format];
if (glFormat == GLenum.GL_COMPRESSED_TEXTURE_FORMATS)
{
/* Note that we're using glInternalFormat, not glFormat.
* In this case, they should actually be the same thing,
* but we use glFormat somewhat differently for
* compressed textures.
* -flibit
*/
glCompressedTexSubImage2D(
GLenum.GL_TEXTURE_2D,
level,
x,
y,
w,
h,
XNAToGL.TextureInternalFormat[(int) format],
elementCount * elementSizeInBytes,
data + (startIndex * elementSizeInBytes)
);
}
else
{
// Set pixel alignment to match texel size in bytes
int packSize = Texture.GetFormatSize(format);
if (packSize != 4)
{
glPixelStorei(
GLenum.GL_UNPACK_ALIGNMENT,
packSize
);
}
glTexSubImage2D(
GLenum.GL_TEXTURE_2D,
level,
x,
y,
w,
h,
glFormat,
XNAToGL.TextureDataType[(int) format],
data + (startIndex * elementSizeInBytes)
);
// Keep this state sane -flibit
if (packSize != 4)
{
glPixelStorei(
GLenum.GL_UNPACK_ALIGNMENT,
4
);
}
}
#if !DISABLE_THREADING
});
#endif
}
public void SetTextureData3D(
IGLTexture texture,
SurfaceFormat format,
int level,
int left,
int top,
int right,
int bottom,
int front,
int back,
IntPtr data,
int startIndex,
int elementCount,
int elementSizeInBytes
) {
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
BindTexture(texture);
glTexSubImage3D(
GLenum.GL_TEXTURE_3D,
level,
left,
top,
front,
right - left,
bottom - top,
back - front,
XNAToGL.TextureFormat[(int) format],
XNAToGL.TextureDataType[(int) format],
data + (startIndex * elementSizeInBytes)
);
#if !DISABLE_THREADING
});
#endif
}
public void SetTextureDataCube(
IGLTexture texture,
SurfaceFormat format,
int xOffset,
int yOffset,
int width,
int height,
CubeMapFace cubeMapFace,
int level,
IntPtr data,
int startIndex,
int elementCount,
int elementSizeInBytes
) {
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
BindTexture(texture);
GLenum glFormat = XNAToGL.TextureFormat[(int) format];
if (glFormat == GLenum.GL_COMPRESSED_TEXTURE_FORMATS)
{
/* Note that we're using glInternalFormat, not glFormat.
* In this case, they should actually be the same thing,
* but we use glFormat somewhat differently for
* compressed textures.
* -flibit
*/
glCompressedTexSubImage2D(
GLenum.GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int) cubeMapFace,
level,
xOffset,
yOffset,
width,
height,
XNAToGL.TextureInternalFormat[(int) format],
elementCount * elementSizeInBytes,
data + (startIndex * elementSizeInBytes)
);
}
else
{
glTexSubImage2D(
GLenum.GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int) cubeMapFace,
level,
xOffset,
yOffset,
width,
height,
glFormat,
XNAToGL.TextureDataType[(int) format],
data + (startIndex * elementSizeInBytes)
);
}
#if !DISABLE_THREADING
});
#endif
}
public void SetTextureData2DPointer(
Texture2D texture,
IntPtr ptr
) {
BindTexture(texture.texture);
// Set pixel alignment to match texel size in bytes
int packSize = Texture.GetFormatSize(texture.Format);
if (packSize != 4)
{
glPixelStorei(
GLenum.GL_UNPACK_ALIGNMENT,
packSize
);
}
glTexSubImage2D(
GLenum.GL_TEXTURE_2D,
0,
0,
0,
texture.Width,
texture.Height,
XNAToGL.TextureFormat[(int) texture.Format],
XNAToGL.TextureDataType[(int) texture.Format],
ptr
);
// Keep this state sane -flibit
if (packSize != 4)
{
glPixelStorei(GLenum.GL_UNPACK_ALIGNMENT, 4);
}
}
#endregion
#region glGetTexImage Methods
public void GetTextureData2D(
IGLTexture texture,
SurfaceFormat format,
int width,
int height,
int level,
int subX,
int subY,
int subW,
int subH,
IntPtr data,
int startIndex,
int elementCount,
int elementSizeInBytes
) {
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
if (level == 0 && ReadTargetIfApplicable(
texture,
width,
height,
level,
data,
subX,
subY,
subW,
subH
)) {
return;
}
BindTexture(texture);
GLenum glFormat = XNAToGL.TextureFormat[(int) format];
if (glFormat == GLenum.GL_COMPRESSED_TEXTURE_FORMATS)
{
throw new NotImplementedException("GetData, CompressedTexture");
}
else if (subX == 0 && subY == 0 && subW == width && subH == height)
{
// Just throw the whole texture into the user array.
glGetTexImage(
GLenum.GL_TEXTURE_2D,
level,
glFormat,
XNAToGL.TextureDataType[(int) format],
data
);
}
else
{
// Get the whole texture...
IntPtr texData = Marshal.AllocHGlobal(width * height * elementSizeInBytes);
glGetTexImage(
GLenum.GL_TEXTURE_2D,
level,
glFormat,
XNAToGL.TextureDataType[(int) format],
texData
);
// Now, blit the rect region into the user array.
int curPixel = -1;
for (int row = subY; row < subY + subH; row += 1)
{
for (int col = subX; col < subX + subW; col += 1)
{
curPixel += 1;
if (curPixel < startIndex)
{
// If we're not at the start yet, just keep going...
continue;
}
if (curPixel > elementCount)
{
// If we're past the end, we're done!
return;
}
// FIXME: Can we copy via pitch instead, or something? -flibit
memcpy(
data + ((curPixel - startIndex) * elementSizeInBytes),
texData + (((row * width) + col) * elementSizeInBytes),
(IntPtr) elementSizeInBytes
);
}
}
Marshal.FreeHGlobal(texData);
}
#if !DISABLE_THREADING
});
#endif
}
public void GetTextureData3D(
IGLTexture texture,
SurfaceFormat format,
int left,
int top,
int front,
int right,
int bottom,
int back,
int level,
IntPtr data,
int startIndex,
int elementCount,
int elementSizeInBytes
) {
throw new NotImplementedException();
}
public void GetTextureDataCube(
IGLTexture texture,
SurfaceFormat format,
int size,
CubeMapFace cubeMapFace,
int level,
int subX,
int subY,
int subW,
int subH,
IntPtr data,
int startIndex,
int elementCount,
int elementSizeInBytes
) {
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
BindTexture(texture);
GLenum glFormat = XNAToGL.TextureFormat[(int) format];
if (glFormat == GLenum.GL_COMPRESSED_TEXTURE_FORMATS)
{
throw new NotImplementedException("GetData, CompressedTexture");
}
else if (subX == 0 && subY == 0 && subW == size && subH == size)
{
// Just throw the whole texture into the user array.
glGetTexImage(
GLenum.GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int) cubeMapFace,
level,
glFormat,
XNAToGL.TextureDataType[(int) format],
data
);
}
else
{
// Get the whole texture...
IntPtr texData = Marshal.AllocHGlobal(size * size * elementSizeInBytes);
glGetTexImage(
GLenum.GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int) cubeMapFace,
level,
glFormat,
XNAToGL.TextureDataType[(int) format],
texData
);
// Now, blit the rect region into the user array.
int curPixel = -1;
for (int row = subY; row < subY + subH; row += 1)
{
for (int col = subX; col < subX + subW; col += 1)
{
curPixel += 1;
if (curPixel < startIndex)
{
// If we're not at the start yet, just keep going...
continue;
}
if (curPixel > elementCount)
{
// If we're past the end, we're done!
return;
}
// FIXME: Can we copy via pitch instead, or something? -flibit
memcpy(
data + ((curPixel - startIndex) * elementSizeInBytes),
texData + (((row * size) + col) * elementSizeInBytes),
(IntPtr) elementSizeInBytes
);
}
}
Marshal.FreeHGlobal(texData);
}
#if !DISABLE_THREADING
});
#endif
}
#endregion
#region glBindTexture Method
private void BindTexture(IGLTexture texture)
{
OpenGLTexture tex = texture as OpenGLTexture;
if (tex.Target != Textures[0].Target)
{
glBindTexture(Textures[0].Target, 0);
}
if (tex != Textures[0])
{
glBindTexture(
tex.Target,
tex.Handle
);
}
Textures[0] = tex;
}
#endregion
#region glDeleteTexture Method
private void DeleteTexture(IGLTexture texture)
{
uint handle = (texture as OpenGLTexture).Handle;
for (int i = 0; i < currentAttachments.Length; i += 1)
{
if (handle == currentAttachments[i])
{
// Force an attachment update, this no longer exists!
currentAttachments[i] = uint.MaxValue;
}
}
glDeleteTextures(1, ref handle);
}
#endregion
#region glReadPixels Methods
public void ReadBackbuffer(
IntPtr data,
int dataLen,
int startIndex,
int elementCount,
int elementSizeInBytes,
int subX,
int subY,
int subW,
int subH
) {
/* FIXME: Right now we're expecting one of the following:
* - byte[]
* - int[]
* - uint[]
* - Color[]
* Anything else will freak out because we're using
* color backbuffers. Maybe check this out when adding
* support for more backbuffer types!
* -flibit
*/
if (startIndex > 0 || elementCount != (dataLen / elementSizeInBytes))
{
throw new NotImplementedException(
"ReadBackbuffer startIndex/elementCount"
);
}
uint prevReadBuffer = currentReadFramebuffer;
if (Backbuffer.MultiSampleCount > 0)
{
// We have to resolve the renderbuffer to a texture first.
uint prevDrawBuffer = currentDrawFramebuffer;
OpenGLBackbuffer glBack = Backbuffer as OpenGLBackbuffer;
if (glBack.Texture == 0)
{
glGenTextures(1, out glBack.Texture);
glBindTexture(GLenum.GL_TEXTURE_2D, glBack.Texture);
glTexImage2D(
GLenum.GL_TEXTURE_2D,
0,
(int) GLenum.GL_RGBA,
glBack.Width,
glBack.Height,
0,
GLenum.GL_RGBA,
GLenum.GL_UNSIGNED_BYTE,
IntPtr.Zero
);
glBindTexture(Textures[0].Target, Textures[0].Handle);
}
BindFramebuffer(resolveFramebufferDraw);
glFramebufferTexture2D(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0,
GLenum.GL_TEXTURE_2D,
glBack.Texture,
0
);
BindReadFramebuffer(glBack.Handle);
glBlitFramebuffer(
0, 0, glBack.Width, glBack.Height,
0, 0, glBack.Width, glBack.Height,
GLenum.GL_COLOR_BUFFER_BIT,
GLenum.GL_LINEAR
);
BindDrawFramebuffer(prevDrawBuffer);
BindReadFramebuffer(resolveFramebufferDraw);
}
else
{
BindReadFramebuffer(
(Backbuffer is OpenGLBackbuffer) ?
(Backbuffer as OpenGLBackbuffer).Handle :
0
);
}
glReadPixels(
subX,
subY,
subW,
subH,
GLenum.GL_RGBA,
GLenum.GL_UNSIGNED_BYTE,
data
);
BindReadFramebuffer(prevReadBuffer);
// Now we get to do a software-based flip! Yes, really! -flibit
int pitch = subW * 4;
IntPtr temp = Marshal.AllocHGlobal(pitch);
for (int row = 0; row < subH / 2; row += 1)
{
// Top to temp, bottom to top, temp to bottom
memcpy(temp, data + (row * pitch), (IntPtr) pitch);
memcpy(data + (row * pitch), data + ((subH - row - 1) * pitch), (IntPtr) pitch);
memcpy(data + ((subH - row - 1) * pitch), temp, (IntPtr) pitch);
}
Marshal.FreeHGlobal(temp);
}
/// <summary>
/// Attempts to read the texture data directly from the FBO using glReadPixels
/// </summary>
/// <typeparam name="T">Texture data type</typeparam>
/// <param name="texture">The texture to read from</param>
/// <param name="width">The texture width</param>
/// <param name="height">The texture height</param>
/// <param name="level">The texture level</param>
/// <param name="data">The texture data array</param>
/// <param name="rect">The portion of the image to read from</param>
/// <returns>True if we successfully read the texture data</returns>
private bool ReadTargetIfApplicable(
IGLTexture texture,
int width,
int height,
int level,
IntPtr data,
int subX,
int subY,
int subW,
int subH
) {
bool texUnbound = ( currentDrawBuffers != 1 ||
currentAttachments[0] != (texture as OpenGLTexture).Handle );
if (texUnbound && !useES3)
{
return false;
}
uint prevReadBuffer = currentReadFramebuffer;
uint prevWriteBuffer = currentDrawFramebuffer;
if (texUnbound)
{
BindFramebuffer(resolveFramebufferRead);
glFramebufferTexture2D(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0,
GLenum.GL_TEXTURE_2D,
(texture as OpenGLTexture).Handle,
level
);
}
else
{
BindReadFramebuffer(targetFramebuffer);
}
/* glReadPixels should be faster than reading
* back from the render target if we are already bound.
*/
glReadPixels(
subX,
subY,
subW,
subH,
GLenum.GL_RGBA, // FIXME: Assumption!
GLenum.GL_UNSIGNED_BYTE,
data
);
if (texUnbound)
{
if (prevReadBuffer == prevWriteBuffer)
{
BindFramebuffer(prevReadBuffer);
}
else
{
BindReadFramebuffer(prevReadBuffer);
BindDrawFramebuffer(prevWriteBuffer);
}
}
else
{
BindReadFramebuffer(prevReadBuffer);
}
return true;
}
#endregion
#region RenderTarget->Texture Method
public void ResolveTarget(RenderTargetBinding target)
{
if ((target.RenderTarget as IRenderTarget).MultiSampleCount > 0)
{
uint prevBuffer = currentDrawFramebuffer;
// Set up the texture framebuffer
GLenum textureTarget;
int width, height;
if (target.RenderTarget is RenderTarget2D)
{
textureTarget = GLenum.GL_TEXTURE_2D;
Texture2D target2D = (target.RenderTarget as Texture2D);
width = target2D.Width;
height = target2D.Height;
}
else
{
textureTarget = GLenum.GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int) target.CubeMapFace;
TextureCube targetCube = (target.RenderTarget as TextureCube);
width = targetCube.Size;
height = targetCube.Size;
}
BindFramebuffer(resolveFramebufferDraw);
glFramebufferTexture2D(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0,
textureTarget,
(target.RenderTarget.texture as OpenGLTexture).Handle,
0
);
// Set up the renderbuffer framebuffer
BindFramebuffer(resolveFramebufferRead);
glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0,
GLenum.GL_RENDERBUFFER,
((target.RenderTarget as IRenderTarget).ColorBuffer as OpenGLRenderbuffer).Handle
);
// Blit!
if (scissorTestEnable)
{
glDisable(GLenum.GL_SCISSOR_TEST);
}
BindDrawFramebuffer(resolveFramebufferDraw);
glBlitFramebuffer(
0, 0, width, height,
0, 0, width, height,
GLenum.GL_COLOR_BUFFER_BIT,
GLenum.GL_LINEAR
);
if (scissorTestEnable)
{
glEnable(GLenum.GL_SCISSOR_TEST);
}
BindFramebuffer(prevBuffer);
}
// If the target has mipmaps, regenerate them now
if (target.RenderTarget.LevelCount > 1)
{
OpenGLTexture prevTex = Textures[0];
BindTexture(target.RenderTarget.texture);
glGenerateMipmap((target.RenderTarget.texture as OpenGLTexture).Target);
BindTexture(prevTex);
}
}
#endregion
#region Framebuffer Methods
private void BindFramebuffer(uint handle)
{
if ( currentReadFramebuffer != handle &&
currentDrawFramebuffer != handle )
{
glBindFramebuffer(
GLenum.GL_FRAMEBUFFER,
handle
);
currentReadFramebuffer = handle;
currentDrawFramebuffer = handle;
}
else if (currentReadFramebuffer != handle)
{
BindReadFramebuffer(handle);
}
else if (currentDrawFramebuffer != handle)
{
BindDrawFramebuffer(handle);
}
}
private void BindReadFramebuffer(uint handle)
{
if (handle == currentReadFramebuffer)
{
return;
}
glBindFramebuffer(
GLenum.GL_READ_FRAMEBUFFER,
handle
);
currentReadFramebuffer = handle;
}
private void BindDrawFramebuffer(uint handle)
{
if (handle == currentDrawFramebuffer)
{
return;
}
glBindFramebuffer(
GLenum.GL_DRAW_FRAMEBUFFER,
handle
);
currentDrawFramebuffer = handle;
}
#endregion
#region Renderbuffer Methods
public IGLRenderbuffer GenRenderbuffer(
int width,
int height,
SurfaceFormat format,
int multiSampleCount
) {
uint handle = 0;
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
glGenRenderbuffers(1, out handle);
glBindRenderbuffer(
GLenum.GL_RENDERBUFFER,
handle
);
if (multiSampleCount > 0)
{
glRenderbufferStorageMultisample(
GLenum.GL_RENDERBUFFER,
multiSampleCount,
XNAToGL.TextureInternalFormat[(int) format],
width,
height
);
}
else
{
glRenderbufferStorage(
GLenum.GL_RENDERBUFFER,
XNAToGL.TextureInternalFormat[(int) format],
width,
height
);
}
glBindRenderbuffer(
GLenum.GL_RENDERBUFFER,
0
);
#if !DISABLE_THREADING
});
#endif
return new OpenGLRenderbuffer(handle);
}
public IGLRenderbuffer GenRenderbuffer(
int width,
int height,
DepthFormat format,
int multiSampleCount
) {
uint handle = 0;
#if !DISABLE_THREADING
ForceToMainThread(() => {
#endif
glGenRenderbuffers(1, out handle);
glBindRenderbuffer(
GLenum.GL_RENDERBUFFER,
handle
);
if (multiSampleCount > 0)
{
glRenderbufferStorageMultisample(
GLenum.GL_RENDERBUFFER,
multiSampleCount,
XNAToGL.DepthStorage[(int) format],
width,
height
);
}
else
{
glRenderbufferStorage(
GLenum.GL_RENDERBUFFER,
XNAToGL.DepthStorage[(int) format],
width,
height
);
}
glBindRenderbuffer(
GLenum.GL_RENDERBUFFER,
0
);
#if !DISABLE_THREADING
});
#endif
return new OpenGLRenderbuffer(handle);
}
private void DeleteRenderbuffer(IGLRenderbuffer renderbuffer)
{
uint handle = (renderbuffer as OpenGLRenderbuffer).Handle;
if (handle == currentRenderbuffer)
{
// Force a renderbuffer update, this no longer exists!
currentRenderbuffer = uint.MaxValue;
}
glDeleteRenderbuffers(1, ref handle);
}
#endregion
#region glEnable/glDisable Method
private void ToggleGLState(GLenum feature, bool enable)
{
if (enable)
{
glEnable(feature);
}
else
{
glDisable(feature);
}
}
#endregion
#region glClear Method
public void Clear(ClearOptions options, Vector4 color, float depth, int stencil)
{
// glClear depends on the scissor rectangle!
if (scissorTestEnable)
{
glDisable(GLenum.GL_SCISSOR_TEST);
}
bool clearTarget = (options & ClearOptions.Target) == ClearOptions.Target;
bool clearDepth = (options & ClearOptions.DepthBuffer) == ClearOptions.DepthBuffer;
bool clearStencil = (options & ClearOptions.Stencil) == ClearOptions.Stencil;
// Get the clear mask, set the clear properties if needed
GLenum clearMask = GLenum.GL_ZERO;
if (clearTarget)
{
clearMask |= GLenum.GL_COLOR_BUFFER_BIT;
if (!color.Equals(currentClearColor))
{
glClearColor(
color.X,
color.Y,
color.Z,
color.W
);
currentClearColor = color;
}
// glClear depends on the color write mask!
if (colorWriteEnable != ColorWriteChannels.All)
{
// FIXME: ColorWriteChannels1/2/3? -flibit
glColorMask(true, true, true, true);
}
}
if (clearDepth)
{
clearMask |= GLenum.GL_DEPTH_BUFFER_BIT;
if (depth != currentClearDepth)
{
glClearDepth((double) depth);
currentClearDepth = depth;
}
// glClear depends on the depth write mask!
if (!zWriteEnable)
{
glDepthMask(true);
}
}
if (clearStencil)
{
clearMask |= GLenum.GL_STENCIL_BUFFER_BIT;
if (stencil != currentClearStencil)
{
glClearStencil(stencil);
currentClearStencil = stencil;
}
// glClear depends on the stencil write mask!
if (stencilWriteMask != -1)
{
// AKA 0xFFFFFFFF, ugh -flibit
glStencilMask(-1);
}
}
// CLEAR!
glClear(clearMask);
// Clean up after ourselves.
if (scissorTestEnable)
{
glEnable(GLenum.GL_SCISSOR_TEST);
}
if (clearTarget && colorWriteEnable != ColorWriteChannels.All)
{
// FIXME: ColorWriteChannels1/2/3? -flibit
glColorMask(
(colorWriteEnable & ColorWriteChannels.Red) != 0,
(colorWriteEnable & ColorWriteChannels.Blue) != 0,
(colorWriteEnable & ColorWriteChannels.Green) != 0,
(colorWriteEnable & ColorWriteChannels.Alpha) != 0
);
}
if (clearDepth && !zWriteEnable)
{
glDepthMask(false);
}
if (clearStencil && stencilWriteMask != -1) // AKA 0xFFFFFFFF, ugh -flibit
{
glStencilMask(stencilWriteMask);
}
}
#endregion
#region SetRenderTargets Method
public void SetRenderTargets(
RenderTargetBinding[] renderTargets,
IGLRenderbuffer renderbuffer,
DepthFormat depthFormat
) {
// Bind the right framebuffer, if needed
if (renderTargets == null)
{
BindFramebuffer(
(Backbuffer is OpenGLBackbuffer) ?
(Backbuffer as OpenGLBackbuffer).Handle :
0
);
flipViewport = 1;
return;
}
else
{
BindFramebuffer(targetFramebuffer);
flipViewport = -1;
}
int i;
for (i = 0; i < renderTargets.Length; i += 1)
{
IGLRenderbuffer colorBuffer = (renderTargets[i].RenderTarget as IRenderTarget).ColorBuffer;
if (colorBuffer != null)
{
attachments[i] = (colorBuffer as OpenGLRenderbuffer).Handle;
attachmentTypes[i] = GLenum.GL_RENDERBUFFER;
}
else
{
attachments[i] = (renderTargets[i].RenderTarget.texture as OpenGLTexture).Handle;
if (renderTargets[i].RenderTarget is RenderTarget2D)
{
attachmentTypes[i] = GLenum.GL_TEXTURE_2D;
}
else
{
attachmentTypes[i] = GLenum.GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int) renderTargets[i].CubeMapFace;
}
}
}
// Update the color attachments, DrawBuffers state
for (i = 0; i < renderTargets.Length; i += 1)
{
if (attachments[i] != currentAttachments[i])
{
if (currentAttachments[i] != 0)
{
if ( attachmentTypes[i] != GLenum.GL_RENDERBUFFER &&
currentAttachmentTypes[i] == GLenum.GL_RENDERBUFFER )
{
glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0 + i,
GLenum.GL_RENDERBUFFER,
0
);
}
else if ( attachmentTypes[i] == GLenum.GL_RENDERBUFFER &&
currentAttachmentTypes[i] != GLenum.GL_RENDERBUFFER )
{
glFramebufferTexture2D(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0 + i,
currentAttachmentTypes[i],
0,
0
);
}
}
if (attachmentTypes[i] == GLenum.GL_RENDERBUFFER)
{
glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0 + i,
GLenum.GL_RENDERBUFFER,
attachments[i]
);
}
else
{
glFramebufferTexture2D(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0 + i,
attachmentTypes[i],
attachments[i],
0
);
}
currentAttachments[i] = attachments[i];
currentAttachmentTypes[i] = attachmentTypes[i];
}
else if (attachmentTypes[i] != currentAttachmentTypes[i])
{
// Texture cube face change!
glFramebufferTexture2D(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0 + i,
attachmentTypes[i],
attachments[i],
0
);
currentAttachmentTypes[i] = attachmentTypes[i];
}
}
while (i < currentAttachments.Length)
{
if (currentAttachments[i] != 0)
{
if (currentAttachmentTypes[i] == GLenum.GL_RENDERBUFFER)
{
glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0 + i,
GLenum.GL_RENDERBUFFER,
0
);
}
else
{
glFramebufferTexture2D(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0 + i,
currentAttachmentTypes[i],
0,
0
);
}
currentAttachments[i] = 0;
currentAttachmentTypes[i] = GLenum.GL_TEXTURE_2D;
}
i += 1;
}
if (renderTargets.Length != currentDrawBuffers)
{
glDrawBuffers(renderTargets.Length, drawBuffersArray);
currentDrawBuffers = renderTargets.Length;
}
// Update the depth/stencil attachment
/* FIXME: Notice that we do separate attach calls for the stencil.
* We _should_ be able to do a single attach for depthstencil, but
* some drivers (like Mesa) cannot into GL_DEPTH_STENCIL_ATTACHMENT.
* Use XNAToGL.DepthStencilAttachment when this isn't a problem.
* -flibit
*/
uint handle;
if (renderbuffer == null)
{
handle = 0;
}
else
{
handle = (renderbuffer as OpenGLRenderbuffer).Handle;
}
if (handle != currentRenderbuffer)
{
if (currentDepthStencilFormat == DepthFormat.Depth24Stencil8)
{
glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_STENCIL_ATTACHMENT,
GLenum.GL_RENDERBUFFER,
0
);
}
currentDepthStencilFormat = depthFormat;
glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_DEPTH_ATTACHMENT,
GLenum.GL_RENDERBUFFER,
handle
);
if (currentDepthStencilFormat == DepthFormat.Depth24Stencil8)
{
glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_STENCIL_ATTACHMENT,
GLenum.GL_RENDERBUFFER,
handle
);
}
currentRenderbuffer = handle;
}
}
#endregion
#region Query Object Methods
public IGLQuery CreateQuery()
{
uint handle;
glGenQueries(1, out handle);
return new OpenGLQuery(handle);
}
private void DeleteQuery(IGLQuery query)
{
uint handle = (query as OpenGLQuery).Handle;
glDeleteQueries(
1,
ref handle
);
}
public void QueryBegin(IGLQuery query)
{
glBeginQuery(
GLenum.GL_SAMPLES_PASSED,
(query as OpenGLQuery).Handle
);
}
public void QueryEnd(IGLQuery query)
{
// May need to check active queries...?
glEndQuery(
GLenum.GL_SAMPLES_PASSED
);
}
public bool QueryComplete(IGLQuery query)
{
uint result;
glGetQueryObjectuiv(
(query as OpenGLQuery).Handle,
GLenum.GL_QUERY_RESULT_AVAILABLE,
out result
);
return result != 0;
}
public int QueryPixelCount(IGLQuery query)
{
uint result;
glGetQueryObjectuiv(
(query as OpenGLQuery).Handle,
GLenum.GL_QUERY_RESULT,
out result
);
return (int) result;
}
#endregion
#region XNA->GL Enum Conversion Class
private static class XNAToGL
{
public static readonly GLenum[] TextureFormat = new GLenum[]
{
GLenum.GL_RGBA, // SurfaceFormat.Color
GLenum.GL_RGB, // SurfaceFormat.Bgr565
GLenum.GL_BGRA, // SurfaceFormat.Bgra5551
GLenum.GL_BGRA, // SurfaceFormat.Bgra4444
GLenum.GL_COMPRESSED_TEXTURE_FORMATS, // SurfaceFormat.Dxt1
GLenum.GL_COMPRESSED_TEXTURE_FORMATS, // SurfaceFormat.Dxt3
GLenum.GL_COMPRESSED_TEXTURE_FORMATS, // SurfaceFormat.Dxt5
GLenum.GL_RG, // SurfaceFormat.NormalizedByte2
GLenum.GL_RGBA, // SurfaceFormat.NormalizedByte4
GLenum.GL_RGBA, // SurfaceFormat.Rgba1010102
GLenum.GL_RG, // SurfaceFormat.Rg32
GLenum.GL_RGBA, // SurfaceFormat.Rgba64
GLenum.GL_LUMINANCE, // SurfaceFormat.Alpha8
GLenum.GL_RED, // SurfaceFormat.Single
GLenum.GL_RG, // SurfaceFormat.Vector2
GLenum.GL_RGBA, // SurfaceFormat.Vector4
GLenum.GL_RED, // SurfaceFormat.HalfSingle
GLenum.GL_RG, // SurfaceFormat.HalfVector2
GLenum.GL_RGBA, // SurfaceFormat.HalfVector4
GLenum.GL_RGBA, // SurfaceFormat.HdrBlendable
GLenum.GL_BGRA, // SurfaceFormat.ColorBgraEXT
};
public static readonly GLenum[] TextureInternalFormat = new GLenum[]
{
GLenum.GL_RGBA8, // SurfaceFormat.Color
GLenum.GL_RGB8, // SurfaceFormat.Bgr565
GLenum.GL_RGB5_A1, // SurfaceFormat.Bgra5551
GLenum.GL_RGBA4, // SurfaceFormat.Bgra4444
GLenum.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, // SurfaceFormat.Dxt1
GLenum.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, // SurfaceFormat.Dxt3
GLenum.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, // SurfaceFormat.Dxt5
GLenum.GL_RG8, // SurfaceFormat.NormalizedByte2
GLenum.GL_RGBA8, // SurfaceFormat.NormalizedByte4
GLenum.GL_RGB10_A2_EXT, // SurfaceFormat.Rgba1010102
GLenum.GL_RG16, // SurfaceFormat.Rg32
GLenum.GL_RGBA16, // SurfaceFormat.Rgba64
GLenum.GL_LUMINANCE8, // SurfaceFormat.Alpha8
GLenum.GL_R32F, // SurfaceFormat.Single
GLenum.GL_RG32F, // SurfaceFormat.Vector2
GLenum.GL_RGBA32F, // SurfaceFormat.Vector4
GLenum.GL_R16F, // SurfaceFormat.HalfSingle
GLenum.GL_RG16F, // SurfaceFormat.HalfVector2
GLenum.GL_RGBA16F, // SurfaceFormat.HalfVector4
GLenum.GL_RGBA16F, // SurfaceFormat.HdrBlendable
GLenum.GL_RGBA8 // SurfaceFormat.ColorBgraEXT
};
public static readonly GLenum[] TextureDataType = new GLenum[]
{
GLenum.GL_UNSIGNED_BYTE, // SurfaceFormat.Color
GLenum.GL_UNSIGNED_SHORT_5_6_5, // SurfaceFormat.Bgr565
GLenum.GL_UNSIGNED_SHORT_5_5_5_1, // SurfaceFormat.Bgra5551
GLenum.GL_UNSIGNED_SHORT_4_4_4_4, // SurfaceFormat.Bgra4444
GLenum.GL_ZERO, // NOPE
GLenum.GL_ZERO, // NOPE
GLenum.GL_ZERO, // NOPE
GLenum.GL_BYTE, // SurfaceFormat.NormalizedByte2
GLenum.GL_BYTE, // SurfaceFormat.NormalizedByte4
GLenum.GL_UNSIGNED_INT_10_10_10_2, // SurfaceFormat.Rgba1010102
GLenum.GL_UNSIGNED_SHORT, // SurfaceFormat.Rg32
GLenum.GL_UNSIGNED_SHORT, // SurfaceFormat.Rgba64
GLenum.GL_UNSIGNED_BYTE, // SurfaceFormat.Alpha8
GLenum.GL_FLOAT, // SurfaceFormat.Single
GLenum.GL_FLOAT, // SurfaceFormat.Vector2
GLenum.GL_FLOAT, // SurfaceFormat.Vector4
GLenum.GL_HALF_FLOAT, // SurfaceFormat.HalfSingle
GLenum.GL_HALF_FLOAT, // SurfaceFormat.HalfVector2
GLenum.GL_HALF_FLOAT, // SurfaceFormat.HalfVector4
GLenum.GL_HALF_FLOAT, // SurfaceFormat.HdrBlendable
GLenum.GL_UNSIGNED_BYTE
};
public static readonly GLenum[] BlendMode = new GLenum[]
{
GLenum.GL_ONE, // Blend.One
GLenum.GL_ZERO, // Blend.Zero
GLenum.GL_SRC_COLOR, // Blend.SourceColor
GLenum.GL_ONE_MINUS_SRC_COLOR, // Blend.InverseSourceColor
GLenum.GL_SRC_ALPHA, // Blend.SourceAlpha
GLenum.GL_ONE_MINUS_SRC_ALPHA, // Blend.InverseSourceAlpha
GLenum.GL_DST_COLOR, // Blend.DestinationColor
GLenum.GL_ONE_MINUS_DST_COLOR, // Blend.InverseDestinationColor
GLenum.GL_DST_ALPHA, // Blend.DestinationAlpha
GLenum.GL_ONE_MINUS_DST_ALPHA, // Blend.InverseDestinationAlpha
GLenum.GL_CONSTANT_COLOR, // Blend.BlendFactor
GLenum.GL_ONE_MINUS_CONSTANT_COLOR, // Blend.InverseBlendFactor
GLenum.GL_SRC_ALPHA_SATURATE // Blend.SourceAlphaSaturation
};
public static readonly GLenum[] BlendEquation = new GLenum[]
{
GLenum.GL_FUNC_ADD, // BlendFunction.Add
GLenum.GL_FUNC_SUBTRACT, // BlendFunction.Subtract
GLenum.GL_FUNC_REVERSE_SUBTRACT, // BlendFunction.ReverseSubtract
GLenum.GL_MAX, // BlendFunction.Max
GLenum.GL_MIN // BlendFunction.Min
};
public static readonly GLenum[] CompareFunc = new GLenum[]
{
GLenum.GL_ALWAYS, // CompareFunction.Always
GLenum.GL_NEVER, // CompareFunction.Never
GLenum.GL_LESS, // CompareFunction.Less
GLenum.GL_LEQUAL, // CompareFunction.LessEqual
GLenum.GL_EQUAL, // CompareFunction.Equal
GLenum.GL_GEQUAL, // CompareFunction.GreaterEqual
GLenum.GL_GREATER, // CompareFunction.Greater
GLenum.GL_NOTEQUAL // CompareFunction.NotEqual
};
public static readonly GLenum[] GLStencilOp = new GLenum[]
{
GLenum.GL_KEEP, // StencilOperation.Keep
GLenum.GL_ZERO, // StencilOperation.Zero
GLenum.GL_REPLACE, // StencilOperation.Replace
GLenum.GL_INCR_WRAP, // StencilOperation.Increment
GLenum.GL_DECR_WRAP, // StencilOperation.Decrement
GLenum.GL_INCR, // StencilOperation.IncrementSaturation
GLenum.GL_DECR, // StencilOperation.DecrementSaturation
GLenum.GL_INVERT // StencilOperation.Invert
};
public static readonly GLenum[] FrontFace = new GLenum[]
{
GLenum.GL_ZERO, // NOPE
GLenum.GL_CW, // CullMode.CullClockwiseFace
GLenum.GL_CCW // CullMode.CullCounterClockwiseFace
};
public static readonly GLenum[] GLFillMode = new GLenum[]
{
GLenum.GL_FILL, // FillMode.Solid
GLenum.GL_LINE // FillMode.WireFrame
};
public static readonly int[] Wrap = new int[]
{
(int) GLenum.GL_REPEAT, // TextureAddressMode.Wrap
(int) GLenum.GL_CLAMP_TO_EDGE, // TextureAddressMode.Clamp
(int) GLenum.GL_MIRRORED_REPEAT // TextureAddressMode.Mirror
};
public static readonly int[] MagFilter = new int[]
{
(int) GLenum.GL_LINEAR, // TextureFilter.Linear
(int) GLenum.GL_NEAREST, // TextureFilter.Point
(int) GLenum.GL_LINEAR, // TextureFilter.Anisotropic
(int) GLenum.GL_LINEAR, // TextureFilter.LinearMipPoint
(int) GLenum.GL_NEAREST, // TextureFilter.PointMipLinear
(int) GLenum.GL_NEAREST, // TextureFilter.MinLinearMagPointMipLinear
(int) GLenum.GL_NEAREST, // TextureFilter.MinLinearMagPointMipPoint
(int) GLenum.GL_LINEAR, // TextureFilter.MinPointMagLinearMipLinear
(int) GLenum.GL_LINEAR // TextureFilter.MinPointMagLinearMipPoint
};
public static readonly int[] MinMipFilter = new int[]
{
(int) GLenum.GL_LINEAR_MIPMAP_LINEAR, // TextureFilter.Linear
(int) GLenum.GL_NEAREST_MIPMAP_NEAREST, // TextureFilter.Point
(int) GLenum.GL_LINEAR_MIPMAP_LINEAR, // TextureFilter.Anisotropic
(int) GLenum.GL_LINEAR_MIPMAP_NEAREST, // TextureFilter.LinearMipPoint
(int) GLenum.GL_NEAREST_MIPMAP_LINEAR, // TextureFilter.PointMipLinear
(int) GLenum.GL_LINEAR_MIPMAP_LINEAR, // TextureFilter.MinLinearMagPointMipLinear
(int) GLenum.GL_LINEAR_MIPMAP_NEAREST, // TextureFilter.MinLinearMagPointMipPoint
(int) GLenum.GL_NEAREST_MIPMAP_LINEAR, // TextureFilter.MinPointMagLinearMipLinear
(int) GLenum.GL_NEAREST_MIPMAP_NEAREST // TextureFilter.MinPointMagLinearMipPoint
};
public static readonly int[] MinFilter = new int[]
{
(int) GLenum.GL_LINEAR, // TextureFilter.Linear
(int) GLenum.GL_NEAREST, // TextureFilter.Point
(int) GLenum.GL_LINEAR, // TextureFilter.Anisotropic
(int) GLenum.GL_LINEAR, // TextureFilter.LinearMipPoint
(int) GLenum.GL_NEAREST, // TextureFilter.PointMipLinear
(int) GLenum.GL_LINEAR, // TextureFilter.MinLinearMagPointMipLinear
(int) GLenum.GL_LINEAR, // TextureFilter.MinLinearMagPointMipPoint
(int) GLenum.GL_NEAREST, // TextureFilter.MinPointMagLinearMipLinear
(int) GLenum.GL_NEAREST // TextureFilter.MinPointMagLinearMipPoint
};
public static readonly GLenum[] DepthStencilAttachment = new GLenum[]
{
GLenum.GL_ZERO, // NOPE
GLenum.GL_DEPTH_ATTACHMENT, // DepthFormat.Depth16
GLenum.GL_DEPTH_ATTACHMENT, // DepthFormat.Depth24
GLenum.GL_DEPTH_STENCIL_ATTACHMENT // DepthFormat.Depth24Stencil8
};
public static readonly GLenum[] DepthStorage = new GLenum[]
{
GLenum.GL_ZERO, // NOPE
GLenum.GL_DEPTH_COMPONENT16, // DepthFormat.Depth16
GLenum.GL_DEPTH_COMPONENT24, // DepthFormat.Depth24
GLenum.GL_DEPTH24_STENCIL8 // DepthFormat.Depth24Stencil8
};
public static readonly float[] DepthBiasScale = new float[]
{
0.0f, // DepthFormat.None
(float) ((1 << 16) - 1), // DepthFormat.Depth16
(float) ((1 << 24) - 1), // DepthFormat.Depth24
(float) ((1 << 24) - 1) // DepthFormat.Depth24Stencil8
};
public static readonly MojoShader.MOJOSHADER_usage[] VertexAttribUsage = new MojoShader.MOJOSHADER_usage[]
{
MojoShader.MOJOSHADER_usage.MOJOSHADER_USAGE_POSITION, // VertexElementUsage.Position
MojoShader.MOJOSHADER_usage.MOJOSHADER_USAGE_COLOR, // VertexElementUsage.Color
MojoShader.MOJOSHADER_usage.MOJOSHADER_USAGE_TEXCOORD, // VertexElementUsage.TextureCoordinate
MojoShader.MOJOSHADER_usage.MOJOSHADER_USAGE_NORMAL, // VertexElementUsage.Normal
MojoShader.MOJOSHADER_usage.MOJOSHADER_USAGE_BINORMAL, // VertexElementUsage.Binormal
MojoShader.MOJOSHADER_usage.MOJOSHADER_USAGE_TANGENT, // VertexElementUsage.Tangent
MojoShader.MOJOSHADER_usage.MOJOSHADER_USAGE_BLENDINDICES, // VertexElementUsage.BlendIndices
MojoShader.MOJOSHADER_usage.MOJOSHADER_USAGE_BLENDWEIGHT, // VertexElementUsage.BlendWeight
MojoShader.MOJOSHADER_usage.MOJOSHADER_USAGE_DEPTH, // VertexElementUsage.Depth
MojoShader.MOJOSHADER_usage.MOJOSHADER_USAGE_FOG, // VertexElementUsage.Fog
MojoShader.MOJOSHADER_usage.MOJOSHADER_USAGE_POINTSIZE, // VertexElementUsage.PointSize
MojoShader.MOJOSHADER_usage.MOJOSHADER_USAGE_SAMPLE, // VertexElementUsage.Sample
MojoShader.MOJOSHADER_usage.MOJOSHADER_USAGE_TESSFACTOR // VertexElementUsage.TessellateFactor
};
public static readonly int[] VertexAttribSize = new int[]
{
1, // VertexElementFormat.Single
2, // VertexElementFormat.Vector2
3, // VertexElementFormat.Vector3
4, // VertexElementFormat.Vector4
4, // VertexElementFormat.Color
4, // VertexElementFormat.Byte4
2, // VertexElementFormat.Short2
4, // VertexElementFormat.Short4
2, // VertexElementFormat.NormalizedShort2
4, // VertexElementFormat.NormalizedShort4
2, // VertexElementFormat.HalfVector2
4 // VertexElementFormat.HalfVector4
};
public static readonly GLenum[] VertexAttribType = new GLenum[]
{
GLenum.GL_FLOAT, // VertexElementFormat.Single
GLenum.GL_FLOAT, // VertexElementFormat.Vector2
GLenum.GL_FLOAT, // VertexElementFormat.Vector3
GLenum.GL_FLOAT, // VertexElementFormat.Vector4
GLenum.GL_UNSIGNED_BYTE, // VertexElementFormat.Color
GLenum.GL_UNSIGNED_BYTE, // VertexElementFormat.Byte4
GLenum.GL_SHORT, // VertexElementFormat.Short2
GLenum.GL_SHORT, // VertexElementFormat.Short4
GLenum.GL_SHORT, // VertexElementFormat.NormalizedShort2
GLenum.GL_SHORT, // VertexElementFormat.NormalizedShort4
GLenum.GL_HALF_FLOAT, // VertexElementFormat.HalfVector2
GLenum.GL_HALF_FLOAT // VertexElementFormat.HalfVector4
};
public static bool VertexAttribNormalized(VertexElement element)
{
return ( element.VertexElementUsage == VertexElementUsage.Color ||
element.VertexElementFormat == VertexElementFormat.NormalizedShort2 ||
element.VertexElementFormat == VertexElementFormat.NormalizedShort4 );
}
public static readonly GLenum[] IndexType = new GLenum[]
{
GLenum.GL_UNSIGNED_SHORT, // IndexElementSize.SixteenBits
GLenum.GL_UNSIGNED_INT // IndexElementSize.ThirtyTwoBits
};
public static readonly int[] IndexSize = new int[]
{
2, // IndexElementSize.SixteenBits
4 // IndexElementSize.ThirtyTwoBits
};
public static readonly GLenum[] Primitive = new GLenum[]
{
GLenum.GL_TRIANGLES, // PrimitiveType.TriangleList
GLenum.GL_TRIANGLE_STRIP, // PrimitiveType.TriangleStrip
GLenum.GL_LINES, // PrimitiveType.LineList
GLenum.GL_LINE_STRIP, // PrimitiveType.LineStrip
GLenum.GL_POINTS // PrimitiveType.PointListEXT
};
public static int PrimitiveVerts(PrimitiveType primitiveType, int primitiveCount)
{
switch (primitiveType)
{
case PrimitiveType.TriangleList:
return primitiveCount * 3;
case PrimitiveType.TriangleStrip:
return primitiveCount + 2;
case PrimitiveType.LineList:
return primitiveCount * 2;
case PrimitiveType.LineStrip:
return primitiveCount + 1;
case PrimitiveType.PointListEXT:
return primitiveCount;
}
throw new NotSupportedException();
}
}
#endregion
#region The Faux-Backbuffer
private bool UseFauxBackbuffer(PresentationParameters presentationParameters, DisplayMode mode)
{
int drawX, drawY;
SDL.SDL_GL_GetDrawableSize(
presentationParameters.DeviceWindowHandle,
out drawX,
out drawY
);
bool displayMismatch = ( drawX != presentationParameters.BackBufferWidth ||
drawY != presentationParameters.BackBufferHeight );
return displayMismatch || (presentationParameters.MultiSampleCount > 0);
}
private class OpenGLBackbuffer : IGLBackbuffer
{
public uint Handle
{
get;
private set;
}
public int Width
{
get;
private set;
}
public int Height
{
get;
private set;
}
public DepthFormat DepthFormat
{
get;
private set;
}
public int MultiSampleCount
{
get;
private set;
}
public uint Texture;
private uint colorAttachment;
private uint depthStencilAttachment;
private OpenGLDevice glDevice;
public OpenGLBackbuffer(
OpenGLDevice device,
int width,
int height,
DepthFormat depthFormat,
int multiSampleCount
) {
Width = width;
Height = height;
glDevice = device;
DepthFormat = depthFormat;
MultiSampleCount = multiSampleCount;
Texture = 0;
// Generate and bind the FBO.
uint handle;
glDevice.glGenFramebuffers(1, out handle);
Handle = handle;
glDevice.BindFramebuffer(Handle);
// Create and attach the color buffer
glDevice.glGenRenderbuffers(1, out colorAttachment);
glDevice.glBindRenderbuffer(GLenum.GL_RENDERBUFFER, colorAttachment);
if (multiSampleCount > 0)
{
glDevice.glRenderbufferStorageMultisample(
GLenum.GL_RENDERBUFFER,
multiSampleCount,
GLenum.GL_RGBA8,
width,
height
);
}
else
{
glDevice.glRenderbufferStorage(
GLenum.GL_RENDERBUFFER,
GLenum.GL_RGBA8,
width,
height
);
}
glDevice.glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0,
GLenum.GL_RENDERBUFFER,
colorAttachment
);
if (depthFormat == DepthFormat.None)
{
// Don't bother creating a depth/stencil buffer.
depthStencilAttachment = 0;
// Keep this state sane.
glDevice.glBindRenderbuffer(GLenum.GL_RENDERBUFFER, 0);
return;
}
// Create and attach the depth/stencil buffer
glDevice.glGenRenderbuffers(1, out depthStencilAttachment);
glDevice.glBindRenderbuffer(GLenum.GL_RENDERBUFFER, depthStencilAttachment);
if (multiSampleCount > 0)
{
glDevice.glRenderbufferStorageMultisample(
GLenum.GL_RENDERBUFFER,
multiSampleCount,
XNAToGL.DepthStorage[(int) depthFormat],
width,
height
);
}
else
{
glDevice.glRenderbufferStorage(
GLenum.GL_RENDERBUFFER,
XNAToGL.DepthStorage[(int) depthFormat],
width,
height
);
}
glDevice.glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_DEPTH_ATTACHMENT,
GLenum.GL_RENDERBUFFER,
depthStencilAttachment
);
if (depthFormat == DepthFormat.Depth24Stencil8)
{
glDevice.glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_STENCIL_ATTACHMENT,
GLenum.GL_RENDERBUFFER,
depthStencilAttachment
);
}
// Keep this state sane.
glDevice.glBindRenderbuffer(GLenum.GL_RENDERBUFFER, 0);
}
public void Dispose()
{
uint handle = Handle;
glDevice.BindFramebuffer(0);
glDevice.glDeleteFramebuffers(1, ref handle);
glDevice.glDeleteRenderbuffers(1, ref colorAttachment);
if (depthStencilAttachment != 0)
{
glDevice.glDeleteRenderbuffers(1, ref depthStencilAttachment);
}
if (Texture != 0)
{
glDevice.glDeleteTextures(1, ref Texture);
}
glDevice = null;
Handle = 0;
}
public void ResetFramebuffer(
PresentationParameters presentationParameters,
bool renderTargetBound
) {
Width = presentationParameters.BackBufferWidth;
Height = presentationParameters.BackBufferHeight;
DepthFormat depthFormat = presentationParameters.DepthStencilFormat;
MultiSampleCount = presentationParameters.MultiSampleCount;
if (Texture != 0)
{
glDevice.glDeleteTextures(1, ref Texture);
Texture = 0;
}
if (renderTargetBound)
{
glDevice.glBindFramebuffer(
GLenum.GL_FRAMEBUFFER, Handle
);
}
// Detach color attachment
glDevice.glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0,
GLenum.GL_RENDERBUFFER,
0
);
// Detach depth/stencil attachment, if applicable
if (depthStencilAttachment != 0)
{
glDevice.glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_DEPTH_ATTACHMENT,
GLenum.GL_RENDERBUFFER,
0
);
if (DepthFormat == DepthFormat.Depth24Stencil8)
{
glDevice.glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_STENCIL_ATTACHMENT,
GLenum.GL_RENDERBUFFER,
0
);
}
}
// Update our color attachment to the new resolution.
glDevice.glBindRenderbuffer(
GLenum.GL_RENDERBUFFER,
colorAttachment
);
if (MultiSampleCount > 0)
{
glDevice.glRenderbufferStorageMultisample(
GLenum.GL_RENDERBUFFER,
MultiSampleCount,
GLenum.GL_RGBA8,
Width,
Height
);
}
else
{
glDevice.glRenderbufferStorage(
GLenum.GL_RENDERBUFFER,
GLenum.GL_RGBA8,
Width,
Height
);
}
glDevice.glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_COLOR_ATTACHMENT0,
GLenum.GL_RENDERBUFFER,
colorAttachment
);
// Generate/Delete depth/stencil attachment, if needed
if (depthFormat == DepthFormat.None)
{
if (depthStencilAttachment != 0)
{
glDevice.glDeleteRenderbuffers(
1,
ref depthStencilAttachment
);
depthStencilAttachment = 0;
}
}
else if (depthStencilAttachment == 0)
{
glDevice.glGenRenderbuffers(
1,
out depthStencilAttachment
);
}
// Update the depth/stencil buffer, if applicable
if (depthStencilAttachment != 0)
{
glDevice.glBindRenderbuffer(
GLenum.GL_RENDERBUFFER,
depthStencilAttachment
);
if (MultiSampleCount > 0)
{
glDevice.glRenderbufferStorageMultisample(
GLenum.GL_RENDERBUFFER,
MultiSampleCount,
XNAToGL.DepthStorage[(int)depthFormat],
Width,
Height
);
}
else
{
glDevice.glRenderbufferStorage(
GLenum.GL_RENDERBUFFER,
XNAToGL.DepthStorage[(int)depthFormat],
Width,
Height
);
}
glDevice.glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_DEPTH_ATTACHMENT,
GLenum.GL_RENDERBUFFER,
depthStencilAttachment
);
if (depthFormat == DepthFormat.Depth24Stencil8)
{
glDevice.glFramebufferRenderbuffer(
GLenum.GL_FRAMEBUFFER,
GLenum.GL_STENCIL_ATTACHMENT,
GLenum.GL_RENDERBUFFER,
depthStencilAttachment
);
}