Exemple #1
0
        // 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;
            }
        }
Exemple #3
0
        // 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);
        }
Exemple #4
0
        /// <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));
    }
Exemple #9
0
        /// <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);
        }
Exemple #10
0
        internal static unsafe InputEvent *GetNextInMemory(InputEvent *current)
        {
            var alignedSizeInBytes = NumberHelpers.AlignToMultiple(current->sizeInBytes, kAlignment);

            return((InputEvent *)((byte *)current + alignedSizeInBytes));
        }
Exemple #11
0
        // 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);
        }