internal unsafe static void SetBodyLinearVelocity(PhysicsBodyHandle body, Vector3 velocity)
        {
            AlignedAllocation <Vector4> vec4Aligned = new AlignedAllocation <Vector4>(16L, (uint)sizeof(Vector4));

            *((Vector4 *)vec4Aligned.AlignedPointer) = velocity;

            // WARNING: No longer thread-safe
            try {
                unsafe {
                    char *failReason = stackalloc char[InteropUtils.MAX_INTEROP_FAIL_REASON_STRING_LENGTH + 1];
                    bool  success    = NativeMethods.PhysicsManager_SetBodyLinearVelocity((IntPtr)failReason, body, vec4Aligned.AlignedPointer);
                    if (!success)
                    {
                        throw new NativeOperationFailedException(Marshal.PtrToStringUni((IntPtr)failReason));
                    }
                }
            }
            finally {
                vec4Aligned.Dispose();
            }
        }
示例#2
0
 public void SetPhysicsShape(
     PhysicsShapeHandle shapeHandle,
     Vector3 physicsShapeOffset,
     float mass,
     bool forceIntransigence             = false,
     bool disablePerformanceDeactivation = false,
     bool collideOnlyWithWorld           = false,
     bool collideWithOnlyDynamics        = false,
     float restitution     = PhysicsManager.DEFAULT_RESTITUTION,
     float linearDamping   = PhysicsManager.DEFAULT_LINEAR_DAMPING,
     float angularDamping  = PhysicsManager.DEFAULT_ANGULAR_DAMPING,
     float friction        = PhysicsManager.DEFAULT_FRICTION,
     float rollingFriction = PhysicsManager.DEFAULT_ROLLING_FRICTION)
 {
     LosgapSystem.InvokeOnMaster(() => {                     // Anti-deadlock measures x.x
         lock (InstanceMutationLock) {
             if (physicsBody != PhysicsBodyHandle.NULL)
             {
                 physicsBody.Dispose();
             }
             if (this.physicsShapeOffset == null)
             {
                 this.physicsShapeOffset = new AlignedAllocation <Vector4>(TRANSFORM_ALIGNMENT, (uint)sizeof(Vector4));
             }
             this.physicsShapeOffset.Value.Write(physicsShapeOffset);
             physicsBody = PhysicsManager.CreateRigidBody(
                 shapeHandle,
                 mass,
                 disablePerformanceDeactivation,
                 forceIntransigence,
                 collideOnlyWithWorld,
                 collideWithOnlyDynamics,
                 transform.AlignedPointer + 32,
                 transform.AlignedPointer + 16,
                 this.physicsShapeOffset.Value.AlignedPointer
                 );
         }
     });
     SetPhysicsProperties(restitution, linearDamping, angularDamping, friction, rollingFriction);
 }
        internal static unsafe void RayTestAllLessGarbage(Vector3 startPoint, Vector3 endPoint, uint maxCollisions, List <RayTestCollisionDesc> reusableResultsList)
        {
            RayTestCollisionDesc *collisionArr      = stackalloc RayTestCollisionDesc[(int)maxCollisions];
            uint maxCollisionsLocal                 = maxCollisions;
            RayTestCollisionDesc *collisionArrLocal = collisionArr;
            uint outNumCollisions;

            AlignedAllocation <Vector4> startPointAligned = new AlignedAllocation <Vector4>(16L, (uint)sizeof(Vector4));

            *((Vector4 *)startPointAligned.AlignedPointer) = startPoint;
            AlignedAllocation <Vector4> endPointAligned = new AlignedAllocation <Vector4>(16L, (uint)sizeof(Vector4));

            *((Vector4 *)endPointAligned.AlignedPointer) = endPoint;

            // WARNING: No longer thread-safe
            unsafe {
                char *failReason = stackalloc char[InteropUtils.MAX_INTEROP_FAIL_REASON_STRING_LENGTH + 1];
                bool  success    = NativeMethods.PhysicsManager_RayTestAll(
                    (IntPtr)failReason,
                    startPointAligned.AlignedPointer,
                    endPointAligned.AlignedPointer,
                    (IntPtr)collisionArrLocal,
                    maxCollisionsLocal,
                    (IntPtr)(&outNumCollisions)
                    );
                if (!success)
                {
                    throw new NativeOperationFailedException(Marshal.PtrToStringUni((IntPtr)failReason));
                }
            }

            startPointAligned.Dispose();
            endPointAligned.Dispose();

            reusableResultsList.Clear();
            for (uint i = 0U; i < outNumCollisions; ++i)
            {
                reusableResultsList.Add(collisionArr[i]);
            }
        }
 internal GeometryCache(IVertexBuffer[] vertexComponentBuffers, string[] vertexComponentSemantics, ResourceFormat[] vertexComponentFormats,
                        IndexBuffer indices, AlignedAllocation <uint> componentStartPointsAlloc, AlignedAllocation <uint> indexStartPointsAlloc, uint numModels,
                        Type vertexType, int cacheID, Dictionary <string, ModelHandle> nameToHandleMap, bool orderFirst)
 {
     Assure.NotNull(vertexComponentBuffers);
     Assure.NotNull(vertexComponentSemantics);
     Assure.NotNull(vertexComponentFormats);
     Assure.NotNull(indices);
     Assure.Equal(vertexComponentBuffers.Length, vertexComponentSemantics.Length, "One or more vertex component arrays have different lengths.");
     Assure.Equal(vertexComponentFormats.Length, vertexComponentSemantics.Length, "One or more vertex component arrays have different lengths.");
     Assure.GreaterThan(vertexComponentBuffers.Length, 0, "Geometry cache with no vertex buffers is invalid.");
     Assure.NotNull(nameToHandleMap);
     this.vertexComponentBuffers   = vertexComponentBuffers;
     this.vertexComponentSemantics = vertexComponentSemantics;
     this.vertexComponentFormats   = vertexComponentFormats;
     this.indices = indices;
     this.componentStartPointsAlloc = componentStartPointsAlloc;
     this.indexStartPointsAlloc     = indexStartPointsAlloc;
     this.componentStartPoints      = (uint *)this.componentStartPointsAlloc.AlignedPointer;
     this.indexStartPoints          = (uint *)this.indexStartPointsAlloc.AlignedPointer;
     this.NumModels       = numModels;
     this.VertexType      = vertexType;
     this.ID              = cacheID;
     this.nameToHandleMap = nameToHandleMap;
     lock (staticMutationLock) {
         activeCaches.Add(ID, this);
         if (orderFirst)
         {
             activeCacheList.Insert(0, this);
         }
         else
         {
             activeCacheList.Add(this);
         }
     }
     this.createInputLayoutFunc = CreateInputLayout;
     GC.AddMemoryPressure(sizeof(uint) * 2 * (NumModels + 1));
 }
 public static unsafe PhysicsShapeHandle CreateCompoundCurveShape(IEnumerable <Vector3> vertices, CollisionShapeOptionsDesc shapeOptions)
 {
     return(LosgapSystem.InvokeOnMaster(() => {
         AlignedAllocation <CollisionShapeOptionsDesc> shapeOptionsAligned = new AlignedAllocation <CollisionShapeOptionsDesc>(16L, (uint)sizeof(CollisionShapeOptionsDesc));
         *((CollisionShapeOptionsDesc *)shapeOptionsAligned.AlignedPointer) = shapeOptions;
         Vector3 *verticesLocal = stackalloc Vector3[vertices.Count()];
         int numVertices = 0;
         foreach (Vector3 vertex in vertices)
         {
             verticesLocal[numVertices++] = vertex;
         }
         PhysicsShapeHandle result;
         InteropUtils.CallNative(
             NativeMethods.PhysicsManager_CreateCompoundCurveShape,
             (IntPtr)verticesLocal,
             (uint)numVertices / 8U,
             shapeOptionsAligned.AlignedPointer,
             (IntPtr)(&result)
             ).ThrowOnFailure();
         shapeOptionsAligned.Dispose();
         return result;
     }));
 }
        internal static unsafe PhysicsBodyHandle RayTestNearest(Vector3 startPoint, Vector3 endPoint, out Vector3 hitPoint)
        {
            AlignedAllocation <Vector4> hitPointAligned = new AlignedAllocation <Vector4>(16L, (uint)sizeof(Vector4));
            Vector4 *hitPoint4Ptr = (Vector4 *)hitPointAligned.AlignedPointer;
            var      result       = LosgapSystem.InvokeOnMaster(() => {
                AlignedAllocation <Vector4> startPointAligned  = new AlignedAllocation <Vector4>(16L, (uint)sizeof(Vector4));
                *((Vector4 *)startPointAligned.AlignedPointer) = startPoint;
                AlignedAllocation <Vector4> endPointAligned    = new AlignedAllocation <Vector4>(16L, (uint)sizeof(Vector4));
                *((Vector4 *)endPointAligned.AlignedPointer)   = endPoint;
                Vector4 *hitPoint4PtrLocal = hitPoint4Ptr;
                PhysicsBodyHandle outPBH;
                InteropUtils.CallNative(NativeMethods.PhysicsManager_RayTestNearest,
                                        startPointAligned.AlignedPointer,
                                        endPointAligned.AlignedPointer,
                                        (IntPtr)(&outPBH),
                                        (IntPtr)(hitPoint4PtrLocal)
                                        ).ThrowOnFailure();
                startPointAligned.Dispose();
                endPointAligned.Dispose();
                return(outPBH);
            });

            try {
                if (result != PhysicsBodyHandle.NULL)
                {
                    hitPoint = (Vector3)(*((Vector4 *)hitPointAligned.AlignedPointer));
                }
                else
                {
                    hitPoint = Vector3.ZERO;
                }
                return(result);
            }
            finally {
                hitPointAligned.Dispose();
            }
        }
        public void TestReadAndWrite()
        {
            // Define variables and constants
            const int NUM_ALLOCS = 200;

            AlignedAllocation <Matrix>[] allocs = new AlignedAllocation <Matrix> [NUM_ALLOCS];

            // Set up context
            for (int i = 0; i < allocs.Length; i++)
            {
                allocs[i] = new AlignedAllocation <Matrix>(16L);
            }

            // Execute
            for (int i = 0; i < allocs.Length; i++)
            {
                allocs[i].Write(new Matrix(
                                    i, i * 2, i * 3, i * 4,
                                    i * 11, i * 12, i * 13, i * 14,
                                    i * 21, i * 22, i * 23, i * 24,
                                    i * 31, i * 32, i * 33, i * 34
                                    ));
            }

            // Assert outcome
            for (int i = 0; i < allocs.Length; i++)
            {
                Assert.AreEqual(new Matrix(
                                    i, i * 2, i * 3, i * 4,
                                    i * 11, i * 12, i * 13, i * 14,
                                    i * 21, i * 22, i * 23, i * 24,
                                    i * 31, i * 32, i * 33, i * 34
                                    ), allocs[i]);
            }

            allocs.ForEach(alloc => alloc.Dispose());
        }
        public void TestAlignment()
        {
            // Define variables and constants
            const int NUM_ALLOCATIONS_PER_TEST_ALIGNMENT = 350;

            int[] testAlignments = { 1, 2, 3, 4, 6, 8, 12, 16, 32 };

            // Set up context


            // Execute


            // Assert outcome
            for (int i = 0; i < testAlignments.Length; i++)
            {
                for (int j = 0; j < NUM_ALLOCATIONS_PER_TEST_ALIGNMENT; j++)
                {
                    AlignedAllocation <Matrix> alignedAlloc = new AlignedAllocation <Matrix>(testAlignments[i]);
                    Assert.IsTrue(((long)alignedAlloc.AlignedPointer) % testAlignments[i] == 0L);
                    alignedAlloc.Dispose();
                }
            }
        }
