public override IObservable <Mat> Process(IObservable <Mat> source)
        {
            return(Observable.Create <Mat>(observer =>
            {
                var name = MeshName;
                if (string.IsNullOrEmpty(name))
                {
                    throw new InvalidOperationException("A mesh name must be specified.");
                }

                MeshInstanced instance = null;
                return source.CombineEither(
                    ShaderManager.ReserveMaterial(ShaderName),
                    (input, material) =>
                {
                    material.Update(() =>
                    {
                        if (instance == null && input != null)
                        {
                            try
                            {
                                var mesh = material.Window.ResourceManager.Load <Mesh>(name);
                                instance = new MeshInstanced(mesh);
                                BindInstanceAttributes(
                                    instance,
                                    input.Cols * input.ElementSize,
                                    instanceAttributes);
                            }
                            catch (Exception ex)
                            {
                                observer.OnError(ex);
                                return;
                            }
                        }

                        if (instance == null)
                        {
                            return;
                        }
                        if (input != null)
                        {
                            instance.InstanceCount = VertexHelper.UpdateVertexBuffer(instance.VertexBuffer, input, Usage);
                        }
                        instance.Draw();
                    });
                    return input;
                }).Finally(() =>
                {
                    if (instance != null)
                    {
                        instance.Dispose();
                    }
                }).SubscribeSafe(observer);
            }));
        }
        static void BindInstanceAttributes(MeshInstanced instance, int stride, InstanceAttributeMappingCollection attributes)
        {
            var vertexStride     = 0;
            var mesh             = instance.InstanceMesh;
            var vertexAttributes = new List <VertexAttributeMapping>();

            GL.BindVertexArray(mesh.VertexArray);
            for (int i = 0; ; i++)
            {
                int enabled;
                GL.GetVertexAttrib(i, VertexAttribParameter.ArrayEnabled, out enabled);
                if (enabled == 0)
                {
                    break;
                }

                int size, type, normalized, vstride;
                var attribute = new VertexAttributeMapping();
                GL.GetVertexAttrib(i, VertexAttribParameter.ArraySize, out size);
                GL.GetVertexAttrib(i, VertexAttribParameter.ArrayType, out type);
                GL.GetVertexAttrib(i, VertexAttribParameter.ArrayNormalized, out normalized);
                GL.GetVertexAttrib(i, VertexAttribParameter.ArrayStride, out vstride);
                if (vertexStride == 0)
                {
                    vertexStride = vstride;
                }
                else if (vstride != vertexStride)
                {
                    throw new InvalidOperationException("Meshes with interleaved vertex buffers are not supported.");
                }

                attribute.Size       = size;
                attribute.Type       = (VertexAttribPointerType)type;
                attribute.Normalized = normalized != 0 ? true : false;
                vertexAttributes.Add(attribute);
            }

            var offset = 0;

            GL.BindVertexArray(instance.VertexArray);
            GL.BindBuffer(BufferTarget.ArrayBuffer, mesh.VertexBuffer);
            for (int i = 0; i < vertexAttributes.Count; i++)
            {
                var attribute = vertexAttributes[i];
                GL.EnableVertexAttribArray(i);
                GL.VertexAttribPointer(
                    i, attribute.Size,
                    attribute.Type,
                    attribute.Normalized,
                    vertexStride, offset);
                offset += attribute.Size * VertexHelper.GetVertexAttributeSize(attribute.Type);
            }

            offset = 0;
            GL.BindBuffer(BufferTarget.ArrayBuffer, instance.VertexBuffer);
            for (int i = 0; i < attributes.Count; i++)
            {
                var attribute = attributes[i];
                var index     = i + vertexAttributes.Count;
                GL.EnableVertexAttribArray(index);
                GL.VertexAttribPointer(
                    index, attribute.Size,
                    attribute.Type,
                    attribute.Normalized,
                    stride, offset);
                GL.VertexAttribDivisor(index, attribute.Divisor);
                offset += attribute.Size * VertexHelper.GetVertexAttributeSize(attribute.Type);
            }

            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            GL.BindVertexArray(0);
        }