Wednesday, December 21, 2011

DirectX Applications in WinRT

Windows 8 Metro Style apps can either use XAML as I discussed before, or they can use DirectX. This is great for Delphi because our in our newest framework FireMonkey the canvas is powered by DirectX on Windows platforms. So once we can create a DirectX, we should be able to turn that into a FireMonkey canvas, and the full power of FireMonkey should be available to the Metro Style application.

Let's look at what Visual Studio generates when you specify a new DirectX application, and try to recreate that with Delphi. Earlier I looked into creating an application using Windows.UI.Xaml. Application, but in order to use DirectX, you must activate the lower level Windows.ApplicationModel.Core.CoreApplication class, and construct your graphics device and surface from the ground up.

As an aside, internally, the Xaml Application has a CoreApplication as well. At //build/, Matt Merry gave a talk on Windows Runtime internals: understanding "Hello World" where he shows the internals of a simple hello world Xaml application, debugging down to the CoreApplication level. This is a must watch talk if you want to truly understand the inner workings of a WinRT application.

Back to Delphi. Like my first post about Xaml, this is all pretty rough and I don't think the code is ready to share as a complete project. But I'll discuss the finer points of most of it.

procedure Main;
var
  insp: IInspectable;
  factory: TViewProviderFactory;
begin
  Set8087CW($133f);  // Because we're using DirectX, disable all FPU exceptions
  factory := TViewProviderFactory.Create;

  OleCheck(RoGetActivationFactory(TWindowsString(SCoreApplication), ICoreApplicationInitialization, insp));
  (insp as ICoreApplicationInitialization).Run(factory);
end;
There's a bit of a difference here between the Xaml application and this one. Here, we're accessing ICoreApplicationInitialization from Windows.ApplicationModel.Core.CoreApplication. ICoreApplicationInitialization represents a set of static methods that is available for this class -- there is no instance of the CoreApplication object. Instead, we just need to provide the CoreApplication class with a IViewProviderFactory which knows how to create a runnable view. My implementation of IViewProviderFactory is simple:
type
  TViewProviderFactory = class(TInspectableObject, IViewProviderFactory)
    function CreateViewProvider: IViewProvider; safecall;
  end;

{ TViewProviderFactory }

function TViewProviderFactory.CreateViewProvider: IViewProvider;
begin
  Result := TViewProvider.Create as IViewProvider;
end;
The ViewProvider is also pretty boilerplate:
type
  TActivationEntryPoint = ( Unknown, DirectXApplication );  
  TViewProvider = class(TInspectableObject, IViewProvider)
  private
    FWindow: ICoreWindow;
    FView: ICoreApplicationView;
    FActivationPoint: TActivationEntryPoint;
  public
    procedure Initialize(window: ICoreWindow; applicationView: ICoreApplicationView); safecall;
    procedure Load(entryPoint: HSTRING); safecall;
    procedure Run; safecall;
    procedure Uninitialize; safecall;
  end;

procedure TViewProvider.Initialize(window: ICoreWindow; applicationView: ICoreApplicationView);
begin
  FWindow := window;
  FView := applicationView;
  FActivationPoint := TActivationEntryPoint.Unknown;
end;

procedure TViewProvider.Load(entryPoint: HSTRING);
begin
  if string(entryPoint) = 'DirectXApplication.App' then
    self.FActivationPoint  := TActivationEntryPoint.DirectXApplication;
end;

procedure TViewProvider.Run;
var
  View: TD3DView;
begin
  if FActivationPoint = TActivationEntryPoint.DirectXApplication then
  begin
    View := TD3DView.Create(FWindow, FView);
    try
      View.Run;
    finally
      View.Free;
    end;
  end;
end;
Once the application successfully gets a ViewProvider, It calls it's Initialize providing a Window and an ApplicationView. It then calls Load, specifying an entryPoint. This entry point is associated with Windows Application Contracts, and for now we're just looking at the launch contract. The entry point for the launch contract is going to be the entry point you specified in your appxmanifest, in the Application node. Here I've specified 'DirectXApplication.App'. After it's been Loaded, Run gets called, and we construct and run a View.

type
  TD3DView = class(TInspectableObject)
  private
    FWindow: ICoreWindow;
    FView: ICoreApplicationView;
    FRenderer: TD3DRender;
  public
    constructor Create(window: ICoreWindow; applicationView: ICoreApplicationView);
    procedure Run;

    procedure OnResize(sender: ICoreWindow; args: IWindowSizeChangedEventArgs);
    procedure OnDpiChanged(sender: IInspectable);
  end;

