public MappedResource MapSubresource(GraphicsResource resource, int subResourceIndex, MapMode mapMode, bool doNotWait = false, int offsetInBytes = 0, int lengthInBytes = 0)
        {
#if DEBUG
            GraphicsDevice.EnsureContextActive();
#endif

            // This resource has just been recycled by the GraphicsResourceAllocator, we force a rename to avoid GPU=>GPU sync point
            if (resource.DiscardNextMap && mapMode == MapMode.WriteNoOverwrite)
                mapMode = MapMode.WriteDiscard;


            var buffer = resource as Buffer;
            if (buffer != null)
            {
                if (lengthInBytes == 0)
                    lengthInBytes = buffer.Description.SizeInBytes;

                if (buffer.StagingData != IntPtr.Zero)
                {
                    // Specific case for constant buffers
                    return new MappedResource(resource, subResourceIndex, new DataBox { DataPointer = buffer.StagingData + offsetInBytes, SlicePitch = 0, RowPitch = 0 }, offsetInBytes, lengthInBytes);
                }

#if SILICONSTUDIO_XENKO_GRAPHICS_API_OPENGLES
                // OpenGL ES 2 needs Staging Data
                if (GraphicsDevice.IsOpenGLES2)
                {
                    Internal.Refactor.ThrowNotImplementedException();
                }
#endif

                IntPtr mapResult = IntPtr.Zero;

                //UnbindVertexArrayObject();
                GL.BindBuffer(buffer.BufferTarget, buffer.BufferId);

#if !SILICONSTUDIO_XENKO_GRAPHICS_API_OPENGLES
                //if (mapMode != MapMode.WriteDiscard && mapMode != MapMode.WriteNoOverwrite)
                //    mapResult = GL.MapBuffer(buffer.bufferTarget, mapMode.ToOpenGL());
                //else
#endif
                {
                    // Orphan the buffer (let driver knows we don't need it anymore)
                    if (mapMode == MapMode.WriteDiscard)
                    {
                        doNotWait = true;
                        GL.BufferData(buffer.BufferTarget, (IntPtr)buffer.Description.SizeInBytes, IntPtr.Zero, buffer.BufferUsageHint);
                    }

                    var unsynchronized = doNotWait && mapMode != MapMode.Read && mapMode != MapMode.ReadWrite;

                    mapResult = GL.MapBufferRange(buffer.BufferTarget, (IntPtr)offsetInBytes, (IntPtr)lengthInBytes, mapMode.ToOpenGLMask() | (unsynchronized ? BufferAccessMask.MapUnsynchronizedBit : 0));
                }

                return new MappedResource(resource, subResourceIndex, new DataBox { DataPointer = mapResult, SlicePitch = 0, RowPitch = 0 });
            }

            var texture = resource as Texture;
            if (texture != null)
            {
                if (lengthInBytes == 0)
                    lengthInBytes = texture.ComputeSubresourceSize(subResourceIndex);

                if (mapMode == MapMode.Read)
                {
                    if (texture.Description.Usage != GraphicsResourceUsage.Staging)
                        throw new NotSupportedException("Only staging textures can be mapped.");

                    var mipLevel = subResourceIndex % texture.MipLevels;

#if SILICONSTUDIO_XENKO_GRAPHICS_API_OPENGLES
                    if (GraphicsDevice.IsOpenGLES2 || texture.StagingData != IntPtr.Zero)
                    {
                        return new MappedResource(resource, subResourceIndex, new DataBox { DataPointer = texture.StagingData + offsetInBytes + texture.ComputeBufferOffset(subResourceIndex, 0), SlicePitch = texture.ComputeSlicePitch(mipLevel), RowPitch = texture.ComputeRowPitch(mipLevel) }, offsetInBytes, lengthInBytes);
                    }
                    else
#endif
                    {
                        if (doNotWait)
                        {
                            // Wait at least 2 frames after last operation
                            if (GraphicsDevice.FrameCounter < texture.PixelBufferFrame + ReadbackFrameDelay)
                            {
                                return new MappedResource(resource, subResourceIndex, new DataBox(), offsetInBytes, lengthInBytes);
                            }
                        }

                        return MapTexture(texture, true, BufferTarget.PixelPackBuffer, texture.PixelBufferObjectId, subResourceIndex, mapMode, offsetInBytes, lengthInBytes);
                    }
                }
                else if (mapMode == MapMode.WriteDiscard)
                {
#if SILICONSTUDIO_XENKO_GRAPHICS_API_OPENGLES
                    if (GraphicsDevice.IsOpenGLES2)
                    {
                        Internal.Refactor.ThrowNotImplementedException();
                    }
#endif
                    if (texture.Description.Usage != GraphicsResourceUsage.Dynamic)
                        throw new NotSupportedException("Only dynamic texture can be mapped.");

                    // Create a temporary unpack pixel buffer
                    // TODO: Pool/allocator? (it's an upload buffer basically)
                    var pixelBufferObjectId = texture.GeneratePixelBufferObject(BufferTarget.PixelUnpackBuffer, PixelStoreParameter.UnpackAlignment, BufferUsageHint.DynamicCopy, texture.ComputeSubresourceSize(subResourceIndex));

                    return MapTexture(texture, false, BufferTarget.PixelUnpackBuffer, pixelBufferObjectId, subResourceIndex, mapMode, offsetInBytes, lengthInBytes);
                }
            }

            throw Internal.Refactor.NewNotImplementedException("MapSubresource not implemented for type " + resource.GetType());
        }
        internal void UpdateSubresource(GraphicsResource resource, int subResourceIndex, DataBox databox)
        {
#if DEBUG
            GraphicsDevice.EnsureContextActive();
#endif
            var buffer = resource as Buffer;
            if (buffer != null)
            {
#if SILICONSTUDIO_XENKO_GRAPHICS_API_OPENGLES
                if (buffer.StagingData != IntPtr.Zero)
                {
                    // Specific case for constant buffers
                    SiliconStudio.Core.Utilities.CopyMemory(buffer.StagingData, databox.DataPointer, buffer.Description.SizeInBytes);
                    return;
                }
#endif

                //UnbindVertexArrayObject();

                if (!GraphicsDevice.HasTextureBuffers && buffer.BufferId == 0)
                {
                    if (activeTexture != 0)
                    {
                        activeTexture = 0;
                        GL.ActiveTexture(TextureUnit.Texture0);
                    }

                    // On platforms where it's not supported, we use a texture instead of a buffer
                    GL.BindTexture(buffer.TextureTarget, buffer.TextureId);
                    boundShaderResourceViews[0] = null; // bound active texture 0 has changed

                    buffer.UpdateTextureSubresource(databox.DataPointer, 0, 0, buffer.ElementCount);
                }
                else
                {
                    GL.BindBuffer(buffer.BufferTarget, buffer.BufferId);
                    GL.BufferData(buffer.BufferTarget, buffer.Description.SizeInBytes, databox.DataPointer, buffer.BufferUsageHint);
                }
            }
            else
            {
                var texture = resource as Texture;
                if (texture != null)
                {
                    if (activeTexture != 0)
                    {
                        activeTexture = 0;
                        GL.ActiveTexture(TextureUnit.Texture0);
                    }

                    // TODO: Handle pitchs
                    // TODO: handle other texture formats
                    GL.BindTexture(texture.TextureTarget, texture.TextureId);
                    boundShaderResourceViews[0] = null; // bound active texture 0 has changed

                    var desc = texture.Description;
                    var mipLevel = subResourceIndex % texture.MipLevels;
                    var arraySlice = subResourceIndex / texture.MipLevels;
                    switch (texture.TextureTarget)
                    {
#if !SILICONSTUDIO_XENKO_GRAPHICS_API_OPENGLES
                        case TextureTarget.Texture1D:
                            GL.TexSubImage1D(TextureTarget.Texture1D, mipLevel, 0, desc.Width, texture.TextureFormat, texture.TextureType, databox.DataPointer);
                            break;
#endif
                        case TextureTarget.Texture2D:
                            GL.TexSubImage2D(TextureTarget2d.Texture2D, mipLevel, 0, 0, desc.Width, desc.Height, texture.TextureFormat, texture.TextureType, databox.DataPointer);
                            break;
                        case TextureTarget.Texture2DArray:
                            GL.TexSubImage3D(TextureTarget3d.Texture2DArray, mipLevel, 0, 0, arraySlice, desc.Width, desc.Height, 1, texture.TextureFormat, texture.TextureType, databox.DataPointer);
                            break;
                        case TextureTarget.Texture3D:
                            GL.TexSubImage3D(TextureTarget3d.Texture3D, mipLevel, 0, 0, 0, desc.Width, desc.Height, desc.Depth, texture.TextureFormat, texture.TextureType, databox.DataPointer);
                            break;
                        case TextureTarget.TextureCubeMap:
                            GL.TexSubImage2D(Texture.GetTextureTargetForDataSet2D(texture.TextureTarget, arraySlice), mipLevel, 0, 0, desc.Width, desc.Height, texture.TextureFormat, texture.TextureType, databox.DataPointer);
                            break;
                        default:
                            Internal.Refactor.ThrowNotImplementedException("UpdateSubresource not implemented for texture target " + texture.TextureTarget);
                            break;
                    }
                }
                else // neither texture nor buffer
                {
                    Internal.Refactor.ThrowNotImplementedException("UpdateSubresource not implemented for type " + resource.GetType());
                }
            }
        }