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());
        }
        private MappedResource MapTexture(Texture texture, bool adjustOffsetForSubresource, BufferTarget bufferTarget, int pixelBufferObjectId, int subResourceIndex, MapMode mapMode, int offsetInBytes, int lengthInBytes)
        {
            int mipLevel = subResourceIndex % texture.MipLevels;

            GL.BindBuffer(bufferTarget, pixelBufferObjectId);
            var mapResult = GL.MapBufferRange(bufferTarget, (IntPtr)offsetInBytes + (adjustOffsetForSubresource ? texture.ComputeBufferOffset(subResourceIndex, 0) : 0), (IntPtr)lengthInBytes, mapMode.ToOpenGLMask());
            GL.BindBuffer(bufferTarget, 0);

            return new MappedResource(texture, subResourceIndex, new DataBox { DataPointer = mapResult, SlicePitch = texture.ComputeSlicePitch(mipLevel), RowPitch = texture.ComputeRowPitch(mipLevel) }, offsetInBytes, lengthInBytes)
            {
                PixelBufferObjectId = pixelBufferObjectId,
            };
        }