== title slide == Good morning; my name is Zeb Figura, and I’m here to talk about what’s happened in the world of Direct3D since the last WineConf talk in 2019. There’s been quite a huge amount of progress in the past three years, so I’d just like to touch on the biggest new developments, and leave everything else for the changelogs. == next slide - about me == To give a bit of background, I first joined the Wine project a little over five years ago, in 2017. I was recruited by CodeWeavers a few months after I started contributing to Wine. My work on Wine has been in a variety of different areas since I started working on the project; by now there’s very few places I haven’t touched at least a little. I have spent a lot of my time on multimedia, low level I/O, and plug-and-play, and since early 2019 I’ve also spent a great deal of time working on Direct3D. Earlier this year Henri Verbeet passed on the rôle of Wine Direct3D maintainer to myself and Jan Sikorski. With that said, let’s get into the interesting news about Direct3D. == next slide - vulkan backend == The biggest and most exciting thing we have to report is the existence of a new Vulkan-based renderer for Direct3D 10 and 11. This lives alongside the GL renderer, and in its current state works as well as or better than the GL renderer for most Direct3D 10 and 11 programs. It can be enabled through the registry or through an environment variable. This renderer was known as “Damavand” while in development, and is still sometimes called that as a nickname. Some of you may have heard this before and been unclear what it refers to, so to clarify, it refers only to the Vulkan backend of wined3d; the GL backend doesn’t have a codename. The Vulkan renderer does not yet offer 3D support for Direct3D9, 8, or lower. In practice this means that some applications using Direct3D9 can run (for example, the builtin DirectShow video renderer actually works), but anything using the 3D pipeline does not work. The main blocker here is that we currently don’t have support for compiling shader model 1-3 shaders or the fixed-function pipeline. There is in-progress work on this, but given that Direct3D9 support over OpenGL works well it’s not been a high priority. The Vulkan renderer uses libvkd3d-shader to compile DirectX shader bytecode to SPIR-V format, so there is an optional out-of-tree dependency. As of earlier this year libvkd3d-shader is bundled in Wine, although it can also be built independently. This Vulkan backend, exciting as it may seem, was developed out of necessity. Our hand was essentially force due to OpenGL being abandoned both by Khronos and, notably, by Apple years earlier. There are certainly advantages to Vulkan’s API design, and the way it allows finer control over the hardware, that might have resulted in a Vulkan renderer being eventually written regardless. However, there’s also quite a lot of disadvantages, not least of which is the amount of time it took to write. I’d like to take some time to talk about both of these. == next slide - benefits == The Vulkan renderer is capable of a few things that are currently unimplemented in GL, things which may be difficult to implement or which are just missing from the language. Most of these are not really about new features, though, but rather inconsistencies or poor design in GL which were corrected in Vulkan. -- For example: * in GL there’s no way to bind an index buffer at an offset when doing an indirect draw; * there’s some extensions written with a hardcoded list of formats rather than (or in addition to) feature query bits, for example storage images and pixel buffer objects; * GL has a limit on the number of texture image units which can be used from a shader; this limit can be avoided with ARB_bindless_texture, but this takes some work to implement correctly; * there is no way in GL to create a 2D texture view of a 3D resource. -- All of these things have affected real games, and these games work just fine with the Vulkan renderer. This is great news that they’re fixed now—provided you have working Vulkan drivers—but on the other hand it would have been nice to have them with GL as well, and it still would be nice. All of these things the hardware is capable of. The other main benefit that we’ve observed from the Vulkan backend is better performance, relative to the GL backend, across a variety of games. The cause of this performance is not something we’ve specifically looked into, largely because we don’t really have time for things like that. In general, though, we've put more work into Vulkan-specific optimizations, and while we often could provide similar optimizations for the GL backend that are almost as performant or as performant, we don't have the time and effort available. [There are ways in which the cleaner API design of Vulkan with respect to things like threading and context management might have helped if we were writing a Direct3D implementation from scratch, but essentially these were problems we already had to solve for GL, and so Vulkan didn't help us at all there.] -- One major cause, though, is that the index buffer offset problem mentioned in the last slide actually prevents applications from using streaming index buffers in GL efficiently. With Vulkan there is no such problem. Apart from that, though, -- == next slide - drawbacks == There are some downsides to the Vulkan backend, however. These aren’t necessarily related to functionality—in general Vulkan is capable of doing everything OpenGL can do, on hardware which it supports. There are things that are not yet implemented in Wine specifically, but these will be eventually addressed. Rather, some aspects of Vulkan are less than unilaterally advantageous. One of the most prominent of these is that pipeline compilation is expensive. This in practice causes a lot of stutter in applications. It can be mitigated by using an on-disk cache, but this does have an oft-ignored cost in disk space, startup cost, and like all things, it takes time and effort to develop. Fortunately, Vulkan is a mutable API, and more recent Vulkan extensions may help address this in a more efficient manner. Vulkan also is not universally supported. This is less of a concern as long as OpenGL drivers can cover what Vulkan cannot, but on the other hand, there are basic features missing from OpenGL that we currently can’t do anything about if the hardware doesn’t support Vulkan either. There is currently no Free Software driver for NVidia GPUs, which means that anyone wanting to use Direct3D software on NVidia hardware, who is unable for one reason or another to use NVidia’s proprietary drivers, can only make use of the Wine OpenGL backend. I’d also like to take a moment to address a related point. There are some people who have the impression that Wine’s Direct3D implementation has the primary or even only purpose of supporting old hardware, or that it *should* have this purpose. There are also other people who assert that only the most recent hardware and drivers are worth supporting, and often fail to realize what kind of older hardware is still in use. We Wine Direct3D developers take the position that everyone’s hardware is worth supporting. We aren’t going to pessimize newer hardware just to optimize support for older hardware, but we also aren’t going to drop support for older hardware just to make it easier to support newer hardware. We try to support everyone's hardware, with the best performance and compatibility possible. And, lastly, perhaps the most important disadvantage of developing the Vulkan backend is simply that it took so much effort to develop. Vulkan is a completely different API than GL, and it makes the application developer do a lot more things manually. It also uses a binary shader format, which is harder to write shaders in; this is especially a concern when we have to compile most of our shaders at runtime. We spent multiple years of time on the Vulkan backend, time which could have been spent on other high-priority tasks, like improving Direct3D 11 support, improving performance, and implementing the HLSL compiler; all things which were delayed as a result. While Vulkan is a huge improvement over OpenGL in terms of API cleanup, there is an associated cost that’s often ignored. == next slide - general performance improvements == Besides the Vulkan backend, one other thing that we’ve recently spent a lot of time on is improving Direct3D performance. One of the biggest items on this list is map acceleration. Like most modern GPU drivers, we offload most GPU work onto a single command stream thread. This was written in order to correctly implement multithreaded Direct3D on top of GL, which in its current form is not the most thread-friendly API. Happily, the command stream also can provide a significant increase in performance. However, in our case this performance improvement was often nullified because resource mapping had to be synchronous, effectively waiting for any CS commands affecting that resource to finish. This has been the single biggest performance bottleneck in games, and especially Direct3D 10 and 11 games. Earlier this year, we submitted patches that get rid of this bottleneck. We now allow streaming maps to skip the command stream in most cases, which means that a lot of games are now a lot closer to their Windows performance. There are a lot of other things that we spent effort on performance-wise. I don’t want to spend too much time listing them all. One thing we have done recently is remove locks from Direct3D 11 deferred contexts, which is a first step towards breaking up the big wined3d lock into more granular pieces. [Another of the larger things we did is to split Direct3D 8 and 9 “managed” textures into CPU and GPU copies, which matches the Windows behaviour more closely and also allows us to send the uploads and downloads through the command stream.] == next slide - HLSL compiler == [I’m not sure what it makes sense to talk about here. I don’t want to repeat anything Giovanni says, but I do want to give a brief summary.] Another interesting new feature implemented in the past three years is the HLSL compiler. This is something that a lot of applications need. It’s been a lower priority for a long time because there are native redistributables, but we’ve wanted to implement a builtin version, and over the past three years we’ve gotten it to a state where it can compile quite a few real shaders. Of course, there is also quite a lot that the HLSL compiler can’t do yet. == next slide - direct3d 12 == We’ve made a few major strides in the Direct3D 12 implementation since 2019 as well. We’ve unfortunately not had much effort available to spend on it, but in the past couple of years Conor McCarthy has helped to improve performance by rewriting the descriptor heap implementation, as well as improving fence performance and compatibility by rewriting the fence implementation using timeline semaphores. We also now have support for shader model 5.1 register spaces and descriptor arrays. Currently the biggest blocker for most Direct3D 12 applications is lack of support for Shader Model 6, which uses a completely different binary format, but as Giovanni mentioned in his talk, we're excited to report that this is being worked on, and we hope to have it ready soon. == next slide - vkd3d library == Aside from functional changes and new features in the vkd3d library, which have already been covered, we have two other pieces of news regarding that library. The first is that as part of the PE conversion, the latest version of vkd3d and vkd3d-shader are bundled with Wine, so that it can be built without needing an external PE dependency. The second piece of news is that we changed the release schedule this year for vkd3d. Previously, due to a lack of available work, review of API design for vkd3d-shader, and a desire to get specific features into new vkd3d versions, we had a very slow release schedule. We now have a strict time-based release every 3 months. The month before release is designated a soft API freeze, during which we review API changes introduced during the previous merge window. Any further API changes will be deferred to the next release. During this time the implementation also becomes progressively more frozen, to ensure that the release is the most stable. == next slide - other GPU == And finally I’d like to mention some of the work that has gone into the general GPU-acceleration world outside of Direct3D. [This will probably be talked about already in the keynote.] One of the most difficult parts of native WoW64 support, and one of the last parts that we still haven’t quite finished, is the GPU libraries. There are two things that make this difficult. Firstly, the list of functions that need to be thunked from 32 to 64 bits is much bigger than in most other modules, and in some cases it is constantly changing, which means we need to write scripts to generate these thunks. This is especially difficult with Vulkan, which has a much more complex API surface than GL or CL, due to its use of chained structures, structure arrays, and so on. The other difficult thing about WoW64 support with GPU libraries is that most of them involve some form of mapping GPU resources, and unfortunately all of these APIs return a pointer. This means that in WoW64 we have a 64-bit pointer, which we can’t return to a 32-bit process. Fortunately all of these APIs have well-established extension systems, so this is a fixable problem. Several people, including Jason Ekstrand and Derek Lesho, have already started work on a Vulkan extension that allows us to control where the pointer is placed. Currently there are no similar extensions for GL or CL yet. Another thing that has been added in the past two years is support for OpenCL 1.2, as well as some CL extensions. This is a step up from our previous support for only CL 1.0, without any extension support. That said, we’re still quite a ways behind the current OpenCL 3.0, and we don’t support many other extensions. And finally I’d like to give a mention to the efforts of Liam Middlebrook and Georg Lehmann, who have been keeping our Vulkan wrappers up to date and helping to support more extensions.