constructor TD3DView.Create(window: Windows_UI_Core_ICoreWindow;
  applicationView: Windows_ApplicationModel_Core_ICoreApplicationView);
var
  insp: IInspectable;
begin
  FWindow := window;
  FView := applicationView;

  // The default mouse cursor is the busy wait cursor, switch to a normal pointer.
  RoGetActivationFactory(TWindowsString(SCoreCursor), ICoreResourceFactory, insp);
  FWindow.PointerCursor := (insp as ICoreResourceFactory).CreateCursor(CoreCursorType.Arrow, 0);

  // Hookup events to update display if the Window size changes or the DPI changes
  FWindow.add_SizeChanged(TResizeHandler.Create(Self.OnResize));
  RoGetActivationFactory(TWindowsString(SDisplayProperties), IDisplayPropertiesStatics, insp);
  (insp as IDisplayPropertiesStatics).add_LogicalDpiChanged(TLogicalDpiChangedHandler.Create(OnDpiChanged));

  // Create a D3D render target
  FRenderer := TD3DRender.Create(FWindow);
end;

procedure TD3DView.Run;
var
  Timer: TStopwatch;
  lastTime, currentTime, frequency: Int64;
  timeTotal, timeDelta: single;
  insp: IInspectable;
begin
  FWindow.Activate;

  RoGetActivationFactory(TWindowsString(SDisplayProperties), IDisplayPropertiesStatics, insp);
  FRenderer.SetDPI( (insp as IDisplayPropertiesStatics).LogicalDpi );

  Timer := TStopwatch.StartNew;
  lastTime := 0;
  frequency := TStopwatch.Frequency;
  while True do
  begin
    currentTime := Timer.ElapsedTicks;
    timeTotal := (currentTime) / frequency;
    timeDelta := (currentTime - lastTime) / frequency;
    lastTime := currentTime;

    FWindow.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessAllIfPresent);
    FRenderer.Update( timeTotal, timeDelta);
    FRenderer.Render;
  end;
end;
Here we see that the view plays a similar role as a message loop in a Win32 application. As long as we're running, we tell the application to process events that have occurred, update the render target, and finally present it to the screen. Here I'm using System.Diagnostics.TStopwatch to notify the render target how long it has been since the last update. Microsoft's DirectX Application template does something similar with QueryPerformanceCounter, which is actually the same thing TStopwatch does behind the scenes, although TStopwatch has the benefits of falling back on lower resolution timers if no high resolution is available, and it is available cross platform. It might be useful in your apps as well.

The details of my TD3DRender are pretty standard Direct3D calls, create a device, render target, back buffer stencil, swap chain, back buffer texture, and the only painting I'm doing is clearing to blue. The code is essentially a direct translation of everything in the Visual Studio's template D3DRenderer.cpp, and I don't think it adds anything by including it here. The only new thing is there's a new method on IDXGIFactory2 for D3D11, CreateSwapChainForImmersiveWindow. In Metro Style apps, you don't have access to Window handles, so we can't create the render target in with the usual arguments; instead we have to specify the IWindow.

With that, I have a DirectX application written in Delphi. One thing I noticed is I cannot run it as a normal executable the way I could with the XAML application I made. The call to Windows.ApplicationModel.Infrastructure.CoreApplication.Run(factory) fails with HRESULT $80004005, "Unspecified error". I'm not sure what happens differently when running from the Desktop, but I assume there is some initialization that doesn't happen. So in order to actually run this, I had to package and install it. I suspect it might be related to new compiler/linker features being needed for Windows 8. csc.exe and cl.exe both have new options specific for Windows8. "/t:appcontainerexe" to build an Appcontainer executable makes me think there might be something different they're linking in as a hint to Explorer; that's clearly not something the Delphi compiler is doing but something we need to investigate.

