private Number4[] CreatePixelShaderInput(ref BarycentricCoordinates coordinates)
        {
            float w = InterpolationUtility.PrecalculateW(
                coordinates.Alpha, coordinates.Beta, coordinates.Gamma,
                _p0.W, _p1.W, _p2.W);

            var v0Data = _primitive.Vertices[0].OutputData;
            var v1Data = _primitive.Vertices[1].OutputData;
            var v2Data = _primitive.Vertices[2].OutputData;

            // Calculate interpolated attribute values for this fragment.
            // TODO: Optimize this.
            var result = new Number4[_primitive.Vertices[0].OutputData.Length];

            foreach (var outputInputBinding in OutputInputBindings.Bindings)
            {
                var v0Value = v0Data[outputInputBinding.Register];
                var v1Value = v1Data[outputInputBinding.Register];
                var v2Value = v2Data[outputInputBinding.Register];

                if (outputInputBinding.SystemValueType != Name.Undefined)
                {
                    throw new NotImplementedException();
                }

                // Create input values. Normally, this will require interpolation
                // of the vertex attributes.
                Number4 inputValue;
                switch (outputInputBinding.InterpolationMode)
                {
                case InterpolationMode.Constant:
                    inputValue = v0Value;
                    break;

                case InterpolationMode.Linear:
                    inputValue = InterpolationUtility.Perspective(
                        coordinates.Alpha, coordinates.Beta, coordinates.Gamma,
                        ref v0Value, ref v1Value, ref v2Value,
                        _p0.W, _p1.W, _p2.W, w);
                    break;

                case InterpolationMode.LinearNoPerspective:
                    inputValue = InterpolationUtility.Linear(
                        coordinates.Alpha, coordinates.Beta, coordinates.Gamma,
                        ref v0Value, ref v1Value, ref v2Value);
                    break;

                default:
                    throw new InvalidOperationException("Unrecognised interpolation mode: " + outputInputBinding.InterpolationMode);
                }

                // Apply component mask so that we don't overwrite other values in this register.
                result[outputInputBinding.Register].WriteMaskedValue(
                    inputValue, outputInputBinding.ComponentMask);
            }

            return(result);
        }
        private bool IsSampleInsideTriangle(ref BarycentricCoordinates coordinates, out float depth)
        {
            // TODO: Use fill convention.

            // If any of these tests fails, the current pixel is not inside the triangle.
            if (coordinates.IsOutsideTriangle)
            {
                depth = 0;
                return(false);
            }

            // Calculate depth.
            depth = InterpolationUtility.Linear(coordinates.Alpha, coordinates.Beta, coordinates.Gamma, _p0.Z, _p1.Z, _p2.Z);

            //// Clip to near and far planes.
            //if (depth < 0 || depth > 1)
            //    return false;

            //// Clip to 0 < w.
            //// TODO: Is this right?
            //// TODO: We do the same thing later on for attribute interpolations, can it be optimised?
            //var w = InterpolationUtility.Linear(coordinates.Alpha, coordinates.Beta, coordinates.Gamma, _p0.W, _p1.W, _p2.W);
            //if (w <= 0)
            //    return false;

            // The exact value to compare against depends on fill mode - if we're rendering wireframe,
            // then check whether sample position is within the "wireframe threshold" (i.e. 1 pixel) of an edge.
            switch (RasterizerState.FillMode)
            {
            case FillMode.Solid:
                return(true);

            case FillMode.Wireframe:
                // TODO: This threshold thing is not correct.
                const float wireframeThreshold = 0.1f;
                return(coordinates.Alpha < wireframeThreshold || coordinates.Beta < wireframeThreshold || coordinates.Gamma < wireframeThreshold);

            default:
                throw new NotSupportedException();
            }
        }