示例#9
0
        /// <summary>
        /// Builds the <see cref="GeometryCache"/> with all the models that have previously been added with <see cref="AddModel"/>.
        /// This method may only be called once per GeometryCacheBuilder.
        /// </summary>
        /// <returns>A new <see cref="GeometryCache"/> containing 'baked' resource data for all the loaded models and an
        /// "instance pool", allowing model instances to be created and loaded in to the <see cref="Scene"/>.</returns>
        public unsafe GeometryCache Build()
        {
            lock (instanceMutationLock) {
                if (isBuilt)
                {
                    throw new InvalidOperationException("Cache has already been built!");
                }

                FieldInfo[] vertexComponents = typeof(TVertex).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                if (vertexComponents.Length > VertexShader.MAX_VS_INPUT_BINDINGS)
                {
                    throw new InvalidOperationException("Vertex type '" + typeof(TVertex).Name + "' has too many components. Maximum " +
                                                        "permissible is " + VertexShader.MAX_VS_INPUT_BINDINGS + ", but the vertex type has " + vertexComponents.Length + " fields.");
                }
                else if (vertexComponents.Length == 0)
                {
                    throw new InvalidOperationException("Vertex type '" + typeof(TVertex).Name + "' has no components. Please choose a type with " +
                                                        "at least one field.");
                }

                if (vertexCounts.Count == 0)
                {
                    throw new InvalidOperationException("Can not build empty geometry cache: Please add at least one model first!");
                }

                if (modelNames.AnyDuplicates())
                {
                    throw new InvalidOperationException("No two models should have the same name.");
                }
                if (GeometryCache.CheckForModelNameClashes(modelNames))
                {
                    throw new InvalidOperationException("One or more model names added to this builder have already been used in other active geometry caches.");
                }



                IVertexBuffer[]  vertexComponentBuffers   = new IVertexBuffer[vertexComponents.Length];
                string[]         vertexComponentSemantics = new string[vertexComponents.Length];
                ResourceFormat[] vertexComponentFormats   = new ResourceFormat[vertexComponents.Length];

                for (int i = 0; i < vertexComponents.Length; ++i)
                {
                    FieldInfo component = vertexComponents[i];
                    if (!component.FieldType.IsBlittable())
                    {
                        throw new InvalidOperationException("Invalid vertex component type: '" + component.FieldType.Name + "'.");
                    }
                    if (!component.HasCustomAttribute <VertexComponentAttribute>())
                    {
                        throw new InvalidOperationException("Every field on given vertex type (" + typeof(TVertex).Name + ") must be annoted with " +
                                                            "a " + typeof(VertexComponentAttribute).Name + "!");
                    }

                    typeof(GeometryCacheBuilder <TVertex>)
                    .GetMethod("FillComponentBuffer", BindingFlags.NonPublic | BindingFlags.Instance)                             // TODO replace with nameof() operator when C#6 is released
                    .MakeGenericMethod(component.FieldType)
                    .Invoke(this, new object[] { component, i, vertexComponentBuffers, vertexComponentSemantics, vertexComponentFormats });
                }

                IndexBuffer indexBuffer = BufferFactory.NewIndexBuffer().WithInitialData(indices.ToArray()).WithUsage(ResourceUsage.Immutable);

                Assure.Equal(vertexCounts.Count, indexCounts.Count);
                AlignedAllocation <uint> componentStartPointsAlloc = AlignedAllocation <uint> .AllocArray(
                    START_POINT_ARRAY_ALIGNMENT,
                    (uint)vertexCounts.Count + 1U                      // Extra one so we can set the last value to one-past-the-end (performance improvement later on)
                    );

                AlignedAllocation <uint> indexStartPointsAlloc = AlignedAllocation <uint> .AllocArray(
                    START_POINT_ARRAY_ALIGNMENT,
                    (uint)vertexCounts.Count + 1U                      // Extra one so we can set the last value to one-past-the-end (performance improvement later on)
                    );

                uint *componentStartPtr = (uint *)componentStartPointsAlloc.AlignedPointer;
                uint *indexStartPtr     = (uint *)indexStartPointsAlloc.AlignedPointer;
                uint  vbCounter         = 0U;
                uint  ibCounter         = 0U;
                for (int i = 0; i < vertexCounts.Count; ++i)
                {
                    componentStartPtr[i] = vbCounter;
                    indexStartPtr[i]     = ibCounter;
                    vbCounter           += vertexCounts[i];
                    ibCounter           += indexCounts[i];
                }

                // Set the last two elements of each start-point array to one-past-the-last, so we don't have to test
                // for being the 'last' count later on
                componentStartPtr[vertexCounts.Count] = (uint)vertices.Count;
                indexStartPtr[vertexCounts.Count]     = (uint)indices.Count;

                Dictionary <string, ModelHandle> modelNameToHandleMap = new Dictionary <string, ModelHandle>();
                for (uint i = 0U; i < modelNames.Count; ++i)
                {
                    modelNameToHandleMap.Add(modelNames[(int)i], new ModelHandle(cacheID, i));
                }

                isBuilt = true;

                return(new GeometryCache(
                           vertexComponentBuffers,
                           vertexComponentSemantics,
                           vertexComponentFormats,
                           indexBuffer,
                           componentStartPointsAlloc,
                           indexStartPointsAlloc,
                           (uint)vertexCounts.Count,
                           typeof(TVertex),
                           cacheID,
                           modelNameToHandleMap,
                           orderFirst
                           ));
            }
        }
        public unsafe void TestGetModelBufferValues()
        {
            // Define variables and constants
            const int FAKE_CACHE_ID = 1351616;

            IVertexBuffer[] buffers = new IVertexBuffer[2];
            buffers[0] = BufferFactory.NewVertexBuffer <Vector3>()
                         .WithInitialData(new[] { Vector3.ONE * 0f, Vector3.ONE * 1f, Vector3.ONE * 2f, Vector3.ONE * 3f, Vector3.ONE * 4f, Vector3.ONE * 5f })
                         .WithUsage(ResourceUsage.Immutable)
                         .Create();
            buffers[1] = BufferFactory.NewVertexBuffer <Vector2>()
                         .WithInitialData(new[] { Vector2.ONE * 0f, Vector2.ONE * 1f, Vector2.ONE * 2f, Vector2.ONE * 3f, Vector2.ONE * 4f, Vector2.ONE * 5f })
                         .WithUsage(ResourceUsage.Immutable)
                         .Create();
            string[]         semantics = { "POSITION", "TEXCOORD" };
            ResourceFormat[] formats   = { ResourceFormat.R32G32B32Float, ResourceFormat.R32G32Float };
            IndexBuffer      indices   = BufferFactory.NewIndexBuffer()
                                         .WithInitialData(new uint[] { 0, 1, 2, 1, 2, 0, 2, 1, 0, 3, 4, 5, 4, 5, 3, 5, 3, 4, 5, 3, 4 })
                                         .WithUsage(ResourceUsage.Immutable);
            AlignedAllocation <uint> componentStartPointsAlloc = AlignedAllocation <uint> .AllocArray(7L, 3);

            *(((uint *)componentStartPointsAlloc.AlignedPointer) + 0) = 0U;
            *(((uint *)componentStartPointsAlloc.AlignedPointer) + 1) = 3U;
            *(((uint *)componentStartPointsAlloc.AlignedPointer) + 2) = 6U;
            AlignedAllocation <uint> indexStartPointsAlloc = AlignedAllocation <uint> .AllocArray(7L, 3);

            *(((uint *)indexStartPointsAlloc.AlignedPointer) + 0) = 0U;
            *(((uint *)indexStartPointsAlloc.AlignedPointer) + 1) = 9U;
            *(((uint *)indexStartPointsAlloc.AlignedPointer) + 2) = 21U;
            Dictionary <string, ModelHandle> modelNameMap = new Dictionary <string, ModelHandle>()
            {
                { FAKE_CACHE_ID + "A", new ModelHandle(FAKE_CACHE_ID, 0U) },
                { FAKE_CACHE_ID + "B", new ModelHandle(FAKE_CACHE_ID, 1U) }
            };
            const uint NUM_MODELS = 2;

            uint outVBStartIndex, outIBStartIndex, outVBCount, outIBCount;

            // Set up context
            GeometryCache cache = new GeometryCache(
                buffers, semantics, formats,
                indices, componentStartPointsAlloc, indexStartPointsAlloc,
                NUM_MODELS, typeof(FakeVertex), FAKE_CACHE_ID, modelNameMap
                );

            // Execute
            cache.GetModelBufferValues(0U, out outVBStartIndex, out outIBStartIndex, out outVBCount, out outIBCount);

            // Assert outcome
            Assert.AreEqual(0U, outVBStartIndex);
            Assert.AreEqual(0U, outIBStartIndex);
            Assert.AreEqual(3U, outVBCount);
            Assert.AreEqual(9U, outIBCount);

            cache.GetModelBufferValues(1U, out outVBStartIndex, out outIBStartIndex, out outVBCount, out outIBCount);

            Assert.AreEqual(3U, outVBStartIndex);
            Assert.AreEqual(9U, outIBStartIndex);
            Assert.AreEqual(3U, outVBCount);
            Assert.AreEqual(12U, outIBCount);

#if !DEVELOPMENT && !RELEASE
            try {
                cache.GetModelBufferValues(2U, out outVBStartIndex, out outIBStartIndex, out outVBCount, out outIBCount);
                Assert.Fail();
            }
            catch (AssuranceFailedException) { }
#endif

            cache.Dispose();
        }
        public unsafe void TestGetInputLayout()
        {
            // Define variables and constants
            const int FAKE_CACHE_ID = 1351618;

            IVertexBuffer[] buffers = new IVertexBuffer[2];
            buffers[0] = BufferFactory.NewVertexBuffer <Vector3>()
                         .WithInitialData(new[] { Vector3.ONE * 0f, Vector3.ONE * 1f, Vector3.ONE * 2f, Vector3.ONE * 3f, Vector3.ONE * 4f, Vector3.ONE * 5f })
                         .WithUsage(ResourceUsage.Immutable)
                         .Create();
            buffers[1] = BufferFactory.NewVertexBuffer <float>()
                         .WithInitialData(new[] { 0f, 1f, 2f, 3f, 4f, 5f })
                         .WithUsage(ResourceUsage.Immutable)
                         .Create();
            string[]         semantics = { "POSITION", "RANDOM_FLOATS" };
            ResourceFormat[] formats   = { ResourceFormat.R32G32B32Float, ResourceFormat.R32G32Float };
            IndexBuffer      indices   = BufferFactory.NewIndexBuffer()
                                         .WithInitialData(new uint[] { 0, 1, 2, 1, 2, 0, 2, 1, 0, 3, 4, 5, 4, 5, 3, 5, 3, 4, 5, 3, 4 })
                                         .WithUsage(ResourceUsage.Immutable);
            AlignedAllocation <uint> componentStartPointsAlloc = AlignedAllocation <uint> .AllocArray(7L, 3);

            *(((uint *)componentStartPointsAlloc.AlignedPointer) + 0) = 0U;
            *(((uint *)componentStartPointsAlloc.AlignedPointer) + 1) = 3U;
            *(((uint *)componentStartPointsAlloc.AlignedPointer) + 2) = 6U;
            AlignedAllocation <uint> indexStartPointsAlloc = AlignedAllocation <uint> .AllocArray(7L, 3);

            *(((uint *)indexStartPointsAlloc.AlignedPointer) + 0) = 0U;
            *(((uint *)indexStartPointsAlloc.AlignedPointer) + 1) = 9U;
            *(((uint *)indexStartPointsAlloc.AlignedPointer) + 2) = 21U;
            Dictionary <string, ModelHandle> modelNameMap = new Dictionary <string, ModelHandle>()
            {
                { FAKE_CACHE_ID + "A", new ModelHandle(FAKE_CACHE_ID, 0U) },
                { FAKE_CACHE_ID + "B", new ModelHandle(FAKE_CACHE_ID, 1U) }
            };
            const uint NUM_MODELS = 2;

            // Set up context
            GeometryCache cache = new GeometryCache(
                buffers, semantics, formats,
                indices, componentStartPointsAlloc, indexStartPointsAlloc,
                NUM_MODELS, typeof(Vector4), FAKE_CACHE_ID, modelNameMap
                );

            ConstantBuffer <Matrix> instanceCBuffer = BufferFactory.NewConstantBuffer <Matrix>().WithUsage(ResourceUsage.DiscardWrite);
            VertexShader            vs1             = new VertexShader(
                @"Tests\SimpleVS.cso",
                new VertexInputBinding(0U, "INSTANCE_TRANSFORM"),
                new ConstantBufferBinding(0U, "CameraTransform", instanceCBuffer),
                new VertexInputBinding(1U, "POSITION")
                );
            VertexShader vs2 = new VertexShader(
                @"Tests\SimpleVS.cso",
                new VertexInputBinding(0U, "INSTANCE_TRANSFORM"),
                new ConstantBufferBinding(0U, "CameraTransform", instanceCBuffer),
                new VertexInputBinding(1U, "RANDOM_FLOATS"),
                new VertexInputBinding(2U, "POSITION")
                );
            VertexShader vs3 = new VertexShader(
                @"Tests\SimpleVS.cso",
                new VertexInputBinding(0U, "INSTANCE"),
                new ConstantBufferBinding(0U, "VPTransform", instanceCBuffer),
                new VertexInputBinding(1U, "TEXCOORD")
                );

            // Execute
            GeometryInputLayout inputLayout1 = cache.GetInputLayout(vs1);
            GeometryInputLayout inputLayout2 = cache.GetInputLayout(vs2);

            try {
                cache.GetInputLayout(vs3);
                Assert.Fail();
            }
            catch (InvalidOperationException) { }

            // Assert outcome
            Assert.AreEqual(cache, inputLayout1.AssociatedCache);
            Assert.AreEqual(cache, inputLayout2.AssociatedCache);

            Assert.AreEqual(vs1, inputLayout1.AssociatedShader);
            Assert.AreEqual(vs2, inputLayout2.AssociatedShader);

            KeyValuePair <VertexInputBinding, IVertexBuffer>[] boundCompBuffers = inputLayout1.BoundComponentBuffers;
            Assert.AreEqual(buffers[0], boundCompBuffers.First(kvp => kvp.Key == vs1.GetBindingByIdentifier("POSITION")).Value);
            Assert.IsFalse(boundCompBuffers.Any(kvp => kvp.Value == buffers[1]));

            boundCompBuffers = inputLayout2.BoundComponentBuffers;
            Assert.AreEqual(buffers[0], boundCompBuffers.First(kvp => kvp.Key == vs2.GetBindingByIdentifier("POSITION")).Value);
            Assert.AreEqual(buffers[1], boundCompBuffers.First(kvp => kvp.Key == vs2.GetBindingByIdentifier("RANDOM_FLOATS")).Value);

            Assert.AreEqual(inputLayout1, cache.GetInputLayout(vs1));
            Assert.AreEqual(inputLayout2, cache.GetInputLayout(vs2));

            cache.Dispose();
            inputLayout2.Dispose();
            inputLayout1.Dispose();
            vs1.Dispose();
            vs2.Dispose();
            vs3.Dispose();
            instanceCBuffer.Dispose();
            indices.Dispose();
            buffers[1].Dispose();
            buffers[0].Dispose();
        }