public FrameRequest(int frameId, Duration frameTime, Duration circleTime, FrameRequest previousFrameRequest) { FrameId = frameId; FrameTime = frameTime; CircleTime = circleTime; PreviousFrameRequest = previousFrameRequest; Rendered = new ManualResetEvent(false); Compressed = new ManualResetEvent(false); Transmitted = new ManualResetEvent(false); }
private Task Transmit(FrameRequest request, ArraySegment <byte> segment) { var sw = new Stopwatch(); sw.Start(); var task = _webSocket.SendDataAsync(segment, _cancellationToken); return(task.ContinueWith(t => _stats.AddTransmitDuration(sw.Elapsed), _cancellationToken)); }
private void Render(FrameRequest request, Graphics gfx) { Duration frameTime = request.FrameTime; Duration circleTime = request.CircleTime; var frameTimeMs = frameTime.TotalMilliseconds; var sw = new Stopwatch(); sw.Start(); var width = _spec.Width; var height = _spec.Height; var radius = _spec.Radius; var center = _spec.Center; gfx.ResetTransform(); gfx.Clear(Color.SkyBlue); gfx.TranslateTransform(width * 0.5f, height * 0.5f); var frameAngle = (circleTime.TotalSeconds * 360 / _spec.SpinDurationSec) % 360; gfx.RotateTransform((float)frameAngle); if (_background != null) { var state = gfx.Save(); gfx.CompositingMode = CompositingMode.SourceCopy; gfx.CompositingQuality = CompositingQuality.HighSpeed; gfx.InterpolationMode = InterpolationMode.NearestNeighbor; gfx.SmoothingMode = SmoothingMode.None; gfx.DrawImage(_background, -width * 0.5f, -height * 0.5f); gfx.Restore(state); } var transform = gfx.Transform; gfx.Transform = transform; gfx.FillRectangle(Brushes.Black, center, -5, radius, 10); _stats.AddRenderDuration(sw.Elapsed); gfx.ResetTransform(); // Draw a bouncing ball const float ballRadius = 20; gfx.FillEllipse(Brushes.Orange, width * 0.5f - ballRadius, height - (height - 2 * ballRadius) * (float)Math.Abs(Math.Sin((float)(frameTimeMs / 1000f))), ballRadius * 2, ballRadius * 2); request.Rendered.Set(); }
public void PostRequest(FrameRequest request) { Debug.Assert(_requests.IsEmpty); _requests.Enqueue(request); _queued.Set(); }
private async Task Compress(FrameRequest request, Bitmap srcImage, Compressor[] compressors, int quality = 90) { var imageOffset = FrameHeader.MessageSize; var header = _stats.Update(request.FrameId, request.FrameTime); PixelFormat pixelFormat = srcImage.PixelFormat; int width = srcImage.Width; int height = srcImage.Height; BitmapData bitmapData = srcImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, pixelFormat); int stride = bitmapData.Stride; int bytesPerPixel = pixelFormat.GetBytesPerPixel(); IntPtr scan0 = bitmapData.Scan0; try { int remainingCompressors = compressors.Length; var tasks = compressors .Select((compressor, index) => Task.Run( delegate { var sw = new Stopwatch(); sw.Start(); var segmentX = (index >> 1) * (_spec.Width / 2); var segmentY = (index & 1) * (_spec.Height / 2); var ptr = scan0.ToInt64() + segmentY * stride + segmentX * bytesPerPixel; var data = compressor.Compress(new IntPtr(ptr), stride, imageOffset, pixelFormat, quality); if (Interlocked.Decrement(ref remainingCompressors) == 0) { // All compression threads are done. request.Compressed.Set(); } var span = MemoryMarshal.Cast <byte, FrameHeader>( new Span <byte>(data.Array, data.Offset, data.Count)); span[0] = header; span[0].SegmentX = segmentX; span[0].SegmentY = segmentY; _stats.AddCompressedSize(data.Count); _stats.AddCompressDuration(sw.Elapsed); // Make sure the previous frame is transmitted first request.PreviousFrameRequest.Transmitted.WaitOne(); // Send this segment return(Transmit(request, data)); }, _cancellationToken)) .ToArray(); Stopwatch tw = new Stopwatch(); tw.Start(); await Task.WhenAll(tasks); _stats.AddTransmitDuration(tw.Elapsed); request.Transmitted.Set(); } finally { srcImage.UnlockBits(bitmapData); } }