Wednesday, August 16, 2017

resizing-a-t-viewport-3d

I have almost finished an application or two. The two applications are using the same technology. Both are Delphi FMX applications which use a TViewport3D as the main component which fills the whole client area of the window, at least most of the time. There have been problems with resizing the component. Note that I am writing a multiplatform HD application, not the 3D application. There is a little difference between the two. I have always been using the HD application where the TViewport3D is a normal component on the form.

This blog post is a summary. I am writing this without looking at the source, no IDE open, and I will not show code. (I have reset the template of this blog to the default without any support for syntax highlighting because the browser complained about using http link to the resources and I do not want that.)

Back to the TViewport3D, and from here on I will just say Viewport. The first problem is obvious when you look at the Resize method. The Viewport will destroy the current context and create a new one whenever the component is resized.

First thing I did was to ensure that the context is not destroyed but kept alive when resizing. This has revealed a bug. I need to set one more buffer resource to nil. It is in method DoResize of TDX11Context. This is where other resources are also set to nil, for example the render target. You want to add FCopyBuffer to the list (RSP-18850).

Then I was looking at the number of times resize is called during a manual resize of the application window with the mouse. I needed to handle a windows message not handled so far and act only when resize has finished (WM_EXITSIZEMOVE, RSP-18851). You will always have a hard time debugging or finally improving something related to resize if you do not have that, I think.

It is important that the Viewport is not aligned to client and automatically resized. Just set the size of it whenever something has finished changing. When I say size, I mean Size. Do not set Width followed by Height, you want to use an instance of TControlSize. I will get back to alignment in a moment.

For some time, focus on application startup. How many times is the Viewport resized when the application starts up. It should be one.

Turns out that Width and Height are set to a default size in the constructor which triggers a resize. I got rid of that (RSP-17296). It felt much better, at that time my application did set the size of the Viewport to an area greater than zero, it was aligned to client area of the parent.

I found out that the second application behaved better than the first and why. In the process, I switched alignment to none which resulted in an interesting situation. Width and Height of the Viewport were initially zero. This means that the context is nil and will stay nil, until you set the size. In my code, there was one location where I needed to test for context nil. I did that and reached the next stage.

It was in the Paint method. If the context is nil, the bitmap is also nil. Paint should test for a nil bitmap and exit immediately without an attempt to draw anything (RSP-18852). With that in place I could start up the application with an empty Viewport. This makes a good test. You should be able to run the application with a Viewport area of zero without a crash. If the test passes you can go on and give it a decent size. Then it will create a context and draw as intended.

As I have said above, it should do this just once when the application starts up. Use a draw counter to check. If it instead will draw a few times during startup this will probably be OK. And if resizing seems to work without problems then this is good for you, but keep in mind there might be a problem.

Back to alignment. Do not align. Then size is not adjusted during a resize. Do this when resize has ended. (Nice that I have the exit resize event.) You will have more confidence that the application will survive wild excessive resizing by the end user, who does not seem to feel the pain.

My application uses a modified context class. I will create and use a special buffer resource on the GPU. There is a show stopper problem when the context is recreated too often. You may not see this in a normal application. But there is a potential problem with resizing a TViewport3D in 10.2.1 and before.