19 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Well done , Nice article .I heard that WinRT native UI controls cannot be used with Direct X apps(At least until now).I feel if we go behind DirectX we may need to lose many features of Metro UI.

    Again, well done for your involvement in Delphi and WinRT

    ReplyDelete
  3. By "native UI controls" you mean the XAML controls? Check out my older posts on the viability of Delphi using and consuming XAML based Metro UIs, it's very possible.

    I think you should be able to create a XAML view and a DirectX view in the same application and swap between them (don't quote me on that :-) ), but yes they are mainly mutually exclusive. The reason DirectX is interesting to Delphi is because DirectX backs FireMonkey, and the features from FireMonkey would then be available to you.

    And thanks for the feedback!

    ReplyDelete
  4. Wow, such a big and detailed instruction, very like such things! Yesterday I tried to do like u wrote, and fortunately I could fixed my problem! Thanks a lot, and in return I would like to recommend u one nice site https://yumdownload.com/directx where u can always find only the best versions of all soft including DirectX!

    ReplyDelete
  5. Games entered our life very quickly, with the help of games, we can immerse ourselves in the virtual world and for a few hours forget about our problems and bad mood. But still there are times when after the installation of the game there are various problems. It happens that sometimes there is not enough of a file to run the game. One of the most common problems when there are not enough files for the DirectX game library. One of the most common errors is an error when there is no d3dx9_43.dll and this error does not allow the game or application to start. This file allows you to enable some graphics accelerator functions (Graphics Card). Eliminate this error is simple enough and does not take much time, just download the d3dx9_43.dll missing https://fix4dll.com/d3dx9_43_dll file to your computer and install it in the correct folder. You can download this dll-library from website completely free of charge.

    ReplyDelete
  6. Every product is especially designed to give every office employee or other customers a comfortable sitting posture without any back pain.
    Our products can also be customized according to a client’s requirement.
    So, the next time you go to shop for sofas, tables or chairs though it be for any purpose don’t forget to have a look at our beautifully designed range of products.
    Though we manufacture different products but among all office furniture chairs are the most demanded. We not only deal with local clients but also with national and international clients. The products that we manufacture are supplied to various offices, hospitals, auditoriums, cafeteria, homes and schools. The one reason for being the best office chairs suppliers is due to the durable, economic, comfortable and cost-effective products it manufactures. Our company is so well equipped and makes use of latest technology that it is able to manufacture more than 5,000 chairs every year.


    Chair Manufacturers in Mumbai
    Chair Supplier in Mumbai
    Office Chair Supplier in Mumbai
    Visitor Chair Supplier in Mumbai
    Chair Dealers in Mumbai
    Top Chair Manufacturers in Mumbai
    Best Chair Manufacturers in Mumbai

    ReplyDelete
  7. With the modification in the economic state of affairs, factors like globalization of markets, international economic amalgamation, reduced obstacles to business and trade and increase in competition have given rise to the requirement of transportation. It has become one of the most vital infrastructural essential for the expansion of business in today’s era.
    Packers and Movers in Gachibowli
    Packers and Movers in Kukatpally

    ReplyDelete

  8. Indian Packers and Movers in Mumbai give packing and moving services like loading & unloading, packing & unpacking, car carriers, transportation, domestic &local shifting, international shifting, Industrial shifting, corporate shifting, Warehousing, etc. Packers and Movers in Mumbai achieve conceit in offering great packing and moving services at reasonable costs. We deal all types of packing and moving services in Mumbai and other main cities of India.
    Packers and Movers in KhargharPackers and Movers in Jogeshwari
    Packers and Movers in Kharghar
    Packers and Movers in Dombivli
    Packers and Movers in Thane West

    ReplyDelete
  9. This comment has been removed by the author.

    ReplyDelete
  10. Such a beneficial article. Your article is very interesting, and I really enjoyed it. I would like to thank you for the efforts you had made for writing this awesome article for more related topic on COVID 19 Awareness Wall Sticker Printing

    ReplyDelete
  11. Nice post! This is a very nice blog that I will definitively come back to more times this year! Thanks for informative post.# @ Packers and Movers Kolkata


    ReplyDelete
  12. Spending some quality experiences in the arms of the busty girls is truly unique.Busty Call Girls in Manali Feel the heat of the models often and have fun with their curvy bodies.Call Girls Booking in Manali Relishing great entertainment with these girls could make you feel better than ever.Punjabi Escorts in Agra Simply hire the right partner and make love with her passionately.Low Rate Call Girls in Dehradun The close companionship that you can make with the sizzling girl would be spectacular.Low Rate Call Girls in Dehradun So, ensure to do all types of hot moves and feel the heat of the beautiful companions.High Class Call Girls in Faridabad Feel the heat of the body of the dazzling girl and make love with her figure passionately.

    ReplyDelete
  13. Let's look at what Visual Studio generates when you specify a new DirectX application, and try to recreate that with Delphi. Earlier I looked into creating an application using Windows.UI.Xaml. Application, but in order to use DirectX, you must activate the lower level Windows.ApplicationModel.Core.CoreApplication class, and construct your graphics device and surface from the ground up.

    ReplyDelete
  14. I looked into creating an application using Windows.UI.Xaml. Application, but in order to use DirectX, you must activate the lower level Windows.ApplicationModel.Core.CoreApplication class, and construct your graphics device and surface from the ground up9apps apk

    ReplyDelete