public unsafe void SetShaderConstantBuffers() { ConstantBuffer <Vector4> cb0 = BufferFactory.NewConstantBuffer <Vector4>().WithUsage(ResourceUsage.DiscardWrite); ConstantBuffer <Matrix> cb1 = BufferFactory.NewConstantBuffer <Matrix>().WithUsage(ResourceUsage.DiscardWrite); Shader shader = new FragmentShader( @"Tests\SimpleFS.cso", new ConstantBufferBinding(0U, "CB0", cb0), new ConstantBufferBinding(1U, "CB1", cb1) ); RenderCommand testCommand = RenderCommand.SetShaderConstantBuffers(shader); Assert.AreEqual(RenderCommandInstruction.FSSetCBuffers, testCommand.Instruction); ResourceHandle *resHandleArray = (ResourceHandle *)new IntPtr(UnsafeUtils.Reinterpret <RenderCommandArgument, long>(testCommand.Arg1, sizeof(long))); Assert.AreEqual(cb0.ResourceHandle, resHandleArray[0]); Assert.AreEqual(cb1.ResourceHandle, resHandleArray[1]); Assert.AreEqual((RenderCommandArgument)shader.NumConstantBufferSlots, testCommand.Arg2); shader.Dispose(); cb1.Dispose(); cb0.Dispose(); #if !DEVELOPMENT && !RELEASE try { RenderCommand.SetShaderConstantBuffers(null); Assert.Fail(); } catch (AssuranceFailedException) { } try { RenderCommand.SetShaderConstantBuffers(shader); Assert.Fail(); } catch (AssuranceFailedException) { } #endif }
public void TestSettingMaterialProperties() { ConstantBuffer <Vector4> matColorBuffer = BufferFactory.NewConstantBuffer <Vector4>().WithUsage(ResourceUsage.DiscardWrite); TextureSampler textureSampler = new TextureSampler(TextureFilterType.Anisotropic, TextureWrapMode.Border, AnisotropicFilteringLevel.EightTimes); FragmentShader testFS = new FragmentShader( @"Tests\SimpleFS.cso", new ConstantBufferBinding(0U, "MaterialColor", matColorBuffer), new TextureSamplerBinding(0U, "DefaultSampler") ); Material testMaterial = new Material("Test Material", testFS); testMaterial.SetMaterialConstantValue((ConstantBufferBinding)testFS.GetBindingByIdentifier("MaterialColor"), Vector4.ONE); testMaterial.SetMaterialResource((TextureSamplerBinding)testFS.GetBindingByIdentifier("DefaultSampler"), textureSampler); #if !DEVELOPMENT && !RELEASE ConstantBufferBinding cb = new ConstantBufferBinding(1U, "Test", matColorBuffer); try { testMaterial.SetMaterialConstantValue(cb, Vector4.RIGHT); Assert.Fail(); } catch (AssuranceFailedException) { } finally { (cb as IDisposable).Dispose(); } try { testMaterial.SetMaterialConstantValue((ConstantBufferBinding)testFS.GetBindingByIdentifier("MaterialColor"), Matrix.IDENTITY); Assert.Fail(); } catch (AssuranceFailedException) { } #endif testFS.Dispose(); matColorBuffer.Dispose(); testMaterial.Dispose(); textureSampler.Dispose(); }
public void TestSetShader() { ConstantBuffer <Matrix> vpMat = BufferFactory.NewConstantBuffer <Matrix>().WithUsage(ResourceUsage.DiscardWrite); VertexShader vs = VertexShader.NewDefaultShader(vpMat); ConstantBuffer <Vector4> colorVec = BufferFactory.NewConstantBuffer <Vector4>().WithUsage(ResourceUsage.DiscardWrite); FragmentShader fs = new FragmentShader(@"Tests\SimpleFS.cso", new ConstantBufferBinding(0U, "MaterialProperties", colorVec)); RenderCommand testCommand = RenderCommand.SetShader(vs); Assert.AreEqual(RenderCommandInstruction.VSSetShader, testCommand.Instruction); Assert.AreEqual((RenderCommandArgument)(IntPtr)vs.Handle, testCommand.Arg1); testCommand = RenderCommand.SetShader(fs); Assert.AreEqual(RenderCommandInstruction.FSSetShader, testCommand.Instruction); Assert.AreEqual((RenderCommandArgument)(IntPtr)fs.Handle, testCommand.Arg1); #if !DEVELOPMENT && !RELEASE try { RenderCommand.SetShader(null); Assert.Fail(); } catch (AssuranceFailedException) { } #endif vs.Dispose(); fs.Dispose(); vpMat.Dispose(); colorVec.Dispose(); #if !DEVELOPMENT && !RELEASE try { RenderCommand.SetShader(fs); Assert.Fail(); } catch (AssuranceFailedException) { } #endif }
public static Font Load(string fontFile, FragmentShader textFS, uint?lineHeightPixels, int?kerningPixels) { Assure.NotNull(fontFile); Assure.NotNull(textFS); Assure.False(textFS.IsDisposed); if (!IOUtils.IsValidFilePath(fontFile) || !File.Exists(fontFile)) { throw new FileNotFoundException("File '" + fontFile + "' not found: Could not load font."); } XDocument fontDocument = XDocument.Load(fontFile, LoadOptions.None); XElement root = fontDocument.Root; XElement commonElement = root.Element("common"); if (commonElement == null) { throw new InvalidOperationException("Could not find common element in given font file."); } string name = Path.GetFileNameWithoutExtension(fontFile).CapitalizeFirst(); uint texWidth; uint texHeight; try { texWidth = uint.Parse(commonElement.Attribute("scaleW").Value); texHeight = uint.Parse(commonElement.Attribute("scaleH").Value); if (lineHeightPixels == null) { lineHeightPixels = uint.Parse(commonElement.Attribute("lineHeight").Value) / 2U; } } catch (Exception e) { throw new InvalidOperationException("Could not read scaleW, scaleH, or lineHeight value!", e); } XElement pagesElement = root.Element("pages"); IEnumerable <XElement> pageElements = pagesElement.Elements("page"); ITexture2D[] pageArray = new ITexture2D[pageElements.Count()]; ShaderResourceView[] characterPageViews = new ShaderResourceView[pageArray.Length]; foreach (XElement pageElement in pageElements) { int id; string filename; try { id = int.Parse(pageElement.Attribute("id").Value); filename = pageElement.Attribute("file").Value; } catch (Exception e) { throw new InvalidOperationException("Could not read page ID or filename for page " + pageElement + ".", e); } string fullFilename = Path.Combine(Path.GetDirectoryName(fontFile), filename); if (!IOUtils.IsValidFilePath(fullFilename) || !File.Exists(fullFilename)) { throw new InvalidOperationException("Page file '" + fullFilename + "' does not exist!"); } if (id < 0 || id >= pageArray.Length || pageArray[id] != null) { throw new InvalidOperationException("Invalid or duplicate page ID '" + id + "'."); } pageArray[id] = TextureFactory.LoadTexture2D() .WithFilePath(fullFilename) .WithPermittedBindings(GPUBindings.ReadableShaderResource) .WithUsage(ResourceUsage.Immutable) .Create(); characterPageViews[id] = pageArray[id].CreateView(); } GeometryCacheBuilder <DefaultVertex> characterCacheBuilder = new GeometryCacheBuilder <DefaultVertex>(); Dictionary <char, FontCharacter> charMap = new Dictionary <char, FontCharacter>(); XElement charsElement = root.Element("chars"); foreach (XElement charElement in charsElement.Elements("char")) { char unicodeValue; uint x, y, width, height; int pageID, yOffset; try { unicodeValue = (char)short.Parse(charElement.Attribute("id").Value); x = uint.Parse(charElement.Attribute("x").Value); y = uint.Parse(charElement.Attribute("y").Value); width = uint.Parse(charElement.Attribute("width").Value); height = uint.Parse(charElement.Attribute("height").Value); pageID = int.Parse(charElement.Attribute("page").Value); yOffset = int.Parse(charElement.Attribute("yoffset").Value); } catch (Exception e) { throw new InvalidOperationException("Could not acquire character ID, page ID, or dimensions for char " + charElement + ".", e); } Rectangle charMapBoundary = new Rectangle(x, y, width, height); ModelHandle modelHandle = characterCacheBuilder.AddModel( "Font_" + name + "_Character_" + unicodeValue, new[] { new DefaultVertex( new Vector3(0f, 0f, 1f), Vector3.BACKWARD, new Vector2( charMapBoundary.GetCornerX(Rectangle.RectangleCorner.BottomLeft) / texWidth, charMapBoundary.GetCornerY(Rectangle.RectangleCorner.BottomLeft) / texHeight )), new DefaultVertex( new Vector3(charMapBoundary.Width, 0f, 1f), Vector3.BACKWARD, new Vector2( charMapBoundary.GetCornerX(Rectangle.RectangleCorner.BottomRight) / texWidth, charMapBoundary.GetCornerY(Rectangle.RectangleCorner.BottomRight) / texHeight )), new DefaultVertex( new Vector3(charMapBoundary.Width, -charMapBoundary.Height, 1f), Vector3.BACKWARD, new Vector2( charMapBoundary.GetCornerX(Rectangle.RectangleCorner.TopRight) / texWidth, charMapBoundary.GetCornerY(Rectangle.RectangleCorner.TopRight) / texHeight )), new DefaultVertex( new Vector3(0f, -charMapBoundary.Height, 1f), Vector3.BACKWARD, new Vector2( charMapBoundary.GetCornerX(Rectangle.RectangleCorner.TopLeft) / texWidth, charMapBoundary.GetCornerY(Rectangle.RectangleCorner.TopLeft) / texHeight )), }, new[] { 0U, 1U, 3U, 1U, 2U, 3U } ); //yOffset = 0; //if (unicodeValue == '.') yOffset = (int) (lineHeightPixels.Value * 0.9f); charMap.Add( unicodeValue, new FontCharacter( unicodeValue, charMapBoundary, modelHandle, textFS, characterPageViews[pageID], yOffset ) ); } if (kerningPixels == null) { kerningPixels = (int)(charMap.Values.Max(value => value.Boundary.Width) * 0.15f); } uint maxCharHeight = (uint)charMap.Values.Max(fc => fc.Boundary.Height); return(new Font( name, lineHeightPixels.Value, kerningPixels.Value, characterCacheBuilder.Build(), pageArray, characterPageViews, charMap, (ConstantBufferBinding)textFS.GetBindingByIdentifier(TEXT_COLOR_SHADER_CB_NAME), maxCharHeight )); }
public void TestAllowsDuplicateSlotsForDifferentBindingTypes() { Shader shader = new FragmentShader(@"Tests\SimpleFS.cso", new TextureSamplerBinding(0U, "A"), new ResourceViewBinding(0U, "B")); shader.Dispose(); }
public void TestDisallowsDuplicateBindingSlots() { Shader shader = new FragmentShader(@"Tests\SimpleFS.cso", new TextureSamplerBinding(0U, "A"), new TextureSamplerBinding(0U, "B")); }
public void TestDisallowsDuplicateBindingNames() { Shader shader = new FragmentShader(@"Tests\SimpleFS.cso", new ResourceViewBinding(0U, "A"), new ResourceViewBinding(1U, "A")); }
private void RenderCache_IterateMaterial(int materialIndex) { // Set up context variables KeyValuePair <Material, ModelInstanceManager.MIDArray> currentKVP = currentInstanceData[materialIndex]; Material currentMaterial = currentKVP.Key; ModelInstanceManager.MIDArray currentMID = currentKVP.Value; // Skip this material if it or its shader are disposed if (currentMaterial.IsDisposed || currentMaterial.Shader.IsDisposed) { return; } // Skip this material if we're not using it bool inUse = false; for (int i = 0; i < currentMID.Length; ++i) { if (currentMID.Data[i].InUse) { inUse = true; break; } } if (!inUse) { return; } // Prepare shader according to material params, and switch to it or update it if (lastSetFragmentShader != currentMaterial.Shader || lastFrameNum != frameNum) { lastSetFragmentShader = currentMaterial.Shader; lastFrameNum = frameNum; QueueShaderSwitch(lastSetFragmentShader); } var queuedSRP = currentMaterial.FragmentShaderResourcePackage; if (lastSetFragmentShader == geomFSWithShadowSupport) { if (modifiedSRP == null) { modifiedSRP = new ShaderResourcePackage(); } modifiedSRP.CopyFrom(queuedSRP); modifiedSRP.SetValue((ResourceViewBinding)lastSetFragmentShader.GetBindingByIdentifier("ShadowMap"), previousShadowBufferSRV); queuedSRP = modifiedSRP; } QueueShaderResourceUpdate(lastSetFragmentShader, queuedSRP); // Filter & sort if (materialFilteringWorkspace == null || materialFilteringWorkspace.Length < currentCache.NumModels) { materialFilteringWorkspace = new FastClearList <Transform> [currentCache.NumModels]; for (int i = 0; i < materialFilteringWorkspace.Length; ++i) { materialFilteringWorkspace[i] = new FastClearList <Transform>(); } } for (int i = 0; i < materialFilteringWorkspace.Length; ++i) { materialFilteringWorkspace[i].Clear(); } SortByProximityToCamera(currentMID); uint numInstances = 0U; for (uint i = 0U; i < currentMID.Length; ++i) { ModelInstanceData curMID = sortedModelData[i]; if (!curMID.InUse) { continue; } SceneLayer layer = currentSceneLayers[curMID.SceneLayerIndex]; if (layer == null || !layer.GetRenderingEnabled() || !addedSceneLayers.Contains(layer)) { continue; } if (curMID.ModelIndex == __VEGG_MH.ModelIndex && currentCache.ID == __VEGG_MH.GeoCacheID) { int instanceIndex = 0; for (int j = 0; j < currentMID.Length; ++j) { if (currentMID.Data[j].Transform == curMID.Transform) { instanceIndex = j; break; } } Quaternion rot = Quaternion.IDENTITY; foreach (var kvp in __VEGG_MIH_ARR) { if (kvp.Key.InstanceIndex == instanceIndex) { rot = kvp.Value; break; } } materialFilteringWorkspace[curMID.ModelIndex].Add(curMID.Transform.RotateBy(rot)); } else { materialFilteringWorkspace[curMID.ModelIndex].Add(curMID.Transform); } ++numInstances; } // Concatenate & queue render commands if (instanceConcatWorkspace == null || instanceConcatWorkspace.Length < numInstances) { instanceConcatWorkspace = new Matrix[numInstances << 1]; // x2 so we don't create loads of garbage if the count keeps increasing by 1 } uint instanceStartOffset = RenderCache_IterateMaterial_ConcatReserve(numInstances); uint nextWorkspaceIndex = 0; uint outVBStartIndex, outIBStartIndex, outVBCount, outIBCount; for (uint mI = 0U; mI < materialFilteringWorkspace.Length; ++mI) { FastClearList <Transform> filteredTransformList = materialFilteringWorkspace[mI]; int numFilteredTransforms = filteredTransformList.Count; if (numFilteredTransforms == 0) { continue; } currentCache.GetModelBufferValues(mI, out outVBStartIndex, out outIBStartIndex, out outVBCount, out outIBCount); QueueRenderCommand(RenderCommand.DrawIndexedInstanced( (int)outVBStartIndex, outIBStartIndex, outIBCount, nextWorkspaceIndex + instanceStartOffset, (uint)numFilteredTransforms )); for (int iI = 0; iI < numFilteredTransforms; ++iI) { if (mI == __EGGHACK_MH.ModelIndex && currentCache.ID == __EGGHACK_MH.GeoCacheID) { instanceConcatWorkspace[nextWorkspaceIndex++] = filteredTransformList[iI].RotateBy(__EGGHACK_ROT).AsMatrixTransposed; } else { instanceConcatWorkspace[nextWorkspaceIndex++] = filteredTransformList[iI].AsMatrixTransposed; } } } RenderCache_IterateMaterial_Concat(instanceConcatWorkspace, instanceStartOffset, numInstances); }
public FontCharacter(char unicodeValue, Rectangle boundary, ModelHandle modelHandle, FragmentShader fragmentShader, ShaderResourceView fontMapResourceView, int yOffset) { UnicodeValue = unicodeValue; Boundary = boundary; ModelHandle = modelHandle; FragmentShader = fragmentShader; FontMapResourceView = fontMapResourceView; YOffset = yOffset; }
private void RenderCache_IterateMaterial(int materialIndex) { // Set up context variables KeyValuePair <Material, ModelInstanceManager.MIDArray> currentKVP = currentInstanceData[materialIndex]; Material currentMaterial = currentKVP.Key; ModelInstanceManager.MIDArray currentMID = currentKVP.Value; // Skip this material if it or its shader are disposed if (currentMaterial.IsDisposed || currentMaterial.Shader.IsDisposed) { return; } // Prepare shader according to material params, and switch to it or update it if (lastSetFragmentShader != currentMaterial.Shader || lastFrameNum != frameNum) { lastSetFragmentShader = currentMaterial.Shader; lastFrameNum = frameNum; QueueShaderSwitch(lastSetFragmentShader); } QueueShaderResourceUpdate(lastSetFragmentShader, currentMaterial.FragmentShaderResourcePackage); // Filter & sort if (materialFilteringWorkspace == null || materialFilteringWorkspace.Length < currentCache.NumModels) { materialFilteringWorkspace = new FastClearList <Transform> [currentCache.NumModels]; for (int i = 0; i < materialFilteringWorkspace.Length; ++i) { materialFilteringWorkspace[i] = new FastClearList <Transform>(); } } for (int i = 0; i < materialFilteringWorkspace.Length; ++i) { materialFilteringWorkspace[i].Clear(); } ModelInstanceData *midData = currentMID.Data; uint numInstances = 0U; for (uint i = 0U; i < currentMID.Length; ++i) { ModelInstanceData curMID = midData[i]; if (!curMID.InUse) { continue; } SceneLayer layer = currentSceneLayers[curMID.SceneLayerIndex]; if (layer == null || !layer.GetRenderingEnabled() || !addedSceneLayers.Contains(layer)) { continue; } materialFilteringWorkspace[curMID.ModelIndex].Add(curMID.Transform); ++numInstances; } // Concatenate & queue render commands if (instanceConcatWorkspace == null || instanceConcatWorkspace.Length < numInstances) { instanceConcatWorkspace = new Matrix[numInstances << 1]; // x2 so we don't create loads of garbage if the count keeps increasing by 1 } uint instanceStartOffset = RenderCache_IterateMaterial_ConcatReserve(numInstances); uint nextWorkspaceIndex = 0; uint outVBStartIndex, outIBStartIndex, outVBCount, outIBCount; for (uint mI = 0U; mI < materialFilteringWorkspace.Length; ++mI) { FastClearList <Transform> filteredTransformList = materialFilteringWorkspace[mI]; int numFilteredTransforms = filteredTransformList.Count; if (numFilteredTransforms == 0) { continue; } currentCache.GetModelBufferValues(mI, out outVBStartIndex, out outIBStartIndex, out outVBCount, out outIBCount); QueueRenderCommand(RenderCommand.DrawIndexedInstanced( (int)outVBStartIndex, outIBStartIndex, outIBCount, nextWorkspaceIndex + instanceStartOffset, (uint)numFilteredTransforms )); for (int iI = 0; iI < numFilteredTransforms; ++iI) { instanceConcatWorkspace[nextWorkspaceIndex++] = filteredTransformList[iI].AsMatrixTransposed; } } RenderCache_IterateMaterial_Concat(instanceConcatWorkspace, instanceStartOffset, numInstances); }
public unsafe void TestSetShaderResourceViews() { Texture2DBuilder <TexelFormat.RGBA32UInt> texBuilder = TextureFactory.NewTexture2D <TexelFormat.RGBA32UInt>() .WithWidth(100U) .WithHeight(100U) .WithUsage(ResourceUsage.DiscardWrite); Texture2D <TexelFormat.RGBA32UInt> tex0 = texBuilder.Create(); Texture2D <TexelFormat.RGBA32UInt> tex2 = texBuilder.Create(); BaseResourceView rv0 = tex0.CreateView(); BaseResourceView rv2 = tex2.CreateView(); Shader shader = new FragmentShader( @"Tests\SimpleFS.cso", new ResourceViewBinding(0U, "RV0"), new ResourceViewBinding(1U, "RV1"), new ResourceViewBinding(2U, "RV2") ); Dictionary <ResourceViewBinding, BaseResourceView> rvDict = new Dictionary <ResourceViewBinding, BaseResourceView>(); rvDict[shader.ResourceViewBindings[0]] = rv0; rvDict[shader.ResourceViewBindings[2]] = rv2; RenderCommand testCommand = RenderCommand.SetShaderResourceViews(shader, rvDict); Assert.AreEqual(RenderCommandInstruction.FSSetResources, testCommand.Instruction); ResourceViewHandle *resHandleArray = (ResourceViewHandle *)new IntPtr(UnsafeUtils.Reinterpret <RenderCommandArgument, long>(testCommand.Arg1, sizeof(long))); Assert.AreEqual(rv0.ResourceViewHandle, resHandleArray[0]); Assert.AreEqual(ResourceViewHandle.NULL, resHandleArray[1]); Assert.AreEqual(rv2.ResourceViewHandle, resHandleArray[2]); Assert.AreEqual((RenderCommandArgument)3U, testCommand.Arg2); #if !DEVELOPMENT && !RELEASE try { RenderCommand.SetShaderResourceViews(null, rvDict); Assert.Fail(); } catch (AssuranceFailedException) { } try { RenderCommand.SetShaderResourceViews(shader, null); Assert.Fail(); } catch (AssuranceFailedException) { } #endif tex0.Dispose(); tex2.Dispose(); rv0.Dispose(); rv2.Dispose(); #if !DEVELOPMENT && !RELEASE try { RenderCommand.SetShaderResourceViews(shader, rvDict); Assert.Fail(); } catch (AssuranceFailedException) { } #endif shader.Dispose(); #if !DEVELOPMENT && !RELEASE try { RenderCommand.SetShaderResourceViews(shader, new Dictionary <ResourceViewBinding, BaseResourceView>()); Assert.Fail(); } catch (AssuranceFailedException) { } #endif }
public unsafe void TestSetShaderTextureSamplers() { TextureSampler ts0 = new TextureSampler(TextureFilterType.Anisotropic, TextureWrapMode.Border, AnisotropicFilteringLevel.EightTimes); TextureSampler ts2 = new TextureSampler(TextureFilterType.Anisotropic, TextureWrapMode.Border, AnisotropicFilteringLevel.EightTimes); Shader shader = new FragmentShader( @"Tests\SimpleFS.cso", new TextureSamplerBinding(0U, "TS0"), new TextureSamplerBinding(1U, "TS1"), new TextureSamplerBinding(2U, "TS2") ); Dictionary <TextureSamplerBinding, TextureSampler> tsDict = new Dictionary <TextureSamplerBinding, TextureSampler>(); tsDict[shader.TextureSamplerBindings[0]] = ts0; tsDict[shader.TextureSamplerBindings[2]] = ts2; RenderCommand testCommand = RenderCommand.SetShaderTextureSamplers(shader, tsDict); Assert.AreEqual(RenderCommandInstruction.FSSetSamplers, testCommand.Instruction); ResourceHandle *resHandleArray = (ResourceHandle *)new IntPtr(UnsafeUtils.Reinterpret <RenderCommandArgument, long>(testCommand.Arg1, sizeof(long))); Assert.AreEqual(ts0.ResourceHandle, resHandleArray[0]); Assert.AreEqual(ResourceHandle.NULL, resHandleArray[1]); Assert.AreEqual(ts2.ResourceHandle, resHandleArray[2]); Assert.AreEqual((RenderCommandArgument)3U, testCommand.Arg2); #if !DEVELOPMENT && !RELEASE try { RenderCommand.SetShaderTextureSamplers(null, tsDict); Assert.Fail(); } catch (AssuranceFailedException) { } try { RenderCommand.SetShaderTextureSamplers(shader, null); Assert.Fail(); } catch (AssuranceFailedException) { } #endif ts0.Dispose(); ts2.Dispose(); #if !DEVELOPMENT && !RELEASE try { RenderCommand.SetShaderTextureSamplers(shader, tsDict); Assert.Fail(); } catch (AssuranceFailedException) { } #endif shader.Dispose(); #if !DEVELOPMENT && !RELEASE try { RenderCommand.SetShaderTextureSamplers(shader, new Dictionary <TextureSamplerBinding, TextureSampler>()); Assert.Fail(); } catch (AssuranceFailedException) { } #endif }
public unsafe void TestCreateAndDestroyInstance() { // Define variables and constants const int NUM_INSTANCES = 1000; var gcb = new GeometryCacheBuilder <TestVertex>(); gcb.AddModel("TCADI_a", new[] { new TestVertex(Vector3.ONE), new TestVertex(Vector3.LEFT), }, new[] { 0U, 1U, 1U, 0U, 1U, 0U }); gcb.AddModel("TCADI_b", new[] { new TestVertex(Vector3.RIGHT), new TestVertex(Vector3.UP), }, new[] { 0U, 1U, 1U, 0U, 1U, 0U }); gcb.AddModel("TCADI_c", new[] { new TestVertex(Vector3.ZERO), new TestVertex(Vector3.DOWN), }, new[] { 0U, 1U, 1U, 0U, 1U, 0U }); GeometryCache testCache = gcb.Build(); SceneLayer testLayerA = Scene.CreateLayer("Test Layer A"); SceneLayer testLayerB = Scene.CreateLayer("Test Layer B"); ConstantBuffer <Vector4> fsColorBuffer = BufferFactory.NewConstantBuffer <Vector4>().WithUsage(ResourceUsage.DiscardWrite); FragmentShader fs = new FragmentShader(@"Tests\SimpleFS.cso", new ConstantBufferBinding(0U, "MaterialProperties", fsColorBuffer)); Material testMatA = new Material("Brick", fs); Material testMatB = new Material("Wood", fs); // Set up context // Execute ModelInstanceHandle[] instanceArr = new ModelInstanceHandle[NUM_INSTANCES]; for (int i = 0; i < NUM_INSTANCES; ++i) { Transform transform = new Transform( Vector3.ONE * i, Quaternion.FromAxialRotation(Vector3.UP, i), Vector3.ONE * -i ); if (i % 5 == 0) { instanceArr[i] = testLayerA.CreateModelInstance(new ModelHandle(testCache.ID, (uint)(i % 3)), (i % 2 == 0 ? testMatA : testMatB), transform); } else { instanceArr[i] = testLayerB.CreateModelInstance(new ModelHandle(testCache.ID, (uint)(i % 3)), (i % 2 == 0 ? testMatA : testMatB), transform); } } // Assert outcome RenderingModule.RenderStateBarrier.FreezeMutations(); // Cheeky, but we have to on debug mode (and it's re-entrant for now, so no problem) var instanceData = testCache.GetModelInstanceData(); for (int i = 0; i < NUM_INSTANCES; ++i) { Material instanceMaterial = Material.GetMaterialByIndex(instanceArr[i].MaterialIndex); ModelInstanceManager.MIDArray materialDataArray = instanceData.First(kvp => kvp.Key == instanceMaterial).Value; Assert.AreEqual((i % 2 == 0 ? testMatA : testMatB), instanceMaterial); Assert.AreEqual((i % 5 == 0 ? testLayerA : testLayerB), Scene.GetLayerByIndex(materialDataArray.Data[GetMIHInstanceIndex(instanceArr[i])].SceneLayerIndex)); Assert.IsTrue(materialDataArray.Data[GetMIHInstanceIndex(instanceArr[i])].InUse); Assert.AreEqual((uint)(i % 3), materialDataArray.Data[GetMIHInstanceIndex(instanceArr[i])].ModelIndex); Assert.AreEqual( new Transform( Vector3.ONE * i, Quaternion.FromAxialRotation(Vector3.UP, i), Vector3.ONE * -i ), instanceArr[i].Transform ); Assert.AreEqual(instanceArr[i].Transform, materialDataArray.Data[GetMIHInstanceIndex(instanceArr[i])].Transform); instanceArr[i].Dispose(); Assert.IsFalse(materialDataArray.Data[GetMIHInstanceIndex(instanceArr[i])].InUse); } RenderingModule.RenderStateBarrier.UnfreezeMutations(); testCache.Dispose(); testMatA.Dispose(); testMatB.Dispose(); testLayerA.Dispose(); testLayerB.Dispose(); fs.Dispose(); fsColorBuffer.Dispose(); }
public void TestAllMethods() { // Define variables and constants ModelInstanceManager testMIM = new ModelInstanceManager(); const int NUM_ALLOCATIONS = 3000; const int NUM_MODELS = 7; const int NUM_MATERIALS = 11; const int NUM_SCENE_LAYERS = 3; ConstantBuffer <Vector4> fsCBuffer = BufferFactory.NewConstantBuffer <Vector4>().WithUsage(ResourceUsage.DiscardWrite); FragmentShader testFS = new FragmentShader(@"Tests\SimpleFS.cso", new ConstantBufferBinding(0U, "MaterialProperties", fsCBuffer)); Material[] materials = new Material[NUM_MATERIALS]; for (int i = 0; i < NUM_MATERIALS; ++i) { materials[i] = new Material(i.ToString(), testFS); } // Set up context ModelInstanceHandle[] instances = new ModelInstanceHandle[NUM_ALLOCATIONS]; // Execute for (int i = 0; i < NUM_ALLOCATIONS; i++) { Transform initialTransform = new Transform( Vector3.ONE * i, Quaternion.FromAxialRotation(Vector3.ONE * (i + 1), MathUtils.PI), Vector3.ONE * -i ); instances[i] = testMIM.AllocateInstance(materials[i % NUM_MATERIALS].Index, (uint)i % NUM_MODELS, (uint)i % NUM_SCENE_LAYERS, initialTransform); } for (int i = 0; i < NUM_ALLOCATIONS; i += 2) { instances[i].Transform = instances[i].Transform.With(scale: Vector3.FORWARD * i); } // Assert outcome RenderingModule.RenderStateBarrier.FreezeMutations(); ArraySlice <KeyValuePair <Material, ModelInstanceManager.MIDArray> > midData = testMIM.GetModelInstanceData(); RenderingModule.RenderStateBarrier.UnfreezeMutations(); Assert.AreEqual(NUM_ALLOCATIONS, midData.Sum(kvp => { unsafe { int val = 0; for (int i = 0; i < kvp.Value.Length; ++i) { if (kvp.Value.Data[i].InUse) { ++val; } } return(val); } })); unsafe { foreach (KeyValuePair <Material, ModelInstanceManager.MIDArray> kvp in midData) { Assert.IsTrue(materials.Contains(kvp.Key)); for (uint i = 0U; i < kvp.Value.Length; ++i) { if (!kvp.Value.Data[i].InUse) { continue; } Assert.AreEqual(1, instances.Count(mih => mih.Transform == kvp.Value.Data[i].Transform)); } } } materials.ForEach(mat => mat.Dispose()); testFS.Dispose(); fsCBuffer.Dispose(); }