// Compute the total size of we need for a single state buffer to encompass // all devices we have and also linearly assign offsets to all the devices // within such a buffer. private static uint ComputeSizeOfSingleBufferAndOffsetForEachDevice(InputDevice[] devices, int deviceCount, ref uint[] offsets) { if (devices == null) { return(0); } var result = new uint[deviceCount]; var sizeInBytes = 0u; for (var i = 0; i < deviceCount; ++i) { var sizeOfDevice = devices[i].m_StateBlock.alignedSizeInBytes; sizeOfDevice = NumberHelpers.AlignToMultiple(sizeOfDevice, 4); if (sizeOfDevice == 0) // Shouldn't happen as we don't allow empty layouts but make sure we catch this if something slips through. { throw new Exception($"Device '{devices[i]}' has a zero-size state buffer"); } result[i] = sizeInBytes; sizeInBytes += sizeOfDevice; } offsets = result; return(sizeInBytes); }
public unsafe void QueueEvent(IntPtr ptr) { var eventPtr = (InputEvent *)ptr; var eventSize = eventPtr->sizeInBytes; var alignedEventSize = NumberHelpers.AlignToMultiple(eventSize, 4); lock (m_Lock) { eventPtr->eventId = m_NextEventId; ++m_NextEventId; // Enlarge buffer, if we have to. if ((m_EventWritePosition + alignedEventSize) > m_EventBuffer.Length) { var newBufferSize = m_EventBuffer.Length + Mathf.Max((int)alignedEventSize, 1024); var newBuffer = new NativeArray <byte>(newBufferSize, Allocator.Persistent); UnsafeUtility.MemCpy(newBuffer.GetUnsafePtr(), m_EventBuffer.GetUnsafePtr(), m_EventWritePosition); m_EventBuffer.Dispose(); m_EventBuffer = newBuffer; } // Copy event. UnsafeUtility.MemCpy((byte *)m_EventBuffer.GetUnsafePtr() + m_EventWritePosition, ptr.ToPointer(), eventSize); m_EventWritePosition += (int)alignedEventSize; ++m_EventCount; } }
// Compute the total size of we need for a single state buffer to encompass // all devices we have and also linearly assign offsets to all the devices // within such a buffer. private static uint ComputeSizeOfSingleBufferAndOffsetForEachDevice(InputDevice[] devices, int deviceCount, ref uint[] offsets) { if (devices == null) { return(0); } var result = new uint[deviceCount]; var currentOffset = 0u; var sizeInBytes = 0u; for (var i = 0; i < deviceCount; ++i) { var size = devices[i].m_StateBlock.alignedSizeInBytes; size = NumberHelpers.AlignToMultiple(size, 4); ////REVIEW: what should we do about this case? silently accept it and just give the device the current offset? if (size == 0) { throw new Exception(string.Format("Device '{0}' has a zero-size state buffer", devices[i])); } sizeInBytes += size; result[i] = currentOffset; currentOffset += (uint)size; } offsets = result; return(sizeInBytes); }
/// <summary> /// Get the next event after the given one. /// </summary> /// <param name="currentPtr">A valid event pointer.</param> /// <returns>Pointer to the next event in memory.</returns> /// <remarks> /// This method applies no checks and must only be called if there is an event following the /// given one. Also, the size of the given event must be 100% as the method will simply /// take the size and advance the given pointer by it (and aligning it to <see cref="kAlignment"/>). /// </remarks> /// <seealso cref="GetNextInMemoryChecked"/> internal static unsafe InputEvent *GetNextInMemory(InputEvent *currentPtr) { Debug.Assert(currentPtr != null); var alignedSizeInBytes = NumberHelpers.AlignToMultiple(currentPtr->sizeInBytes, kAlignment); return((InputEvent *)((byte *)currentPtr + alignedSizeInBytes)); }
/// <summary> /// Advance the read position to the next event in the buffer, preserving or not preserving the /// current event depending on <paramref name="leaveEventInBuffer"/>. /// </summary> /// <param name="currentReadPos"></param> /// <param name="currentWritePos"></param> /// <param name="numEventsRetainedInBuffer"></param> /// <param name="numRemainingEvents"></param> /// <param name="leaveEventInBuffer"></param> /// <remarks> /// This method MUST ONLY BE CALLED if the current event has been fully processed. If the at <paramref name="currentWritePos"/> /// is smaller than the current event, then this method will OVERWRITE parts or all of the current event. /// </remarks> internal void AdvanceToNextEvent(ref InputEvent *currentReadPos, ref InputEvent *currentWritePos, ref int numEventsRetainedInBuffer, ref int numRemainingEvents, bool leaveEventInBuffer) { Debug.Assert(Contains(currentReadPos)); Debug.Assert(Contains(currentWritePos)); Debug.Assert(currentReadPos >= currentWritePos); // Get new read position *before* potentially moving the current event so that we don't // end up overwriting the data we need to find the next event in memory. var newReadPos = currentReadPos; if (numRemainingEvents > 1) { newReadPos = InputEvent.GetNextInMemoryChecked(currentReadPos, ref this); } // If the current event should be left in the buffer, advance write position. if (leaveEventInBuffer) { // Move down in buffer if read and write pos have deviated from each other. var numBytes = currentReadPos->sizeInBytes; if (currentReadPos != currentWritePos) { UnsafeUtility.MemMove(currentWritePos, currentReadPos, numBytes); } currentWritePos = (InputEvent *)((byte *)currentWritePos + NumberHelpers.AlignToMultiple(numBytes, 4)); ++numEventsRetainedInBuffer; } currentReadPos = newReadPos; --numRemainingEvents; }
// Allocates all buffers to serve the given updates and comes up with a spot // for the state block of each device. Returns the new state blocks for the // devices (it will *NOT* install them on the devices). public unsafe uint[] AllocateAll(InputUpdateType updateMask, InputDevice[] devices) { uint[] newDeviceOffsets = null; sizePerBuffer = ComputeSizeOfSingleBufferAndOffsetForEachDevice(devices, ref newDeviceOffsets); if (sizePerBuffer == 0) { return(null); } sizePerBuffer = NumberHelpers.AlignToMultiple(sizePerBuffer, 4); var isDynamicUpdateEnabled = (updateMask & InputUpdateType.Dynamic) == InputUpdateType.Dynamic; var isFixedUpdateEnabled = (updateMask & InputUpdateType.Fixed) == InputUpdateType.Fixed; // Determine how much memory we need. var deviceCount = devices.Length; var mappingTableSizePerBuffer = (uint)(deviceCount * sizeof(void *) * 2); if (isDynamicUpdateEnabled) { totalSize += sizePerBuffer * 2; totalSize += mappingTableSizePerBuffer; } if (isFixedUpdateEnabled) { totalSize += sizePerBuffer * 2; totalSize += mappingTableSizePerBuffer; } // Before render doesn't have its own buffers. #if UNITY_EDITOR totalSize += sizePerBuffer * 2; totalSize += mappingTableSizePerBuffer; #endif // Allocate. m_AllBuffers = (IntPtr)UnsafeUtility.Malloc(totalSize, 4, Allocator.Persistent); UnsafeUtility.MemClear(m_AllBuffers.ToPointer(), totalSize); // Set up device to buffer mappings. var ptr = m_AllBuffers; if (isDynamicUpdateEnabled) { m_DynamicUpdateBuffers = SetUpDeviceToBufferMappings(devices, ref ptr, sizePerBuffer, mappingTableSizePerBuffer); } if (isFixedUpdateEnabled) { m_FixedUpdateBuffers = SetUpDeviceToBufferMappings(devices, ref ptr, sizePerBuffer, mappingTableSizePerBuffer); } #if UNITY_EDITOR m_EditorUpdateBuffers = SetUpDeviceToBufferMappings(devices, ref ptr, sizePerBuffer, mappingTableSizePerBuffer); #endif return(newDeviceOffsets); }
public InputEvent *AllocateEvent(int sizeInBytes, int capacityIncrementInBytes = 2048) { if (sizeInBytes < InputEvent.kBaseEventSize) { throw new ArgumentException( string.Format("sizeInBytes must be >= sizeof(InputEvent) == {0} (was {1})", InputEvent.kBaseEventSize, sizeInBytes), "sizeInBytes"); } var alignedSizeInBytes = NumberHelpers.AlignToMultiple(sizeInBytes, InputEvent.kAlignment); // See if we need to enlarge our buffer. var currentCapacity = capacityInBytes; if (currentCapacity < alignedSizeInBytes) { // Yes, so reallocate. var newCapacity = Math.Max(currentCapacity + capacityIncrementInBytes, currentCapacity + alignedSizeInBytes); var newSize = this.sizeInBytes + newCapacity; if (newSize > int.MaxValue) { throw new NotImplementedException("NativeArray long support"); } var newBuffer = new NativeArray <byte>((int)newSize, Allocator.Persistent, NativeArrayOptions.ClearMemory); if (m_Buffer.IsCreated) { UnsafeUtility.MemCpy(newBuffer.GetUnsafePtr(), m_Buffer.GetUnsafeReadOnlyPtr(), this.sizeInBytes); } else { m_BufferEnd = 0; } if (m_WeOwnTheBuffer) { m_Buffer.Dispose(); } m_Buffer = newBuffer; m_WeOwnTheBuffer = true; } var eventPtr = (InputEvent *)((byte *)m_Buffer.GetUnsafePtr() + m_BufferEnd); eventPtr->sizeInBytes = (uint)sizeInBytes; m_BufferEnd += alignedSizeInBytes; ++m_EventCount; return(eventPtr); }
public unsafe void State_CanGetMetrics() { // Make sure we start out with blank data. var metrics = InputSystem.GetMetrics(); Assert.That(metrics.totalEventCount, Is.Zero); Assert.That(metrics.totalEventBytes, Is.Zero); Assert.That(metrics.totalUpdateCount, Is.Zero); var device1 = InputSystem.AddDevice <Gamepad>(); var device2 = InputSystem.AddDevice <Keyboard>(); InputSystem.QueueStateEvent(device1, new GamepadState()); InputSystem.QueueStateEvent(device1, new GamepadState()); InputSystem.QueueStateEvent(device2, new KeyboardState()); InputSystem.Update(); var device3 = InputSystem.AddDevice <Mouse>(); InputSystem.RemoveDevice(device3); metrics = InputSystem.GetMetrics(); // Manually compute the size of the combined state buffer so that we // have a check that catches if the size changes (for good or no good reason). var overheadPerBuffer = 3 * sizeof(void *) * 2; // Mapping table with front and back buffer pointers for three devices. var combinedDeviceStateSize = NumberHelpers.AlignToMultiple( device1.stateBlock.alignedSizeInBytes + device2.stateBlock.alignedSizeInBytes + device3.stateBlock.alignedSizeInBytes, 4); var sizePerBuffer = overheadPerBuffer + combinedDeviceStateSize * 2; // Front+back var sizeOfSingleBuffer = combinedDeviceStateSize; const int kDoubleBufferCount = #if UNITY_EDITOR 3 // Dynamic + fixed + editor #else 2 // Dynamic + fixed #endif ; var eventByteCount = StateEvent.GetEventSizeWithPayload <GamepadState>() * 2 + StateEvent.GetEventSizeWithPayload <KeyboardState>(); Assert.That(metrics.maxNumDevices, Is.EqualTo(3)); Assert.That(metrics.maxStateSizeInBytes, Is.EqualTo((kDoubleBufferCount * sizePerBuffer) + (sizeOfSingleBuffer * 2))); Assert.That(metrics.totalEventBytes, Is.EqualTo(eventByteCount)); Assert.That(metrics.totalEventCount, Is.EqualTo(3)); Assert.That(metrics.totalUpdateCount, Is.EqualTo(1)); Assert.That(metrics.totalEventProcessingTime, Is.GreaterThan(0.0000001)); Assert.That(metrics.averageEventBytesPerFrame, Is.EqualTo(eventByteCount).Within(0.00001)); Assert.That(metrics.averageProcessingTimePerEvent, Is.GreaterThan(0.0000001)); }
/// <summary> /// Get the next event after the given one. Throw if that would point to invalid memory as indicated /// by the given memory buffer. /// </summary> /// <param name="currentPtr">A valid event pointer to an event inside <paramref name="buffer"/>.</param> /// <param name="buffer">Event buffer in which to advance to the next event.</param> /// <returns>Pointer to the next event.</returns> /// <exception cref="InvalidOperationException">There are no more events in the given buffer.</exception> internal static unsafe InputEvent *GetNextInMemoryChecked(InputEvent *currentPtr, ref InputEventBuffer buffer) { Debug.Assert(currentPtr != null); Debug.Assert(buffer.Contains(currentPtr), "Given event is not contained in given event buffer"); var alignedSizeInBytes = NumberHelpers.AlignToMultiple(currentPtr->sizeInBytes, kAlignment); var nextPtr = (InputEvent *)((byte *)currentPtr + alignedSizeInBytes); if (!buffer.Contains(nextPtr)) { throw new InvalidOperationException(string.Format( "Event '{0}' is last event in given buffer with size {1}", new InputEventPtr(currentPtr), buffer.sizeInBytes)); } return(nextPtr); }
internal static unsafe InputEvent *GetNextInMemory(InputEvent *current) { var alignedSizeInBytes = NumberHelpers.AlignToMultiple(current->sizeInBytes, kAlignment); return((InputEvent *)((byte *)current + alignedSizeInBytes)); }
// Allocates all buffers to serve the given updates and comes up with a spot // for the state block of each device. Returns the new state blocks for the // devices (it will *NOT* install them on the devices). public uint[] AllocateAll(InputUpdateType updateMask, InputDevice[] devices, int deviceCount) { uint[] newDeviceOffsets = null; sizePerBuffer = ComputeSizeOfSingleBufferAndOffsetForEachDevice(devices, deviceCount, ref newDeviceOffsets); if (sizePerBuffer == 0) { return(null); } sizePerBuffer = NumberHelpers.AlignToMultiple(sizePerBuffer, 4); var isDynamicUpdateEnabled = (updateMask & InputUpdateType.Dynamic) != 0; var isFixedUpdateEnabled = (updateMask & InputUpdateType.Fixed) != 0; var isManualUpdateEnabled = (updateMask & InputUpdateType.Manual) != 0; // Determine how much memory we need. var mappingTableSizePerBuffer = (uint)(deviceCount * sizeof(void *) * 2); if (isDynamicUpdateEnabled) { totalSize += sizePerBuffer * 2; totalSize += mappingTableSizePerBuffer; } if (isFixedUpdateEnabled) { totalSize += sizePerBuffer * 2; totalSize += mappingTableSizePerBuffer; } if (isManualUpdateEnabled) { totalSize += sizePerBuffer * 2; totalSize += mappingTableSizePerBuffer; } // Before render doesn't have its own buffers. #if UNITY_EDITOR totalSize += sizePerBuffer * 2; totalSize += mappingTableSizePerBuffer; #endif // Plus 2 more buffers (1 for default states, and one for noise filters). totalSize += sizePerBuffer * 2; // Allocate. m_AllBuffers = UnsafeUtility.Malloc(totalSize, 4, Allocator.Persistent); UnsafeUtility.MemClear(m_AllBuffers, totalSize); // Set up device to buffer mappings. var ptr = (byte *)m_AllBuffers; if (isDynamicUpdateEnabled) { m_DynamicUpdateBuffers = SetUpDeviceToBufferMappings(devices, deviceCount, ref ptr, sizePerBuffer, mappingTableSizePerBuffer); } if (isFixedUpdateEnabled) { m_FixedUpdateBuffers = SetUpDeviceToBufferMappings(devices, deviceCount, ref ptr, sizePerBuffer, mappingTableSizePerBuffer); } if (isManualUpdateEnabled) { m_ManualUpdateBuffers = SetUpDeviceToBufferMappings(devices, deviceCount, ref ptr, sizePerBuffer, mappingTableSizePerBuffer); } #if UNITY_EDITOR m_EditorUpdateBuffers = SetUpDeviceToBufferMappings(devices, deviceCount, ref ptr, sizePerBuffer, mappingTableSizePerBuffer); #endif // Default state and noise filter buffers go last. defaultStateBuffer = ptr; noiseMaskBuffer = ptr + sizePerBuffer; return(newDeviceOffsets); }