public unsafe void TestDrawIndexedInstanced() { RenderCommand testCommand = RenderCommand.DrawIndexedInstanced(1, 2U, 3U, 4U, 5U); Assert.AreEqual(RenderCommandInstruction.DrawIndexedInstanced, testCommand.Instruction); Assert.AreEqual((RenderCommandArgument)1, testCommand.Arg1); ulong arg2AsUlong = UnsafeUtils.Reinterpret <RenderCommandArgument, ulong>(testCommand.Arg2, sizeof(ulong)); uint *arg23Ptr = (uint *)&arg2AsUlong; Assert.AreEqual(2U, arg23Ptr[0]); Assert.AreEqual(3U, arg23Ptr[1]); ulong arg3AsUlong = UnsafeUtils.Reinterpret <RenderCommandArgument, ulong>(testCommand.Arg3, sizeof(ulong)); uint *arg45Ptr = (uint *)&arg3AsUlong; Assert.AreEqual(4U, arg45Ptr[0]); Assert.AreEqual(5U, arg45Ptr[1]); }
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); }
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); }