private IEnumerable <Drawable> buildPositionalInputQueue(InputState state) { positionalInputQueue.Clear(); if (this is UserInputManager) { FrameStatistics.Increment(StatisticsCounterType.PositionalIQ); } var children = AliveInternalChildren; for (int i = 0; i < children.Count; i++) { children[i].BuildPositionalInputQueue(state.Mouse.Position, positionalInputQueue); } positionalInputQueue.Reverse(); return(positionalInputQueue); }
private void handleKeyboardEvent(object sender, KeyboardKeyEventArgs e) { var rawState = e.Keyboard; if (lastRawState != null && rawState.Equals(lastRawState)) { return; } lastRawState = rawState; var newState = new TkKeyboardState(rawState); PendingInputs.Enqueue(new KeyboardKeyInput(newState.Keys, lastEventState?.Keys)); lastEventState = newState; FrameStatistics.Increment(StatisticsCounterType.KeyEvents); }
public override bool Initialize(GameHost host) { Enabled.BindValueChanged(enabled => { if (enabled) { host.InputThread.Scheduler.Add(scheduledRefreshDevices = new ScheduledDelegate(refreshDevices, 0, 500)); host.InputThread.Scheduler.Add(scheduledPoll = new ScheduledDelegate(delegate { foreach (var device in devices) { if (device.RawState.Equals(device.LastRawState)) { continue; } var newState = new OpenTKJoystickState(device); handleState(device, newState); FrameStatistics.Increment(StatisticsCounterType.JoystickEvents); } }, 0, 0)); } else { scheduledPoll?.Cancel(); scheduledRefreshDevices?.Cancel(); foreach (var device in devices) { if (device.LastState != null) { handleState(device, new JoystickState()); } } devices.Clear(); mostSeenDevices = 0; } }, true); return(true); }
/// <summary> /// Refresh using a cached delegate. /// </summary> /// <returns>Whether refreshing was possible.</returns> public bool EnsureValid() { if (IsValid) { return(true); } if (updateDelegate == null) { return(false); } value = updateDelegate(); isValid = true; FrameStatistics.Increment(StatisticsCounterType.Refreshes); return(true); }
private SlimReadOnlyListWrapper <Drawable> buildPositionalInputQueue(Vector2 screenSpacePos) { positionalInputQueue.Clear(); if (this is UserInputManager) { FrameStatistics.Increment(StatisticsCounterType.PositionalIQ); } var children = AliveInternalChildren; for (int i = 0; i < children.Count; i++) { children[i].BuildPositionalInputQueue(screenSpacePos, positionalInputQueue); } positionalInputQueue.Reverse(); return(positionalInputQueue.AsSlimReadOnly()); }
protected override void LoadComplete() { base.LoadComplete(); PerformanceOverlay performanceOverlay; LoadComponentAsync(performanceOverlay = new PerformanceOverlay(Host.Threads.Reverse()) { Margin = new MarginPadding(5), Direction = FillDirection.Vertical, Spacing = new Vector2(10, 10), AutoSizeAxes = Axes.Both, Alpha = 0, Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, Depth = float.MinValue }, AddInternal); FrameStatistics.BindValueChanged(e => performanceOverlay.State = e.NewValue, true); }
/// <summary> /// Binds a texture to draw with. /// </summary> /// <param name="textureId">The texture to bind.</param> /// <param name="unit">The texture unit to bind it to.</param> /// <returns>true if the provided texture was not already bound (causing a binding change).</returns> public static bool BindTexture(int textureId, TextureUnit unit = TextureUnit.Texture0) { var index = GetTextureUnitId(unit); if (last_bound_texture[index] == textureId) { return(false); } FlushCurrentBatch(); GL.ActiveTexture(unit); GL.BindTexture(TextureTarget.Texture2D, textureId); last_bound_texture[index] = textureId; last_bound_texture_is_atlas[GetTextureUnitId(unit)] = false; FrameStatistics.Increment(StatisticsCounterType.TextureBinds); return(true); }
private void dispatchEvent(byte eventType, byte key, byte velocity) { Logger.Log($"Handling MIDI event {eventType:X2}:{key:X2}:{velocity:X2}"); switch (eventType) { case MidiEvent.NoteOn when velocity != 0: Logger.Log($"NoteOn: {(MidiKey)key}/{velocity / 128f:P}"); PendingInputs.Enqueue(new MidiKeyInput((MidiKey)key, velocity, true)); FrameStatistics.Increment(StatisticsCounterType.MidiEvents); break; case MidiEvent.NoteOff: case MidiEvent.NoteOn when velocity == 0: Logger.Log($"NoteOff: {(MidiKey)key}/{velocity / 128f:P}"); PendingInputs.Enqueue(new MidiKeyInput((MidiKey)key, 0, false)); FrameStatistics.Increment(StatisticsCounterType.MidiEvents); break; } }
public override void DrawTriangle(Triangle vertexTriangle, RectangleF?textureRect, ColourInfo drawColour, Action <TexturedVertex2D> vertexAction = null, Vector2?inflationPercentage = null) { Debug.Assert(!isDisposed); RectangleF texRect = GetTextureRect(textureRect); if (inflationPercentage.HasValue) { texRect = texRect.Inflate(new Vector2(inflationPercentage.Value.X * texRect.Width, inflationPercentage.Value.Y * texRect.Height)); } if (vertexAction == null) { if (triangleBatch == null) { triangleBatch = new LinearBatch <TexturedVertex2D>(512, 128, PrimitiveType.Triangles); } vertexAction = triangleBatch.Add; } vertexAction(new TexturedVertex2D { Position = vertexTriangle.P0, TexturePosition = new Vector2((texRect.Left + texRect.Right) / 2, texRect.Top), Colour = drawColour.TopLeft.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexTriangle.P1, TexturePosition = new Vector2(texRect.Left, texRect.Bottom), Colour = drawColour.BottomLeft.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexTriangle.P2, TexturePosition = new Vector2(texRect.Right, texRect.Bottom), Colour = drawColour.BottomRight.Linear, }); FrameStatistics.Increment(StatisticsCounterType.KiloPixels, (long)vertexTriangle.ConservativeArea); }
public override bool Initialize(GameHost host) { Enabled.BindValueChanged(e => { if (e.NewValue) { host.InputThread.Scheduler.Add(scheduled = new ScheduledDelegate(delegate { refreshDevices(); foreach (var device in devices) { if ((device.LastRawState.HasValue && device.RawState.Equals(device.LastRawState.Value)) || !device.RawState.IsConnected) { continue; } handleState(device, new OsuTKJoystickState(device)); FrameStatistics.Increment(StatisticsCounterType.JoystickEvents); } }, 0, 0)); } else { scheduled?.Cancel(); foreach (var device in devices) { if (device.LastState != null) { handleState(device, new JoystickState()); } } devices.Clear(); } }, true); return(true); }
protected BasicGameHost(string gameName = @"") { Instance = this; AppDomain.CurrentDomain.UnhandledException += exceptionHandler; Dependencies.Cache(this); name = gameName; threads = new[] { DrawThread = new GameThread(DrawFrame, @"Draw") { OnThreadStart = DrawInitialize, }, UpdateThread = new GameThread(UpdateFrame, @"Update") { OnThreadStart = UpdateInitialize, Monitor = { HandleGC = true } }, InputThread = new InputThread(null, @"Input") //never gets started. }; Clock = UpdateThread.Clock; MaximumUpdateHz = GameThread.DEFAULT_ACTIVE_HZ; MaximumDrawHz = (DisplayDevice.Default?.RefreshRate ?? 0) * 4; // Note, that RegisterCounters only has an effect for the first // BasicGameHost to be passed into it; i.e. the first BasicGameHost // to be instantiated. FrameStatistics.RegisterCounters(this); Environment.CurrentDirectory = System.IO.Path.GetDirectoryName(FullPath); setActive(true); AddInternal(inputManager = new UserInputManager(this)); Dependencies.Cache(inputManager); }
private int addArea(FrameStatistics frame, PerformanceCollectionType frameTimeType, int currentHeight, byte[] textureData) { Debug.Assert(textureData.Length >= HEIGHT * 4, $"textureData is too small ({textureData.Length}) to hold area data."); double elapsedMilliseconds = 0; int drawHeight = 0; if (frameTimeType == PerformanceCollectionType.Empty) { drawHeight = currentHeight; } else if (frame.CollectedTimes.TryGetValue(frameTimeType, out elapsedMilliseconds)) { legendMapping[(int)frameTimeType].Alpha = 1; drawHeight = (int)(elapsedMilliseconds * scale); } else { return(currentHeight); } Color4 col = getColour(frameTimeType); for (int i = currentHeight - 1; i >= 0; --i) { if (drawHeight-- == 0) { break; } int index = i * 4; textureData[index] = (byte)(255 * col.R); textureData[index + 1] = (byte)(255 * col.G); textureData[index + 2] = (byte)(255 * col.B); textureData[index + 3] = (byte)(255 * (frameTimeType == PerformanceCollectionType.Empty ? (col.A * (1 - (int)((i * 4) / HEIGHT) / 8f)) : col.A)); currentHeight--; } return(currentHeight); }
private void dispatchEvent(byte eventType, byte key, byte velocity) { Logger.Log($"Handling MIDI event {eventType:X2}:{key:X2}:{velocity:X2}"); // Low nibble only contains channel data in note on/off messages // Ignore to receive messages from all channels switch (eventType & 0xF0) { case MidiEvent.NoteOn when velocity != 0: Logger.Log($"NoteOn: {(MidiKey)key}/{velocity / 128f:P}"); PendingInputs.Enqueue(new MidiKeyInput((MidiKey)key, velocity, true)); FrameStatistics.Increment(StatisticsCounterType.MidiEvents); break; case MidiEvent.NoteOff: case MidiEvent.NoteOn when velocity == 0: Logger.Log($"NoteOff: {(MidiKey)key}/{velocity / 128f:P}"); PendingInputs.Enqueue(new MidiKeyInput((MidiKey)key, 0, false)); FrameStatistics.Increment(StatisticsCounterType.MidiEvents); break; } }
public override bool Initialize(GameHost host) { host.InputThread.Scheduler.Add(scheduled = new ScheduledDelegate(delegate { var state = host.IsActive ? OpenTK.Input.Keyboard.GetState() : new OpenTK.Input.KeyboardState(); if (state.Equals(lastState)) { return; } lastState = state; PendingStates.Enqueue(new InputState { Keyboard = new TkKeyboardState(state) }); FrameStatistics.Increment(StatisticsCounterType.KeyEvents); }, 0, 0)); return(true); }
protected void HandleState(OsuTKMouseState state, OsuTKMouseState lastState, bool isAbsolutePosition) { if (lastState == null || isAbsolutePosition) { PendingInputs.Enqueue(new MousePositionAbsoluteInput { Position = state.Position }); currentPosition = state.Position; } else { var delta = state.Position - lastState.Position; if (delta != Vector2.Zero) { PendingInputs.Enqueue(new MousePositionRelativeInput { Delta = delta }); currentPosition += delta; } } if (lastState != null && state.WasActive) { var scrollDelta = state.Scroll - lastState.Scroll; if (scrollDelta != Vector2.Zero) { PendingInputs.Enqueue(new MouseScrollRelativeInput { Delta = scrollDelta, IsPrecise = state.HasPreciseScroll }); } } PendingInputs.Enqueue(new MouseButtonInput(state.Buttons, lastState?.Buttons)); FrameStatistics.Increment(StatisticsCounterType.MouseEvents); }
/// <param name="instanceCount">if >0 will draw instanced geometry</param> /// <returns></returns> public int DrawInstanced(int instanceCount) { if (currentVertex == lastVertex) { return(0); } VertexBuffer <T> vertexBuffer = CurrentVertexBuffer; if (changeBeginIndex >= 0) { vertexBuffer.UpdateRange(changeBeginIndex, changeEndIndex); } if (instanceCount > 0) { vertexBuffer.DrawRangeInstanced(lastVertex, currentVertex, instanceCount); } else { vertexBuffer.DrawRange(lastVertex, currentVertex); } int count = currentVertex - lastVertex; // When using multiple buffers we advance to the next one with every draw to prevent contention on the same buffer with future vertex updates. //TODO: let us know if we exceed and roll over to zero here. currentVertexBuffer = (currentVertexBuffer + 1) % maxBuffers; currentVertex = 0; lastVertex = currentVertex; changeBeginIndex = -1; FrameStatistics.Increment(StatisticsCounterType.DrawCalls); FrameStatistics.Increment(StatisticsCounterType.VerticesDraw, count); return(count); }
public override bool Initialize(GameHost host) { Enabled.ValueChanged += enabled => { if (enabled) { host.InputThread.Scheduler.Add(scheduled = new ScheduledDelegate(delegate { refreshDevices(); foreach (var device in devices) { if (device.State.Equals(device.LastState)) { continue; } if (device.LastState != null) { PendingStates.Enqueue(new InputState { Joystick = new OpenTKJoystickState(device) }); FrameStatistics.Increment(StatisticsCounterType.JoystickEvents); } } }, 0, 0)); } else { scheduled?.Cancel(); devices.Clear(); } }; Enabled.TriggerChange(); return(true); }
/// <summary> /// Generates the DrawNode for ourselves. /// </summary> /// <returns>A complete and updated DrawNode, or null if the DrawNode would be invisible.</returns> protected internal virtual DrawNode GenerateDrawNodeSubtree(int treeIndex, RectangleF bounds) { if (isProxied) { return(null); } DrawNode node = drawNodes[treeIndex]; if (node == null) { drawNodes[treeIndex] = node = CreateDrawNode(); FrameStatistics.Increment(StatisticsCounterType.DrawNodeCtor); } if (invalidationID != node.InvalidationID) { ApplyDrawNode(node); FrameStatistics.Increment(StatisticsCounterType.DrawNodeAppl); } return(node); }
public override bool Initialize(GameHost host) { host.InputThread.Scheduler.Add(scheduled = new ScheduledDelegate(delegate { OpenTK.Input.MouseState state = OpenTK.Input.Mouse.GetCursorState(); Point point = host.Window.PointToClient(new Point(state.X, state.Y)); //todo: reimplement if necessary //Vector2 pos = Vector2.Multiply(point, Vector2.Divide(host.DrawSize, this.Size)); Vector2 pos = new Vector2(point.X, point.Y); var tkState = new TkMouseState(state, pos, host.IsActive); PendingStates.Enqueue(new InputState { Mouse = tkState }); FrameStatistics.Increment(StatisticsCounterType.MouseEvents); }, 0, 0)); return(true); }
/// <summary> /// Updates this audio component. Always runs on the audio thread. /// </summary> public void Update() { ThreadSafety.EnsureNotUpdateThread(); if (IsDisposed) { throw new ObjectDisposedException(ToString(), "Can not update disposed audio components."); } FrameStatistics.Add(StatisticsCounterType.TasksRun, PendingActions.Count); FrameStatistics.Increment(StatisticsCounterType.Components); Action action; while (!IsDisposed && PendingActions.TryDequeue(out action)) { action(); } if (!IsDisposed) { UpdateState(); } }
/// <summary> /// Updates this Drawable and all Drawables further down the scene graph. /// Called once every frame. /// </summary> /// <returns>False if the drawable should not be updated.</returns> protected internal virtual bool UpdateSubTree() { Debug.Assert(!isDisposed, "Disposed Drawables may never be in the scene graph."); if (Parent != null) //we don't want to update our clock if we are at the top of the stack. it's handled elsewhere for us. { customClock?.ProcessFrame(); } if (LoadState < LoadState.Alive) { if (!loadComplete()) { return(false); } } transformationDelay = 0; //todo: this should be moved to after the IsVisible condition once we have TOL for transformations (and some better logic). updateTransforms(); if (!IsPresent) { return(true); } if (scheduler != null) { int amountScheduledTasks = scheduler.Update(); FrameStatistics.Increment(StatisticsCounterType.ScheduleInvk, amountScheduledTasks); } Update(); OnUpdate?.Invoke(); return(true); }
public sealed override void Draw(Action <TexturedVertex2D> vertexAction) { if (RequiresRedraw) { FrameStatistics.Increment(StatisticsCounterType.FBORedraw); SharedData.ResetCurrentEffectBuffer(); using (establishFrameBufferViewport()) { // Fill the frame buffer with drawn children using (BindFrameBuffer(SharedData.MainBuffer)) { // We need to draw children as if they were zero-based to the top-left of the texture. // We can do this by adding a translation component to our (orthogonal) projection matrix. GLWrapper.PushOrtho(screenSpaceDrawRectangle); GLWrapper.Clear(new ClearInfo(backgroundColour)); Child.Draw(vertexAction); GLWrapper.PopOrtho(); } PopulateContents(); } SharedData.DrawVersion = GetDrawVersion(); } Shader.Bind(); base.Draw(vertexAction); DrawContents(); Shader.Unbind(); }
public override bool Initialize(GameHost host) { host.InputThread.Scheduler.Add(scheduled = new ScheduledDelegate(delegate { if (!host.Window.Visible) { return; } var state = OpenTK.Input.Mouse.GetCursorState(); if (state.Equals(lastState)) { return; } lastState = state; Point point = host.Window.PointToClient(new Point(state.X, state.Y)); Vector2 pos = new Vector2(point.X, point.Y); // While not focused, let's silently ignore everything but position. if (!host.Window.Focused) { state = new OpenTK.Input.MouseState(); } PendingStates.Enqueue(new InputState { Mouse = new TkMouseState(state, pos, host.IsActive) }); FrameStatistics.Increment(StatisticsCounterType.MouseEvents); }, 0, 0)); return(true); }
protected override void UpdateState() { FrameStatistics.Increment(StatisticsCounterType.SChannels); base.UpdateState(); }
public void Begin() { start = Time.realtimeSinceStartup; frameStats = GetComponent<FrameStatistics>(); frameStats.Initialize(); }
protected override void UpdateState() { FrameStatistics.Add(StatisticsCounterType.Samples, factories.Count); base.UpdateState(); }
protected override bool CheckChildrenLife() { // We have to at least wait until Clock becomes available. if (LoadState != LoadState.Loaded) { return(false); } bool aliveChildrenChanged = false; // Move loaded children to appropriate list. for (var i = newChildren.Count - 1; i >= 0; --i) { FrameStatistics.Increment(StatisticsCounterType.CCL); var child = newChildren[i]; if (child.LoadState >= LoadState.Ready) { Debug.Assert(!childStateMap.ContainsKey(child)); newChildren.RemoveAt(i); var entry = new ChildEntry(child); childStateMap.Add(child, entry); aliveChildrenChanged |= updateChildEntry(entry); } } var currentTime = Time.Current; // Checks for newly alive children when time is increased or new children added. while (futureChildren.Count > 0) { FrameStatistics.Increment(StatisticsCounterType.CCL); var entry = futureChildren.Min; Debug.Assert(entry.State == LifetimeState.Future); if (currentTime < entry.LifetimeStart) { break; } futureChildren.Remove(entry); aliveChildrenChanged |= updateChildEntry(entry); } while (pastChildren.Count > 0) { FrameStatistics.Increment(StatisticsCounterType.CCL); var entry = pastChildren.Max; Debug.Assert(entry.State == LifetimeState.Past); if (entry.LifetimeEnd <= currentTime) { break; } pastChildren.Remove(entry); aliveChildrenChanged |= updateChildEntry(entry); } for (var i = AliveInternalChildren.Count - 1; i >= 0; --i) { FrameStatistics.Increment(StatisticsCounterType.CCL); var child = AliveInternalChildren[i]; Debug.Assert(childStateMap.ContainsKey(child)); var entry = childStateMap[child]; aliveChildrenChanged |= updateChildEntry(entry); } Debug.Assert(newChildren.Count + futureChildren.Count + pastChildren.Count + AliveInternalChildren.Count == InternalChildren.Count); while (eventQueue.Count != 0) { var e = eventQueue.Dequeue(); OnChildLifetimeBoundaryCrossed(e); } return(aliveChildrenChanged); }
public override void DrawTriangle(Triangle vertexTriangle, RectangleF?textureRect, ColourInfo drawColour, Action <TexturedVertex2D> vertexAction = null, Vector2?inflationPercentage = null) { if (IsDisposed) { throw new ObjectDisposedException(ToString(), "Can not draw a triangle with a disposed texture."); } RectangleF texRect = GetTextureRect(textureRect); Vector2 inflationAmount = inflationPercentage.HasValue ? new Vector2(inflationPercentage.Value.X * texRect.Width, inflationPercentage.Value.Y * texRect.Height) : Vector2.Zero; RectangleF inflatedTexRect = texRect.Inflate(inflationAmount); if (vertexAction == null) { vertexAction = default_triangle_action; } // We split the triangle into two, such that we can obtain smooth edges with our // texture coordinate trick. We might want to revert this to drawing a single // triangle in case we ever need proper texturing, or if the additional vertices // end up becoming an overhead (unlikely). SRGBColour topColour = (drawColour.TopLeft + drawColour.TopRight) / 2; SRGBColour bottomColour = (drawColour.BottomLeft + drawColour.BottomRight) / 2; // Left triangle half vertexAction(new TexturedVertex2D { Position = vertexTriangle.P0, TexturePosition = new Vector2(inflatedTexRect.Left, inflatedTexRect.Top), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = topColour.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexTriangle.P1, TexturePosition = new Vector2(inflatedTexRect.Left, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = drawColour.BottomLeft.Linear, }); vertexAction(new TexturedVertex2D { Position = (vertexTriangle.P1 + vertexTriangle.P2) / 2, TexturePosition = new Vector2((inflatedTexRect.Left + inflatedTexRect.Right) / 2, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = bottomColour.Linear, }); // Right triangle half vertexAction(new TexturedVertex2D { Position = vertexTriangle.P0, TexturePosition = new Vector2(inflatedTexRect.Right, inflatedTexRect.Top), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = topColour.Linear, }); vertexAction(new TexturedVertex2D { Position = (vertexTriangle.P1 + vertexTriangle.P2) / 2, TexturePosition = new Vector2((inflatedTexRect.Left + inflatedTexRect.Right) / 2, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = bottomColour.Linear, }); vertexAction(new TexturedVertex2D { Position = vertexTriangle.P2, TexturePosition = new Vector2(inflatedTexRect.Right, inflatedTexRect.Bottom), TextureRect = new Vector4(texRect.Left, texRect.Top, texRect.Right, texRect.Bottom), BlendRange = inflationAmount, Colour = drawColour.BottomRight.Linear, }); FrameStatistics.Add(StatisticsCounterType.Pixels, (long)vertexTriangle.ConservativeArea); }
private void enqueueJoystickEvent(IInput evt) { PendingInputs.Enqueue(evt); FrameStatistics.Increment(StatisticsCounterType.JoystickEvents); }
private void enqueueInput(IInput input) { PendingInputs.Enqueue(input); FrameStatistics.Increment(StatisticsCounterType.KeyEvents); }
protected override void UpdateState() { FrameStatistics.Add(StatisticsCounterType.Samples, sampleCache.Count); base.UpdateState(); }