I am facing a severe issue with the Camera API, and even if I know that in 99.99% of the situations, frameworks are not to blame, I'm starting to consider that there may be an issue with the way cameras are handled in windows phone.
The Issue
My code is trivial, there are just two very simple pages: the first one is a button used to navigate to the second one, which uses the camera API and renders the preview buffer on screen. On the first page, I also added a rectangle with a looping animation to rotate it, in order to force the application to update the frame rate.
The result of a profile session is as follows:
As long as I stay on the first page, the framerate counter displays a rock solid 60fps. Once I launch the second page featuring the camera, the fps drops due to the use of the system camera. However, when I hit the back key, the camera is correctly disposed, but the framerate is a bit lower (~53 fps). More impressive, the graph data shows that something is still running and hurting the perfs! The built-in analysis tells me that system apps use 47% of the CPU, the same that when the camera page was shown!
Is there a known reason for this behavior?
My Code
My code-behind for the second page:
public partial class MyScannerView : PhoneApplicationPage {
private PhotoCamera camera;
public MyScannerView() {
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e) {
base.OnNavigatedTo(e);
camera = new PhotoCamera(CameraType.Primary);
viewfinderBrush.SetSource(camera);
}
protected override void OnNavigatedFrom(NavigationEventArgs e) {
base.OnNavigatedFrom(e);
if (camera != null) {
camera.Dispose();
camera = null;
}
}
}
My XAML for the second page:
<Canvas x:Name="viewfinderCanvas">
<Canvas.Background>
<VideoBrush x:Name="viewfinderBrush">
<VideoBrush.RelativeTransform>
<CompositeTransform x:Name="viewfinderTransform" CenterX="0.5" CenterY="0.5" />
</VideoBrush.RelativeTransform>
</VideoBrush>
</Canvas.Background>
</Canvas>
I had similar looking issue with application, which uses camera.
Managed to overcome it with using not OnNavigatedFrom but OnNavigatingFrom override and doing it in such order:
Unsubscribing from events
Dispose();
camera = null;
Related
I am currently converting some of my old games implemented using C# (FW version 4.7.2) WinForms to Direct3D using C++.
Currently, all my real-time graphics games implement the OnPaint override, draw into the Graphics object retrieved from the PaintEventArgs parameter and invalidate right after drawing the current frame. (I don't know if this is not recommended, but it works great). These apps use double buffering of course. This causes 100% utilization of one core, which is OK.
The first obstacle I came accross when porting my games to C++ and Direct3D is the refresh rate of the window even if I had done the same thing by implementing the WndProc() and calling InvalidateRect() after drawing.
I followed this quick start guide: https://learn.microsoft.com/en-us/windows/win32/direct2d/direct2d-quickstart
The problem is,
With my windows forms apps, the refresh rate is around 60 fps drawing over a full-size background image, 300-400 fps when drawing on an empty background. The following example, drawing only a rectangle, produces an amazing 2,500 fps:
public class Surface : Form
{
private int fps = 0;
private int lastFPS = 0;
private double lastSeconds = 0;
private Stopwatch chronometer = Stopwatch.StartNew();
Font font = new Font("Courier New", 10f);
public Surface()
{
BackColor = Color.Black;
DoubleBuffered = true;
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.White, 100, 100, 100, 100);
e.Graphics.DrawString("FPS: " + lastFPS, font, Brushes.White, 5, 5);
fps++;
if (chronometer.Elapsed.Seconds > lastSeconds)
{
lastSeconds = chronometer.Elapsed.Seconds;
lastFPS = fps;
fps = 0;
}
Invalidate();
}
}
Needless to say, I was expecting a much better frame rate when porting to DirectX, but the problem is that the WM_PAINT event is not fired frequently enough and I am stuck at 100 fps even if I don't draw anything, and cpu utilization is around 10% only.
I only added the following line to the source code of the quick start guide:
case WM_PAINT:
{
pDemoApp->OnRender();
ValidateRect(hwnd, NULL);
InvalidateRect(hwnd, NULL, TRUE); // THIS IS THE LINE I'VE ADDED
}
What am I doing wrong?
I read that the rendering target of Direct 2D is automatically double buffered,
So the question is, why the WM_PAINT event gets fired only 100 times per second?
Note: Checked the windows forms Control.Invalidate() method source, and what it does is to call InvalidateRect (or RedrawWindow() if child controls are to be invalidated as well)
NOTE: Am I using direct 2d drawing incorrectly here? Should I continuously draw on a separate thread and let DirectX refresh the rendering target as it sees fit?
I've found the issue.
The following statement, while creating the render target for Direct2D, uses the default present option D2D1_PRESENT_OPTIONS_NONE, which causes the rendering engine to wait for the vSync, thus the maximum frames per second on my laptop equals 60 Hz (the refresh speed of the laptops monitor)
// Create a Direct2D render target.
hr = m_pDirect2dFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hwnd, size),
&m_pRenderTarget
);
By changing the present option to immediate, the WM_PAINT message is received right after calling InvalidateRect()
D2D1::HwndRenderTargetProperties(m_hwnd, size, D2D1_PRESENT_OPTIONS_IMMEDIATELY),
So I was too quick to ask this question, because having a refresh rate more than the refresh rate of the monitor doesn't have any effect and what I get from this is that the Direct2D engine is optimizing cpu usage by waiting for the next refresh of the actual screen, which makes good sense.
Trying to draw more than the monitor refresh rate looks like a waste of CPU cycles.
Need to consider implementing a good timing mechanism, because winforms and GDI is most of the time slower than the monitor refresh rate (when you have many sprites and geometries on the screen), and now we are faster than that and should adapt.
InvalidateRect(hwnd,.. itself fires WM_PAINT message on a window with hwnd handle. You've just created a circular reference (race).
I want to write a block diagram design tool (something similar to Simulink or Modelica). I have done something like that already in C++/Qt and C#/WinForms/GDI+, but now I want to go WPF.
Take a block, which can be a rectangle or a square that contains several other smaller shapes as "input/output/bidirectional ports" probably labelled or not, some text and probably a bitmap or vector image. It should provide context menus, basic mouse or drag events (for moving the block, pulling the connections between blocks, etc.) and should allow manual rearrangement of its graphical constituents, maybe in a different editor.
Now imagine a diagram with say 1000 of such blocks (I am exaggerating a bit to allow enough headroom in the future) and corresponding connections. Given that scale, I wonder if I should fall back on to the visual level of WPF and model the interaction part manually or if it is sufficient to use Drawings, Shapes or even Controls for it (like a block being a button).
I am getting a little nervous when I see the ~50 event types a Button supplies and multiply this with the number of blocks times the average number of ports per block. Many elements will just point to the same event handlers or context menus, so these handlers could also be redirected to a management class.
I read through the respective WPF chapters in the book "Pro C# 5.0" and that actually did not allay my fears.
So what level of WPF (visual, drawing, shape, control) is advisable when it comes to speed and memory performance under these requirements?
Sidenote: I am just starting with WPF, so I am a bit stunned about its versatility. It makes strategic decisions a bit difficult for me, which is why I am asking before comprehensively researching.
You could try to create a custom layout with virtualization or use an existing one VirtualizationCanvas.
You need a custom panel for placing your scheme items at correct places in your scheme.
Also, you should create a custom control, based on ItemsControl with custom ItemsControlItems for handling items creating and so on.
Then apply your layout as ItemPanleTemplate for ItemsControl or ListView, where ItemsSource would be bounded to your viewModel with scheme collection.
That's the easiest option, as for me.
So now I have made a small feasibility study on the topic. Add 1800 Buttons to a Canvas, add two mouse events to each of them individually and further add a MouseWheel event to the window to allow basic zooming of the "scene". Observations are on my 10-year old Intel Core2Quad Q6600 + NVidia GTX 460 based machine.
Application starts up fast. Exe is very small (as expected), 8k. Resizing the window (to see more or less of the docked canvas' contents) feels quite snappy. However near fullscreen (all Buttons visible) redraw becomes a little heavy (like ~5Hz refresh rate). App grabs itself 20M of memory instead of 10M with only 1 Button. Zooming is fast enough. Moreover, when zoomed in, the rendering gets noticeably faster (so clipping and such works well). This is good because being in the closely zoomed-in state is the most frequent case when working with larger diagrams. Response to button events is unimpaired in every respect.
Conclusion: using Buttons for diagramming seems doable. It is certainly suboptimal as a graphics app gauged against the capabilities of my machine, but it is still fast enough for the job. Maybe using Shapes instead of Buttons gets a little more out of it. But definitely no case for falling back on low-level graphics.
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="wpf1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="wpf1"
Width="513"
Height="385"
x:Name="window1"
MouseWheel="window1_MouseWheel">
<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" x:Name="canvas1" Background="#FFFFE8E8"/>
</Window>
...and...
public partial class Window1 : Window
{
double totalScale = 1;
public Window1()
{
InitializeComponent();
for (int i=0; i<60; i++)
{
for (int j=0; j<30; j++)
{
Button newButton = new Button();
canvas1.Children.Add(newButton);
newButton.Width = 25;
newButton.Height = 25;
Canvas.SetTop(newButton, j*30);
Canvas.SetLeft(newButton, i*30);
newButton.Click += new System.Windows.RoutedEventHandler(button1_Click);
newButton.MouseMove += new System.Windows.Input.MouseEventHandler(button1_MouseMove);
}
}
}
Button lastButton;
void button1_Click(object sender, RoutedEventArgs e)
{
lastButton = sender as Button;
lastButton.Background = Brushes.Blue;
}
void button1_MouseMove(object sender, MouseEventArgs e)
{
Button butt = sender as Button;
if (lastButton != butt) butt.Background = Brushes.Yellow;
}
void window1_MouseWheel(object sender, MouseWheelEventArgs e)
{
double scaleFactor = Math.Pow(1.0005,e.Delta);
totalScale *= scaleFactor;
canvas1.LayoutTransform = new ScaleTransform(totalScale, totalScale);
}
}
I have created a flashlight app for Windows Phone. As i already acces the camera to enable the flash, i thought it would be a nice feature to also allow the user to view the previewframes, and be able to zoom in to get a better look at things.
I have a canvas with a composite transform (as i also have to rotate the camera to match potrait mode). I use a slider (started with pinch, but i want one hand control) to increase and decrease the scale of the canvas, effectively zooming in and out. So far so good and on windows Phone 8 (HTC 8x) this works fine, i can go crazy on the slider and nothing bad happens.
On Windows Phone 8.1 (Lumia 930) however, at seemingly randoms moments when moving the slider, the app just closes down and in the debug output the following line is shown:
The program '[3164] TaskHost.exe' has exited with code -1073741819 (0xc0000005) 'Access violation'.
I have no idea how to find the cause of this as the moment of the crash seems to be different, its just Always related to the scaling of the canvas. I am hoping someone can point me in the right direction so i can solve this.
The canvas in XAML:
<Canvas x:Name="viewfinderCanvas">
<Canvas.Background>
<VideoBrush x:Name="viewfinderBrush"/>
</Canvas.Background>
<Canvas.RenderTransform>
<CompositeTransform x:Name="rt" />
</Canvas.RenderTransform>
</Canvas>
And my code for setting the scale:
private void Zoom_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (canZoom && !isFocusing)
{
zoomValue = Math.Round(Zoom.Value, 1);
rt.ScaleX = zoomValue * staticValue;
rt.ScaleY = zoomValue * staticValue;
ZoomValue.Text = zoomValue.ToString() + " x";
}
}
Where canzoom is set to true on the loaded event of the mainpage, and isFocusing is set to true upon the autofocus.
I just got the book Head First C# 3rd Edition. In the first part of the book it gets you started creating a simple game where you make bouncing circles that you must avoid while clicking and dragging a "human" to safety. The coding involves C# as well as XAML for the graphics side. Everything works except for one thing. Instead of clicking and dragging the "human" if I click on it it will "follow" the cursor around and the game behaves as if the cursor itself is the human since that is where game over happens when I hit one of the bouncing circles. I cannot figure out what I am doing wrong. I have looked over the code line by line next to the book.....The only thing I can figure out is that this block of code.
private void playArea_PointerMoved(object sender, PointerRoutedEventArgs e)
{
if (humanCaptured)
{
Point pointerPosition = e.GetCurrentPoint(null).Position;
Point relativePosition = grid.TransformToVisual(playArea).TransformPoint(pointerPosition);
if ((Math.Abs(relativePosition.X - Canvas.GetLeft(human)) > human.ActualWidth * 20)
|| (Math.Abs(relativePosition.Y - Canvas.GetTop(human)) > human.ActualHeight * 20))
{
humanCaptured = false;
human.IsHitTestVisible = true;
}
else
{
Canvas.SetLeft(human, relativePosition.X - human.ActualWidth / 2);
Canvas.SetTop(human, relativePosition.Y - human.ActualHeight / 2);
}
}
}
The book says to put the human actual width and height to 3. But if I do that and try to click on the person in the actual game it won't even register the cursor at all. but if I put that at 20 or above and I click on the human as I said earlier it starts following my cursor around and the rest of the game behaves normally.
Here is the code that is supposed to make the cursor be able to "click" and "drag" the human.
private void human_PointerPressed(object sender, PointerRoutedEventArgs e)
{
if (enemyTimer.IsEnabled)
{
humanCaptured = true;
human.IsHitTestVisible = false;
}
}
And here is the related XAML code this is supposed to be working with to make it click and drag but instead is making it follow my cursor around the area:
<StackPanel x:Name="human" Orientation="Vertical" Canvas.Left="17" Canvas.Top="15"
PointerPressed="human_PointerPressed" >
<Ellipse Fill="White" Height="10" Stroke="White" Width="10"/>
<Rectangle Fill="White" Height="25" Stroke="White" Width="10" />
</StackPanel>
Everything else seems to be working right. I have checked the code in the book over and over and gone to their site. Nothing I did is different as far as I can tell. If anyone can help it will be greatly appreciated.
After reading the Book "Head First C#, 3rd Edition"
I would say the problem isn't your code...
It's more about how he detects if a Human get captured, because he does not check if "human" collide with a "enemy" he just did some Pointerevent's which will get triggert if a Pointer (your Mouse) will enter an "enemy" and not if your "human" collide with him...
I have a WPF app that draws a compass. There is a large ring with tick marks and labels. I have a checkbox that toggles the compass graphics on and off. When I first start up the app, the compass turns on and off instantly.
Meanwhile, I have a combo box that grabs some data from a local database and uses that to render some overlay graphics. After using this combo box, the compass graphics no longer toggle quickly. In fact, the UI completely freezes for about 4 seconds whenever I click the checkbox.
I attempted to profile my app using Window Performance Profiling Tool for WPF. When I activated the checkbox, not only did my app freeze, so did the profiler. The graphs "catched up" afterward, but this tells me something must be seriously wrong.
I've managed to nail down that the problem graphics are the tick marks (not the numeric labels). If I eliminate them, the freezing problem stops. If I cut them down from 360 to, say, 36, the app still freezes, but for less time. Again, no matter how many tick marks I have, they toggle instantly when the app first starts.
My question is, How do I figure out why the toggle for my compass graphics goes from instant to horribly slow? I've tried extensive profiling and debugging, and I just can't come up with any reason why setting the Visibility on some tick marks should ever cause the app to freeze.
Edit
Okay, I've stripped everything out of my app to just the bare essentials, zipped it up, and uploaded it to Sendspace. Here is the link (it's about 143K):
http://www.sendspace.com/file/n1u3yg
[Note: don't accidentally click the banner ad, the real download link is much smaller and lower on the page.]
Two requests:
Do you experience the problem on your machine? Try opening Compass.exe (in bin\Release) and clicking the check box rapidly. The compass tick marks should turn on and off with no delay. Then, select an item from the combo box and try rapidly clicking the check box again. On my machine, it's very laggy, and after I stop rapid-fire clicking, it takes a few seconds for the graphics to catch up.
If you do experience the lag, do you see anything in the code that could be causing this odd behavior? The combo box is not connected to anything, so why should selecting an item from it affect the future performance of other graphics on the window?
Although ANTS didn't indicate a particular performance 'hotspot', I think that your technique is slightly flawed as it seems that every tick has a ViewModel that is responsible for handling an individual tick, and you are individually binding those ticks to the view. You end up creating 720 view models for these ticks that fire the a similar event each time the entire compass is shown or hidden. You also create a new LineGeometry every time this field is accessed.
The recommended approach for WPF in a custom drawn situation like this is to use a DrawingVisual and embrace the retained mode aspect of WPF's rendering system. There are several googleable resources that talk about this technique, but the gist is to declare a compass class inherits from FrameworkElement, and some smaller classes that inherit from DrawingVisual and use that to render the compass. With this technique, you can still have a ViewModel drive the compass behavior, but you wouldn't have individual viewmodels for each part of the compass. I'd be inclined to decompose the compass into parts such as bezel, arrow, sight, etc... but your problem may require a different approach.
class Compass : FrameworkElement
{
private readonly List<ICompassPart> _children = new List<ICompassPart>();
public void AddVisualChild(ICompassPart currentObject)
{
_children.Add(currentObject);
AddVisualChild((Visual)currentObject);
}
override protected int VisualChildrenCount { get { return _children.Count; } }
override protected Visual GetVisualChild(int index)
{
if (index < 0 || index >= _children.Count) throw new ArgumentOutOfRangeException();
return _children[index] as Visual;
}
override protected void OnRender(DrawingContext dc)
{
//The control automatically renders its children based on their RenderContext.
//There's really nothing to do here.
dc.DrawRectangle(Background, null, new Rect(RenderSize));
}
}
class Bezel : DrawingVisual
{
private bool _visible;
public bool Visible {
{
get { return _visible; }
set
{
_visible = value;
Update();
}
}
private void Update()
{
var dc = this.RenderOpen().DrawingContext;
dc.DrawLine(/*blah*/);
dc.Close();
}
}