
I can load them, draw them, animate them (although I'm not doing this properly just yet), they can have shadows, they're lit, they're textured. Viewmodel support is there, alpha not yet.
Joint animations are being done on the CPU, vertex animations on the GPU. Joint animations will likely remain on the CPU (at least until I go to D3D11 where multiple constant buffers will enable this to be a candidate for moving).
Vertex animations on the GPU are about twice as fast as on the CPU, and retain full speed even when many models are on-screen at the same time (CPU vertex animations begin to slow down with even 2 models on an i7). Shadows are effectively free (at least I'm not seeing any measurable performance drop from them). The animation code is infinitely cleaner too.
Available constants register space limits an IQM to 80 joints. This will go up to 256 when I move to D3D11. This is a major cause of one source of unhappiness I have with the format - GLSL doesn't expose hardware limits like this but HLSL (and ARB assembly language) most definitely does. Unless you're actually aware that limits such as this do exist, and that they are enforced by the hardware (not the API, the hardware) then you're going to do something like overflow the hardware limit. With HLSL (and ARB assembly) you'll crash, with GLSL you'll just drop back to some kind of software emulation.
Yuck.
Sepaking of "yuck", with DirectQ I'm putting a lot of effort into cleaning out the loading and drawing code, getting rid of the matrix3x4_t stuff and replacing it with D3DXMATRIX. Yes, it's another 4 floats per matrix, but using D3DXMATRIX instead will reduce the lines of code, get rid of some really nasty-looking code, and get me SIMD operations for free.
"Memory wasting"? Hah!
I'm also planning a move to GPU vertex animation for RMQ, which will be very important as the ambitions there are more - errrmmm - ambitious. Of course, that 80 joint limit exists with ARB assembly language (no, moving to GLSL won't help, it still exists with GLSL but GLSL just pretends that it doesn't - hardware limit, remember) so I'll probably have to end up doing some jiggerypokery with the joints to work around it. Oh what a lark!
Tuesday, January 31, 2012
DirectQ IQM Support
Posted by
mhquake
at
11:48 PM
4
comments
Monday, January 30, 2012
DirectQ Update - 30th January 2012
External textures are almost done. Right now I'm left with trying to work out a solution for cases where textures have the same name but different images. Trouble here is that there are two standards for handling this that have evolved over the years, and ideally I'd like to support them both (previously I'd only supported the QRP standard, currently I only support "textures/mapname/", both are needed as the player could be using a texture pack that uses either). I guess I should be grateful that in this case it stopped at two standards...
Today I brought on RMQ BSP2 support and it works quite nicely. I seem to have lost fence texture support somewhere in a code transition, and I need to add IQM support to DirectQ as well, but my intention is to continue with a fairly minimal set of RMQ features - enough to run it without crashing, but you won't get the full effect.
I've also been doing a lot of code cleanup in some places. This has meant a dropping of some features once again, but overall it's getting good. About those features that were dropped...
Full Kurok support is one. In the end implementing full support was too invasive and I'm thinking that for mods that need more radical engine changes such as this an approach similar to that which I'm taking for RMQ is wiser - bare minimum to run it OK and point people at the dedicated engine that it's meant to be run with if they want the full thing. A large part of this is motivated by the suffering I endured with Nehahra support, by the way.
The ability to load external textures from any directory is gone. Instead there are certain well-defined paths which must be used, and for the most part these are the same as what you'll find in engines such as DarkPlaces, FitzQuake and Qrack (in some cases I've added a few extra, and I'm sticking to my guns by only supporting the DarkPlaces MDL/SPR naming convention - which model does "progs/s_light.tga" get loaded for otherwise?)
There's another I can't remember right now, but I do know that it'll be back with D3D11. Should I hold a contest? First to guess it?
Posted by
mhquake
at
8:50 PM
2
comments
Tuesday, January 24, 2012
DirectQ Update - 24th January 2012 - Here be Textures
I've started on reworking DirectQ's external texture loader, meaning that the release will be delayed.
I'd come really close to not doing this at all, but in the end it feels like the right decision: as this will be the last major D3D9 release it deserves to be a higher quality release, and it will give me a cleaner codebase to move to D3D11 from.
I won't go into the reasons why the external texture loader turned out the way it did, other than to say that with hindsight it was a clearly misguided case of trying to be all things to all people (being most things to most people and not creating a mess while doing so is infinitely preferable).
There's been a lot of new code and changed code in this version (even aside from the texture loader) so I expect that I'm going to have to do a patch version or two as follow-on, but for the most part I now feel that it's on fundamentally solid ground in parts of the codebase that previously weren't.
The major remaining things that I'm unhappy about are my use of the D3D effects framework for managing shaders, and memory allocation for server-side entities.
The effects framework is worth covering first, because my earlier HLSL ports didn't even use it at all. I brought it in to make some aspects of dealing with shaders easier, but there was a tradeoff that began to bite some time back, which is in terms of mixing and matching vertex and pixel shaders. I can't just take this vertex shader, pair it with that pixel shader and draw something - I need to write an effect pair for it. That's only a coupla lines of code, but as requirements to more flexibly handle options build up, the number of such pairs begins to balloon out of control.
My D3D11 code doesn't use the effects framework at all and is much cleaner and more flexible as a result. Things like a proper r_showtris mode (which I can't currently do without adding 30-odd more effect pairs) would be easy to add there.
The effects framework also has some small overhead of it's own, resulting in a few percent performance dip.
In the end I'm making the decision to not rework that for this release, as I'll need to be revisiting and reworking my shaders anyway when I move to D3D11. That's a job I would have had to do twice if I had taken it on now. For something like video startup and the modes list it's different - that's isolated in a very specific part of the engine and can be beaten into shape while not affecting how e.g. MDLs are drawn. The shaders permeate the entire renderer, are used everywhere, and one change in the handling of them will upset everything. That's the kind of job that you really only want to be doing once.
Server-side entities is not that big a deal by comparison; it's just a code quality and cleanliness thing. I do have this fear that some day it's going to explode in my face, but it's passed some fairly brutal tests so far and has been doing well enough to not merit raising it above other priorities.
Posted by
mhquake
at
10:24 PM
5
comments
Sunday, January 22, 2012
More on BSP Brush Model Lighting
I've been beating a little more on these, and am beginning to wonder if handling them in any kind of robust and performant manner would really necessitate an architectural overhaul.
The real problem with them stems from the fact that multiple entities can share the same model.
Because each entity must be in a different place, it follows that each entity can be affected by a different set of dynamic lights.
But because the same model is shared, it follows that each entity has the same lightmaps.
So, if more that one of the same model is onscreen at the same time, it means that you can't just update lightmaps in any kind of well-running manner. Instead you need to update them just before the model gets drawn, which results in lots of nasty updates of resources that are in use by the GPU at the time you try to do the update, and which means lots of pipeline stalls and slow downs.
The alternative is that the last set of lightmaps which were updated will be applied to all such models, meaning that the last such model drawn will be lit correctly and the rest won't.
Aaaarrrggggggggggghhhhh.
I'm sorely tempted to shift dynamic lighting over to attenuation maps. These bring their own complications in terms of multiple blend passes, working right with alpha surfaces, etc, but all of the other issues just go away.
Another thing is that I'd be tempted to do them in a Q3A style whereby they're just a relatively simple blend over the previous combined texture and lightmap, meaning that they wouldn't light up dark areas properly.
Quake technology is really really annoying at times.
Fortunately this is just a momentary blip on the road to D3D11, and - provided that I make the decision to go with full GPU lightmaps - all of these issues will be history.
Until then I'm thinking that I'll probably release with lightmap-based dynamic lighting of BSP models included, but with the warning that if you're playing a map where the mapper has gone nuts with them, there will be slowdowns.
Posted by
mhquake
at
11:52 PM
8
comments
More Lighting
DirectQ just got another important addition to lighting, which is that BSP brush models now get dynamic lights properly (there are a lot of issues with giving them lighting from the environment which only a fully realtime solution could satisfactorily resolve).
This resolves one major issue I have with this model type which is that if a mapper uses them for structural geometry or for props, and you fire a rocket in that direction, the rest of the environment is properly lit up but the poor lonely BSP model is left sitting there in the dark.
Personally I'd prefer if this model type was just taken out back and shot; it causes huge architectural complexities (there are even some performance tradeoffs I came out on the wrong side of with just dyanmic lights) and makes something that should be quite simple into a tangled mess. Mappers, for some reason, love them.
Posted by
mhquake
at
7:36 PM
0
comments
Saturday, January 21, 2012
Textures...
I spent a few minutes today cleaning up one thing with DirectQ textures; nothing major but an extra (and completely unnecessary) layer of abstraction was gotten rid of.
After that Heineken Cup Rugby kind of took hold and my time disappeared in a pleasant haze of beer and shouting at the television in a bar. Productivity will be restored tomorrow (some neat ideas brewing for RMQ...)
Posted by
mhquake
at
1:33 AM
1 comments
Friday, January 20, 2012
Update - 20th January 2012
Didn't get much done today; just some brief testing of DirectQ's refresh rate changing (it worked).
There are a few things I want to get stuck into shortly, however, so this is the deep breath before.
RMQ is having a review of it's 2D GUI code. The stuff originally inherited from QuakeSpasm by way of Fitz (or should that be the other way around?) was fundamentally fine (although I would have done it differently, and subsequently made some changes in that direction) but it was heavily geared around drawing a classic Quake layout.
That's just not going to cut it for the next thing I want to get started on, which is CSQC. Yes, I'm beginning to get the itch here, and enforcing a 320x48 viewport at bottom-center of the screen for drawing the status bar is not suitable for cases where CSQC may be used at any time to draw anything at any location on-screen (and with the engine not able to predict it in advance).
The classic Quake layout is going to remain in RMQ for sure, but the backend code behind it needs to be made more general; that includes management of which parts of the screen get drawn on, how scaling is handled, and so on.
So this is a pre-requisite for using CSQC to do this kind of stuff.
(While doing this I had a nice moment of technical epiphany where I realised that you didn't actually need either a projection or modelview matrix to draw your 2D GUI - the transform required is a single MAD instruction rather than 4 DP4s - a micro-optimization for sure but I think it's neat.)
Meanwhile other things are marching on, being wrapped-up, etc. More info about them as they become more solid.
DirectQ work is now slowing down after my recent "get stuck into the video code" fit. I still want to do something about the texture loader (with hindsight I view certain behaviours of it and the decisions behind them as being mistaken) but I'm uncertain if that's going to be in the upcoming release or the one after it.
My current thinking is that if it goes 7 days without any major changes then it'll be close to ready for release, which means that if I don't get started on that work by this time next week, it'll most likely be the one after that gets the texture loader fixup.
That one, of course, will be the D3D11 one.
Posted by
mhquake
at
12:42 AM
0
comments
Thursday, January 19, 2012
Musing on Lightmap Solutions
Over the years I've evolved a number of different solutions to the problem of updating dynamic lightmaps. This is one of the major bottlenecks in Quake and - depending on your hardware - is capable of dropping framerates down to single digit in extreme circumstances.
To set the context, GLQuake (in it's multitextured path) did some of the worst things possible with lightmap updates, and Quake II took it up another notch. The Quake II example is most relevant here as it applies to modded Quake engines that support coloured light. The format used didn't directly map to how the texture was stored in hardware, meaning that the driver has to swap the R and B channels CPU-side before updating, and it called glTexSubImage2D once per surface that needed to be updated, and directly before the surface was drawn, meaning that each such call incurred a pipeline stall (and there may have been hundreds or thousands of these per frame).
I have no doubt that drivers of the time did some awful things behind the scenes to allow this to run well - for run well it did. But run it on a lower quality modern driver and what's really happening tends to slap you in the face pretty hard. A few nips and tucks, some code reorganisation, and things do get much better, but there is still plenty of room for improvement remaining.
The current DirectQ solution uses something evolved from GLQuake. Lightmaps are stored as 4-component textures, updated on the CPU as required, then bulk updated to the GPU before drawing. A scaling factor is stored in the alpha channel to compensate for clamping, and the pixel shader uses this to evaluate the final light level.
There are a number of problems with this approach. There is a lot of shuffling of data around different memory buffers CPU-side before the lightmap can go to the GPU, the scaling factor requires a division (which the GPU will translate into two operations - a reciprocal and a multiply) and there is a potential for pipeline stalls if the CPU needs to update a texture that is currently being used by the GPU for drawing with (at worst there will be a fraction of the stalls that GLQuake/Quake II suffered from).
Different choices of texture format can help relieve some of this. I've tested 32-bit textures using 10 bits each for R, G and B, and 2 bits for alpha (or unused), which provide some extra headroom for dynamic range, but it still doesn't go as high as Quake needs (dynamic lights in particular can overflow it), meaning that you need to do clamping. The spare 2 bits are not enough for a scaling factor, and the choice remains to sacrifice bits of precision. Reading from this type of texture GPU-side is also slower than reading from a traditional 4-component 8-bits-per-component texture, and it does nothing about the problem of shuffling data through two different CPU memory buffers before you can transfer it.
64 bit lightmaps are another solution. With 16 bits of precision per colour component you can Lock a texture rectangle and write directly into it; Quake lightmaps can still overflow even that, so you need to bring your d_lightstylevalue multiplier down to 1 (instead of 22) which means you also need a scaling factor. That gets rid of all of the CPU-side memory shuffling and clamping, but reading from this kind of texture on the GPU is slower than anything mentioned so far. On the other hand the scaling factor is only a multiply, it's constant, and you have 16 bits spare if you want to use them for anything (you could, e.g, pack 2 components of a surface normal in there and rebuild the full normal in shader code). On balance this is the fastest method of all, but - of course - 64 bit textures aren't available on all of the hardware that DirectQ currently targets.
I have two different GPU lightmapping solutions in use, one in the RMQ engine (OpenGL, publicly released) and one in an experimental engine (D3D11, unreleased). Of these the D3D11 method is superior in terms of both performance and quality, but comes at the cost of significantly higher hardware requirements (the RMQ one would likely run on a GeForce 3).
The current RMQ method uses 3 textures per lightmap, with an optimal case that can only need one. Each texture represents one of the R, G and B channels of the final composited lightmap, and each colour component of a 4-channel texture represents one of the surface styles (whoever said that colour had to represent colour, eh?) The d_lightstylevalue values are loaded into the vertex shaders constants registers once per frame, each surface vertex contains 4 lookups (one per style) into this, these are used to build a vertex shader output and from there it's then a simple matter of doing a DotProduct with this output and the texture lookup for each channel to get the final image. (The version of the engine released with the recent demo was slightly different, by the way - the code has evolved some since then.)
The D3D11 method uses 4 textures, one per style on the surface, with optimal cases of 3, 2 or 1. A broadly similar approach is then used, with appropriate differences where required - it's just a matter of how the data is used and which operations are performed. The case where a surface has 2 styles is faster in D3D11, the case where a surface has 4 styles is faster in RMQ. The D3D11 method obviously has a higher texture memory requirement too. (The RMQ optimal 1 case is essentially identical to the D3D11 optimal 1 case, and it makes this decision during load time.)
Where things really diverge is with dynamic lights. RMQ does dynamic lighting per-vertex, which is a compromise given it's lower hardware requirements. In practice it looks fine - most of the time. There are cases where the limitation of per-vertex lighting come through - when a relatively small light is reasonably close to a relatively large triangle, for example. It also involves sending a lot of data to the GPU every frame - 3 dynamic light colours per vertex - but it remains faster than updating textures dynamically.
The D3D11 code essentially replicates GLQuake's handling of dynamic lighting but runs it on the GPU rather than on the CPU. This needs shader branching, bitwise operations in shaders, higher instruction counts, more powerful hardware, but gives much better quality. It also requires loading dynamic light positions and colours (and the radius of each light) into the shader constants each frame (the minimum of 256 constants registers, and the availability of constant buffers, makes this painless; it's also not an option for RMQ as the GL extensions I use only guarantee 96 registers and RMQ needs more than the 32 dynamic lights that standard GLQuake - and the D3D11 code - uses).
Both of these are actually marginally slower than updating textures where no dynamic lights or animating lightstyles are used, but they completely avoid the problem of hitching and uneven framerates caused by texture updates (frametimes can jump from 3ms per frame to 15ms per frame 10 times per second).
On paper the D3D11 code should be slower, but D3D11's closer mapping to how modern hardware actually works, together with improvements in shader model 4.0/4.1 (I haven't tried 5) bring the performance right up to and above the RMQ method. The D3D11 method, of course, also doesn't require an extra transfer of data per surface vertex from CPU memory each frame.
I've an itch to try a deferred renderer (build on my D3D11 code) but I'm holding back on that as I want to keep the current code reasonably intact for use as a reference when I'm porting DirectQ to D3D11 (I haven't yet decided if I'll use GPU lightmaps or 64-bit textures for that, by the way). It's also the case that I want to do some more research and probably write a simple experimental demo with one light before I jump into it for real. This will of course be the slowest method of all, but a fully real-time lighting and shadowing solution does have it's appeal.
All in all there is no perfect solution for lightmaps (at least that I've found). Everything involves some degree of compromise and it's typically a case of: performance, quality, low hardware requirements - pick two.
Posted by
mhquake
at
10:22 PM
1 comments
Wednesday, January 18, 2012
DirectQ Update - 18th January 2012
Another slight code reorganization - recovering default pool resources from a lost device or video restart has moved to the start of a frame rather than happening in place. This was done to prevent an old problem where you'd lose the device, then recover it, then a system event (such as a window resize) might cause you to lose it again immediately afterwards. Now it only recovers these resources once and only when it's been definitively determined that the device is good and we're running again.
This will all go away with D3D11, which much more sensibly has ResizeBuffers and ResizeTarget, allowing an almost complete bypassing of window system events (the only one you really need to handle is a WM_SIZE, and the handling for that is very well-defined, rather than the mix of voodoo incantations required by D3D9).
Mapshots have another small but important improvement. In order to take a mapshot properly it's necessary to go into a special mode in which 2D GUI overlay stuff is not drawn, then redraw the scene (twice because of the buffer swap), then exit the mode. In addition to the second screen redraw (required to put the original back buffer back where it was otherwise we'd be capturing from the former front buffer, which would still have the 2D stuff in it) this caused a momentary flicker on-screen. Simple solution was just to suppress the buffer swap at the end of the first redraw - goodbye second redraw, goodbye flicker, all is nice and solid looking.
That will also go away with D3D11 where I can just call R_RenderScene to overwrite the backbuffer, and will no longer need to fool around with BeginScene/EndScene calls and making sure that they're being called on the correct render target.
TGA screenshots are about 10 times faster than they were. This was inspired by my D3D11 screenshot code, and is mostly the D3D9 equivalent of it. The old code round-tripped through several IDirect3DSurface9s before saving out, which was painful. Screenshot gamma adjustment (scr_screenshotgamma, if I remember correctly) is now effectively free (previously it added a few more IDirect3DSurface9s to the round-trip - sheesh, what was I thinking?) PNG, JPG and DDS screenshots remain slow because of needing to compress, BMP screenshots are as fast as TGA, and I really recommend that nobody uses PCX (scr_screenshotformat). Only TGA has gamma adjustment right now, and is the recommended format. I'll very likely drop the other formats and go TGA only when I move to D3D11.
Not sure if I'm going to add multisampling to the video options menu after all; this would be nice to do but is complicated by the fact that available multisampling modes are dependent on the current video mode, and also the fact that it would be hugely stretching the capabilities of my current menu code to deal with that.
With hindsight I should have been passing a \0 delimited string (terminated with a second \0) to spin control types, but I'm not willing to do such an overhaul right now. Maybe another time.
Until then, the cvar is d3d_multisample, 0 (default) will disable it, values of 1 or up enable, the limit is hardware and video mode dependent, and be aware that some hardware can't support multisampling in windowed modes. It's name will be changing to vid_multisample in the next release, by the way (now that I have robust detection of when the mode list changes I want to go back to standardizing on "vid_" for all of these cvars - this will be the last time that happens; promise).
When exiting DirectQ from a fullscreen mode it will temporarily switch back to windowed before shutting down. This isn't for some cutesy reason like I think it looks cool or anything; it's a requirement of D3D11 and will be the behaviour once I port.
Posted by
mhquake
at
2:30 AM
11
comments
Monday, January 16, 2012
More Video Code
The main D3D-related video code is now down to 900-odd lines. That's about twice as much as my D3D11 version, but much of it is lost device handling, capabilities checking and fallback modes, which don't exist with D3D11.
Been spending a lot of time playing around with switching modes, Alt-Tabbing back and forth, and so on, and things seem solid and behave correctly.
An old DirectQ bug where Aero would be disabled if you switched from a fullscreen mode to a windowed mode has been fixed. This was due to some badly placed window resizing code.
A lot of the old window management code from GLQuake has gone. This was old stuff that was only needed because OpenGL forces you to do much of your own window management; D3D, being more tighly integrated with the windowing system, does much of it for you.
I now have a more robust way of specifying when the video mode list has changed and forcing the first run back to 640x480 windowed mode (required to protect you from accidentally specifying a bad mode from the new list). This means that I was able to drop my "d3d_mode" cvar and bring back good old "vid_mode".
I haven't yet worked out how resolutions and refresh rates are going to work together in the menus.
I've removed the ability to resize the window by dragging it's borders. This was more of a cutesy feature than anything that seemed essential, and it has interactions with the currently selected video mode that complicate things excessively.
I may yet go back and rework the modes list so that it exactly replicates GLQuake's list, with mode 0 being a windowed mode which may vary (depending on values of vid_width and vid_height) and the rest of the modes being the fullscreen modes. Depends on how things work out with refresh rates, but I have a hunch that the new code should be able to do it without too much mess.
I'm going to add multisampling to the video options menu. It was released in the last version but not especially advertised because I was uncertain how stable it was, but I'm happier with the code I have coming together now and am satisfied that I can provide a stable implementation.
The only remaining thing that I have any wobbles about is how to deal with cases where people try to force settings in their driver's control panel. I really wish that those things would just go away.
That's all for now.
Posted by
mhquake
at
11:45 PM
0
comments
The Problem with Refresh Rate
A very productive time fooling around with video modes - I know that I said I wasn't going to work any more on DirectQ's video code until I moved to D3D11, but I decided to put in some prep-work on restructuring in advance of this.
Historically, DirectQ hasn't supported refresh rate changes (aside from a brief time when I had it in but it crashed horribly). The reasons for this are a combination of two main factors: an initial desire on my part to keep the video mode list as close to ID Quake's as I could, and an inability to map the mode list returned by Direct3D to this.
Something had to go in order to enable this, and refresh rate was the chosen victim.
Most of the time when you see refresh rate offered as an option along with resolution, you'll see them as two separate options - first you pick your resolution, then you pick your refresh rate, then you hit an "Apply" option and away you go.
That's not actually the way things work at all.
Refresh rate is, in fact, quite closely tied to your chosen resolution. An example, I hear you cry. OK, here's the mode list from one of my machines:
| Mode | Width | Height | |
| 0 | 640 | 480 | 60 |
| 1 | 640 | 480 | 59 |
| 2 | 640 | 480 | 67 |
| 3 | 640 | 480 | 72 |
| 4 | 640 | 480 | 75 |
| 5 | 720 | 480 | 56 |
| 6 | 720 | 480 | 60 |
| 7 | 720 | 480 | 72 |
| 8 | 720 | 480 | 75 |
| 9 | 720 | 576 | 56 |
| 10 | 720 | 576 | 60 |
| 11 | 720 | 576 | 72 |
| 12 | 720 | 576 | 75 |
| 13 | 800 | 600 | 56 |
| 14 | 800 | 600 | 60 |
| 15 | 800 | 600 | 72 |
| 16 | 800 | 600 | 75 |
| 17 | 1024 | 768 | 60 |
| 18 | 1024 | 768 | 70 |
| 19 | 1024 | 768 | 75 |
| 20 | 1152 | 864 | 60 |
| 21 | 1152 | 864 | 75 |
| 22 | 1280 | 720 | 60 |
| 23 | 1280 | 720 | 59 |
| 24 | 1280 | 768 | 60 |
| 25 | 1280 | 800 | 60 |
| 26 | 1280 | 960 | 60 |
| 27 | 1280 | 960 | 75 |
| 28 | 1280 | 1024 | 60 |
| 29 | 1280 | 1024 | 75 |
| 30 | 1360 | 768 | 60 |
| 31 | 1360 | 768 | 75 |
| 32 | 1366 | 768 | 60 |
| 33 | 1366 | 768 | 75 |
| 34 | 1440 | 900 | 60 |
| 35 | 1440 | 900 | 75 |
A few things should be obvious from this, including the fact that no matter how much you may want a 320x200 mode for nostalgia's sake, if D3D don't report it then you ain't getting it.
The important one for this discussion is that not all refresh rates are available with all modes. Look at those ones available with 640x480 - where'd that 59 come from? And 720x480, 720x576 and 800x600 have a 56hz rate available that's nowhere else. 800x600 has 72 but not 70 whereas 1024x768 is the other way around.
So, what this comes down to is - when selecting a resolution, you also must select a refresh rate to run at as well, otherwise your mode selection is incomplete, and if you fall back on some kind of "default refresh rate" (like the one your desktop is running at) you risk crashing.
So when this finishes up I'm going to have perfect mode changing, perfect refresh rate selection and everything running clean and neat (I've been able to get rid of nearly 600 lines of disgusting code).
A few final notes.
I've begun to suspect that this might lie behind some of the problems that some people are having with trying to start up DirectQ. They may be running their desktop at a certain display mode and attempting to force a fullscreen mode for which their desktop's refresh rate is unavailable. If so - it's always worth trying to start up in a windowed mode instead. Windowed modes are safe, and if nothing else will confirm that the cause of the crash is in the transition to fullscreen.
Direct3D 11 enforces this even tighter. It won't even let you initially start in a fullscreen mode (well, it will, but the documentation issues dire warnings not to) - instead you must start windowed always - irrespective of the user preferences - and then switch to fullscreen if the user wants to. It also enforces that a fullscreen mode must be a properly enumerated mode, so there's going to be no "trying things out and seeing if they work".
Alt-tabbing and transitions between fullscreen and windowed modes have been made safer by placing the resetting/resizing of the window frame after the device reset. It's previous placement had actually caused a second device reset.
The video mode list is going to change again. To protect people from any unwanted consequences of this I'm going to change the cvar names, so your initial startup will be 640x480 windowed.
Posted by
mhquake
at
6:18 PM
0
comments
DirectQ Update - 15th January 2012
Some more work has been done on the input code, getting rid of a lot of jerkiness and roughness that manifested under certain circumstances. A lot of this was "low level" jerkiness that really wasn't too evident in regular gameplay, but could be reproduced by e.g. setting host_timescale to a low-ish value.
There are all kinds of subtle details in the input code and the timing of when you collect input, when you send it to the server, etc, that only appear when you disconnect the client timer from the server timer. Hopefully this is the last such issue.
None of this applies to RMQ which had my second version of this kind of code. DirectQ, being my first, gets to have all the bugs. Lucky DirectQ.
The mapshot code has been fully rewritten and now finally works as intended. The previous take used a fairly brute-force method of taking a smaller shot of the scene, which included creating a new render target at the smaller size, redrawing the entire scene to that render target, and saving it out. There were a host of edge cases where it broke badly, and I was never really happy with it, but for the most part it worked well enough and I wasn't too inclined to touch it until now.
Now all that I do is StretchRect from the backbuffer to a smaller surface, then save that out. It still needs to redraw the scene but that's just to get rid of the 2D GUI elements (otherwise your mapshot would be of the menu); there's no more fooling around with render targets going on, and it's about one-quarter to one-third of the previous code.
When I move to D3D11 this will change again, but the screenshot code I have for D3D11 (which is really quite good) will be able to handle most of what's required.
Some other small improvements in quality and stability mount up as usual; nothing too dramatic but overall I think that the last D3D9 release is going to be a nice one.
Posted by
mhquake
at
12:06 AM
0
comments
Sunday, January 15, 2012
Aaaarrrghhh! Patents!!!!
I've added DXT texture support to RMQ, with the intention of helping to reduce download sizes and speed up load times, but an obvious question arises: what if the end-user's hardware doesn't support the required extension?
My initial thought was to unpack the DDS to an RGBA format and upload that instead. We still get the reduced download size, and the player can play the game.
However, it appears as though unpacking a DXT compressed texture is patented. Now, it may be the case that the patent may be invalid, but who wants to take that risk?
Currently I have a few options.
I could drop DDS support altogether. Goodbye smaller downloads, goodbye faster loading, but the problems go away.
I could implement an alternative - like DarkPlaces does - and unpack to a lower resolution and lower-quality representation.
I could round-trip through an unpatented format (like S2TC) before unpacking.
I could enforce hardware support for compressed textures as a technical requirement.
None of these are ideal, and all of them provide a poorer experience for the player than what I would like. Currently I'm round-tripping, but quality drops off a little (I run a despeckle filter after the round-trip to reduce the worst of that) and there is an obvious increased load time. What I'd really like to do is just make the patent go away. We're obviously not out to make any money from this, and patent enforcement of this kind just makes the consumer suffer.
What really kills me is that I could do this legally in a handful of lines of D3D code - load the DDS texture into the scratch pool, create a regular texture as normal, get both of their level 0 surfaces and use D3DXLoadSurfaceFromSurface to transfer.
Posted by
mhquake
at
5:02 AM
2
comments
Thursday, January 12, 2012
DirectQ Roadmap - Revision
My previous intention has been to release one more DirectQ version - 1.9.0 - which continues to support legacy hardware, then remove certain items of legacy support from an intermediate version (1.9.5?) before moving to D3D11 for version 2.0.0.
I've changed my mind.
I'm still releasing 1.9.0 with legacy support, but after that I'm going to D3D11 and version 2.0.0 straight away.
There are a number of reasons for this, but the change of mind was specifically prompted by a recent occasion I had to look over the video mode code in order to try figure out why someone just couldn't get a windowed mode.
Anyone who's ever looked at DirectQ's video code knows that it's vile. It's a direct descendant of GLQuake's in fact, starting out at the original D3D9 port I did and then having features bolted on as the need arose, bits and pieces moved around, and emergency bugfixes shoved in at what seemed at the time to be the appropriate place. You can quite likely trace a very clear evolution from GLQuake's original through the various releases, but the code was never originally designed to support this kind of evolution in any kind of sane manner.
It's been obvious to me for some time that the code needs to be taken off life-support, and if you've followed my recent posts you'll have found me eulogizing about how clean and elegant the D3D11 code is by comparison. The obvious decision was to kill it at the same time as the move to D3D11, and bringing that move forward can only be a good thing in this regard.
I've also found that the D3D11 experimental engine is starting to evolve under it's own momentum; primarily because D3D11 is - for the most part - just so nice to work with, and partly because there are things I want to do in DirectQ but can't with D3D9.
That needs to stop and it needs to stop soon, because one potential end of that road is looking like an abandonment of DirectQ and taking up the D3D11 codebase as a new primary working codebase.
So there we have it - one more D3D9 release and then the move begins.
There are some decisions to be made about items like feature level support, required shader model, and so on, but I'll talk about them another time.
Posted by
mhquake
at
7:47 PM
0
comments
Wednesday, January 11, 2012
And the Awesome continues
Speeds of the D3D11 engine are coming right up and it's looking as though it's shortly going to be faster than DirectQ in all cases.
D3D11 is quite different in how you need to structure things than OpenGL or even D3D9; you can't just blast out a few drawing commands and hope that the driver will be able to figure out the best thing to do on it's own. Instead you actually have to sit down and think, plan, etc, the most optimal approach. In fairness there are plenty of hints in the the way that the API works that will guide you towards that approach.
A good general rule seems to be something like: if you find that you're taking the long way round, or things are getting messy, then you're probably doing it wrong.
The other side to this approach is that when you do get everything nicely locked in, it pays you back in spades. Your code is cleaner and more maintainable, and you get very high performance.
Take constant buffers as one example. In the old days you'd just set shader constants as and when required, but that's gone in D3D11; instead you have to load everything into a constant buffer and set that as currently active. If you try to do this the old way you'll make a mess and run slow, so instead what you do is create one great big constant buffer that contains almost everything you need that's common between all objects, and upload that once at the start of each frame. End result is a much cleaner and faster implementation.
That, by the way, is something that RMQ can potentially benefit from. Because I use ARB assembly shaders there, I have a number of "program environment" slots that I can do a once-per-frame load into. Of course OpenGL lets you not do it this way, but my hunch is that taking this approach will map closer to how hardware actually works and thereby give better performance.
Update
A Marcher-ing we will go.

720 FPS - this is faster than DirectQ now.
Note the total lack of a skybox (meaning that I'm actually taking a performance hit from drawing the scrolling sky.....)
Update 2
OK, I did the skybox. No screenshot, because you know what the Marcher Fortress looks like by now. It's gone up to 820-ish.
Cubemaps in D3D11 are "fun" - not particularly difficult, but you'd have a hard time working out from the documentation how to do them. A combination of hunches, blind luck, and one web search for a semi-undocumented flag got me there.
This was actually important because I'll need it for DirectQ too, so it's good to have the code ready. There's also a very nice article about using a geometry shader to render to all 6 faces of a cubemap, which will be important for when I start looking at the deferred renderer I was talking about (can I mention "DirectQ 3.0.0 sometime in 2014 or 2015?)
Posted by
mhquake
at
6:02 PM
1 comments
Tuesday, January 10, 2012
Even more Awesome
I just ported the FitzQuake protocol to my D3D11 engine, because I wanted to test it's performance under certain high-stress situations. The end result was quite pleasing.
The obvious first test was my 400 Knights Death Map. Here the current stats are something like: FitzQuake - 50 to 60fps; DirectQ - about 350fps; RMQe - about 300fps; QuakeSpasm - just over 50fps; DarkPlaces - about 100fps.
The D3D11 engine hit 450.
Now, much of the difference in performance between my code and traditional Quake renderers is down to my use of indexed triangle soup allowing MDLs to be drawn in a single call (I also do animation and frame interpolaton on the GPU allowing for use of static vertex buffers), which is pretty consistent in every Quake port I work on. But at the same time - a fairly basic implementation with no special care going on still managing to outperform everything else, and by such a margin, was impressive. It confirms my initial observation that D3D11 is slower with simpler stuff but suffers less of a speed hit as things get more complex.
For grins I added vertex cache optimization to the engine vis D3DXOptimizeFaces and D3DXOptimizeVertices (it was necessary to link to some D3D9 libs for this) and it made it to 550.
I'm going to add instancing to it shortly and we'll see how well it runs then.
This was the only high-stress test I've done so far, as I haven't yet added big map support (it does run Marcher though, but without the skybox, with complaints about sound, and with far clipping plane problems).
What's really interesting about all of this is that it also semi-confirms a hunch I had that D3D9 stuff like this doesn't actually require a D3D9 Device to be running while used. What that means is that the possibility of adding the same vertex cache optimization to the Windows version of RMQe exists, so that's something to try out.
Will report back.
Posted by
mhquake
at
11:48 PM
3
comments
DXGI is Awesome
Have I said that before? Well I'll say it again.
I now have video mode changing fully working, including transitions from windowed to fullscreen and back, changes between modes when windowed or fullscreen, refresh rate, vsync and the ability to pick whether the mode is centered or stretched on the target window.
I don't yet have adapter or output selection; from what I can gather so far these are properties that can only be set at initial creation time, so changing them seems to involve a "destroy and recreate everything" scenario. The only thing that might be hairy about that is the need to recreate textures from BSPs, MDLs and SPRs; every other D3D object that I use can be dynamically created, destroyed and recreated as required.
This is a truly awesome set of capabilities.
Total lines of code for fully functional video startup, shutdown, mode enumeration, mode selection, mode changes, framebuffer creation, window size dragging, and frame begin and end?
450.
Including comments and whitespace.
That's astonishingly minimal, and it's all ultra-clean and elegant. My previous bugs came from a "take a hammer to it" approach, using brute force and ignorance. Once I'd studied the documentation a little more it became obvious what was wrong and what needed to be done, and it's amazing how neatly everything interlocks.
I'm going to enjoy porting this to DirectQ when the time comes.
Posted by
mhquake
at
6:48 PM
2
comments
Video Mode Changing
I bit the bullet and did video mode changing. It's not the prettiest code, but most of that is down to the nasty old classic ID Quake menus and the lack of more refined cvar control - neither of these problems will apply to DirectQ.
As before, I start in a windowed mode and switch to fullscreen during startup if requested. The reason why is because it's safer and less obnoxious on the end-user. There's also the small matter of this text in the D3D documentation: "you should not create a full-screen swap chain .... create a windowed swap chain and then set it full-screen". That should end any arguments about this particular feature of video startup.
I haven't figured out how to change refresh rates yet; refresh rate is a property of the swap chain so I might be just able to destroy the existing swap chain and create a new one with the new rate, but I don't really have the capability to test this properly.
An alternative is to destroy and recreate everything, but that means some mess with texture reloading. Either way it's a bit messy.
Changing between windowed modes is fast and clean; the change happens almost instantaneously. Changing to a new fullscreen mode involves a mode change on the monitor, so it looks a little hairy. That's all quite normal. One slight issue is that status bar, console and menu text and pictures are blurry. I suspect that I'm missing a step here, so I need to review the documentation again.
Alt-tabbing from a fullscreen mode works perfectly, but when you alt-tab back it drops you back to the original mode that the swap chain was first created in. I just need to re-run the mode switch here.
Overall though it's been quite a positive experience, and definitely a lot nicer than messing with lost devices in D3D9 (or with GDI crap in OpenGL).
Posted by
mhquake
at
2:44 AM
0
comments
Monday, January 9, 2012
Fun with the Video Code
The new D3D11 video code is coming along very well indeed. I'm taking the opportunity to clean out all of the nastiness in gl_vidnt, move things around to more logical places (there's code in there that should be in sys_win, other code should be in in_win) and organize it better. The objective is that I'll have a nice set of clean and functional video code that can be almost lifted over to DirectQ 2.0.0 as-is.
The code is being restructured into multiple files; right now I have vid_menu, vid_win32, vid_cmds and vid_d3d11 and I think that should cover all I need. I had made an attempt to do this with DirectQ's current code, but in truth it inherited an evolved mess from the original D3D9 port which only got worse as time went on. I've no real intention of working any more on that version of the video code, barring any absolutely no-questions-asked bugfixes.
DXGI is awesome. Once you get your head around the way things should be structured, it really pays off in spades. I can easily and cleanly handle things like window resizing, refresh rate changes, vsync on or off (it's just a parameter to the Present call now - grin!), dragging window borders around, and so on. This is the way the windowing part of every 3D API should be (hint to MS - porting your OpenGL windowing interface on top of DXGI would be a great thing indeed).
The bad news is that the mode list is going to change again, meaning that existing configs will be invalidated. I'll probably change the cvar names to handle that in a cleaner manner, at least where the change would be destructive.
The good news is that functionality is colossally improved. You'll be able to select which video adapter to use if you have more than one, if your adapter has more than one output you'll be able to select which output, and all of this will be handled cleanly at runtime.
None of this is going to really happen until DirectQ 2.0.0 appears, which is - I guess - maybe 6 months way from now.
Posted by
mhquake
at
8:49 PM
1 comments
Update - 9th January 2012
The D3D11 port is now functionally complete aside from the video mode stuff. I just finished things off by adding r_wateralpha and gl_texturemode support, and it rocks.
For gl_texturemode I added anisotropic filtering too. This one is interesting: traditionally I had thought that anisotropic filtering was something that gets added in addition to the standard filtering modes; it turns out that this isn't the way things work at all. It's actually something that's used instead of the standard modes, so if your gl_texture_anisotropy value is set to higher than 1 then the choice made by gl_texturemode is thrown out and it uses it's own mode. That's slightly annoying but it does clean things up a lot.
The clearer separation of samplers from textures in D3D11 makes this setup very nice indeed.
Regarding video modes, I'm going to take a pass through D3D11 mode enumeration (hopefully there will be no IDXGIFactory crap involved here - I would really really hate for that to happen) and see if I can or can't add them. If it works out well enough I'll add something like Software Quake's video options menu too.
I seem to have introduced a nasty sound bug during the switchover to C++ so for now I've just disabled the sound subsystem. I'll probably revert to the original sound code from q1source.zip and try fix it.
Posted by
mhquake
at
12:39 PM
0
comments
Sunday, January 8, 2012
D3D11 Port Status
The D3D11 port is pretty close to complete. Here's what's left to do:
gl_texturemode
Right now I'm just forcing 4x anisotropic filtering at startup, but I want to get this done fully, even if it's not really a critical feature. It seems like it should be easy enough.
gl_flashblend 1 mode
AKA the alternative to dynamic lights - draw a shaded ball around each light. Not really a big deal as a key feature of this port is GPU updating of dynamic lights, but all the same, it seems like it might be fun as there are a number of possible ways of doing it that are worth exploring.
r_shadows 1
I can reuse a lot of code from MDL drawing here; DirectQ just caches all the relevant info needed to redraw shadows at the correct frame and position (which, because it uses static vertex buffers, is very little) so something similar seems the right way to go. Might be fun. At some point in time a shadow volumes inplementation using the geometry shader for volume extrusion might be an interesting experiment.
r_wateralpha
GLQuake only applies alpha to worldmodel water surfaces, so that's probably the approach I'm going to take; I don't want to complicate things too much by setting up a full alpha sorting scheme - it is only an experimental engine after all.
r_mirroralpha
Won't be done. Suck it down, bitches.
Some notes on items of interest in what was done so far
Brushmodel surfaces
Using the GPU for dynamic lightmapping was a primary motivation for beginning this port. I already had something similar done in an experimental D3D9 build, but there were some D3D11 features I wanted to at least try. On balance it's slower than the D3D9 version in simple scenes but faster than it (sometimes much faster) in more complex scenes, which seems like the right way to be.
I played around with drawing surfaces using individual draw calls and triangle strips with restart indexes, but in the end went for a simple indexed triangle list which still provides the best performance. Draw call overhead in D3D11 is so low that the overhead of building indexes dynamically may well come close to balancing things out. An eventual goal is to experiment some with stream out for building surfaces, which is another objective of this port.
Cutting down on dynamic light update checks was an interesting problem and using bitwise operators in my shaders to replicate the functionality of surf->dlightbits required shader model 4. In SM3 indexing into the constants registers from a pixel shader was a baaaaaaaaaaaaad idea so I used texture lookups to retrieve dynamic light positions and colours; in SM4 using the registers is actually faster. In both, vertex texture fetch came in handy for doing a pre-check.
I also experimented a little with sending each triangle through a geometry shader to cut the pre-check (and many of the calculations associated with it) to a third, but the overhead of adding a GS stage here outweighed any benefit so I dropped it.
Screenshots
These took me ages to figure out how to do, but ended up being really easy and quite elegant. Definitely nicer than the D3D9 version, but nothing compares to good old glReadPixels...
2D GUI Stuff
This was the first thing I did because I just wanted to get some quick and easy output on the screen, and I may have made a bit of a mess in doing so. I'll probably revisit the code. This uses indexed triangle lists too, but I'd like to have some code that uses strips with restart, so I may well port it over to that - I'm not going to be too concerned if it runs a mite slower as the purpose of this engine is to have a sample codebase to draw from in the future.
Video startup
I just locked the mode to 1024 x 768 windowed and didn't bother with anything else. The intention was to have something that worked, not a fully-featured port. At some point in time I'm going to need to go back and do proper video mode enumeration and selection, and I also want to play with mode changes in D3D11, but now is not that time.
Textures
Textures were confusing. The code to load one is a little more verbose than I would have liked, and it's unclear which combinations of flags are valid for any given texture. Once written they load - and reload - quickly enough, and I timedemo bigass1 at maybe 550fps.
It was completely unclear (and seemed to be documented absolutely nowhere) exactly which slots in a -SetShaderResources call map to which textures declared in shader code, so I ended up explicitly specifying texture registers in some of my shaders.
Particles
The second thing I ported because I was itching to do them with a geometry shader. I had actually tried this using OpenGL in the RMQ engine before, but it didn't work out too well. This time around the only problem was that I just couldn't get the GS to output 4 verts in a tristrip properly, so I had to end up using 6 verts in 2 separate tris instead.
Thoughts on Direct3D 11 Itself
I like it a lot, despite much of what was odd about it, and am looking forward to porting DirectQ for real when the time for Release 2.0.0 comes around (dunno when that will be yet; best guess at the moment is second half of this year at the earliest).
Important to remember that here I'm using the D3D11 API on D3D10 class hardware, so full D3D11 will not be a requirement.
Some functionality from previous versions is still there, some is changed slightly, and some is missing altogether. I don't miss the fixed pipeline or drawing from system memory arrays - they sucked - but more minor niggles, such as the verbosity of object creation code, a seeming inability to update a subrect of a texture, and a lack of clarity in the NO_OVERWRITE/DISCARD pattern do exist which will probably need a slight rethinking of how I go about some things.
Overall - it's been fun.
Posted by
mhquake
at
11:06 PM
0
comments
Not bad at all....

There was maybe 2 or 3 days work so far in that port, and most everything is done now. There's still some drawing left to finish off and bugs to iron out, but otherwise - yeah, it's cool.
Overall the experience started out as hugely ugly, but quickly became very enjoyable indeed.
Object creation was the main source of nastiness; everything has gone back to filling in a struct and passing the address of that to creation functions, which can seem an elegant way of doing things but is in reality a colossal pain. I ended up having to wrap most creation functions with routines that just took parameters and filled in the struct for me.
The documentation was vile. When originally doing DirectQ I had the same observation, but as I came to know what I was doing I really began to appreciate it a lot more. That's not happening this time around; it's riddled with errors, basic information is missing, the search and index are useless.
Texture creation is a horrific mess.
On to the nice things. I was able to do a few neat little extras that the higher hardware spec allowed for.
Geometry shader particles were one I screenshotted earlier. This was something that I really really wanted to try out, and it worked very well.
I did a full GPU lightmaps implementation, including per-pixel dynamic lights. That was something that just worked first time, and was another motivation behind the port. Having bitwise operations in shader model 4 meant that I was able to move all the old C code involved in dynamic lights (R_PushDlights, R_MarkLights, R_AddDynamicLights) to the GPU with very little performance loss. As a bonus I added dynamic lighting to BSP brush models and water surfaces.
What's left to do is sprites and some of the 2D stuff.
I think I might release the end-result when done as it's a rather nice port and - despite being very traditional - has a few unique features.
Posted by
mhquake
at
4:36 AM
0
comments
Saturday, January 7, 2012
Direct3D 11 Quake!
I'm currently porting GLQuake to D3D11. This was prompted by one requirement of my recent GPU lightmaps experiments - I needed bitwise operations in shaders, and they're not available in SM3 or below.
So far - it's interesting. I'll rant a bit about the overall experience when done, but for now it's enough to say that things could - and should - have been a lot better.
At the moment I have 2D GUI stuff and particles done, fairly roughly, and am about to move on to bruah models.
This is also a possible part of the roadmap for DirectQ. The next major release (1.9.0) is intended to drop support for a lot of legacy hardware, and the one after that (2.0.0) may go all the way to D3D11.
Posted by
mhquake
at
2:13 AM
3
comments
Wednesday, January 4, 2012
Even more Lighting
I've done an implementation of GPU lightmaps in my test Direct3D engine. This was already done for RMQ (and a forked version of DirectQ) but with this one I bumped the hardware requirements up to full Shader Model 3 and took advantage of extra instructions, floating point textures, longer shaders, extra constants registers space, vertex texture fetch and dynamic branching.
Speed is very competitive; I can safely predict that when this moves to DirectQ (release 1.9.0) it will run faster and look better.
Here we have one clear advantage of bumping hardware requirements - something that previously wasn't possible suddenly becomes not only possible, but much better in every concievable way than what went before.
This is just scraping the tip of the iceberg; there is so much more than can be done to improve things and I'm quite excited about the prospect of throwing out the old lower-spec code and putting in something nice, clean and efficient.
Overall it's definitely the right decision. With Shader Model 3 hitting 8 years old this year, retaining compatibility with the older stuff is no longer a productive thing.
Posted by
mhquake
at
5:41 PM
6
comments
More DirectQ Lighting
A nice improvement has just gone into dynamic light handling. DirectQ will now move each dynamic light into the same space as the brush model entity it's (potentially) affecting before calculating the light contribution, meaning that dynamic lights are applied properly to moving brush models.
You can see this in e1m1 if you ride the first platform down to the open area. Fire your shotgun at the top of the platform and it's lit properly; fire it at the bottom and it's not. Well, it wasn't, because now it is. (You can also see it by riding the plats in e3m4.)
This was something picked up from the RMQ engine, but was able to be somewhat simplified as DirectQ doesn't attempt to light BSP brush models (RMQ does, with varying degrees of success).
I also got rid of some heavyweight CPU-side checking in these functions; it's marginally faster to just update the lightmap.
An idea I have brewing is in relation to entity lighting. Here we have more CPU-side checks and calculations for the light level; it strikes me that instead of doing some of that I can just store a lightmap texture number and the texcoords to be used, then sample the lightmap in my pixel shader. That will give identical lighting to the world and with better quality too (a lot of special case crap will just go away too).
There are some complexities to be worked out here; the surface beneath the entity may not be in the PVS, in which case the lightmap will need updating separately. There's also the case where an entity may straddle the line between two surfaces, although I'm not certain if that's such a big deal.
This is one for trying out first in my experimental engine, I think.
Posted by
mhquake
at
1:42 AM
3
comments
Tuesday, January 3, 2012
A Consideration Regarding Interpolation
Previously DirectQ has used something of a hack for interpolation; this was needed to fix the muzzleflash animations on weapon models and worked by disabling interpolation for certain vertexes if they had moved beyond a certain amount between the first two frames in the model.
While it works perfectly fine for all viewmodels in all mods I've tested, there remains a possibility that at some point in time it's going to break.
Recently I've been experimenting with a weighted blend run on the GPU in a vertex shader. Once again, it calculates the difference between two positions, but rather than being a binary on/off result it rescales the blend factor so that larger differences blend less.
Aside from working correctly with standard viewmodels, this has a number of other consequences.
It will work correctly with any pair of frames. If a viewmodel doesn't show it's muzzleflash animation until the third or fourth frame, it will work.
It works with other models that have muzzleflash animations, such as the Grunt.
It scales smoothly so that if the vertexes don't move beyond the old cutoff point it can still give you something that's better than nothing.
It evaluates a correct 3 dimensional distance, rather than the old one-dimensional distance that the old method used.
Sudden movements over larger distances will not interpolate as smoothly. I'm in two minds about this one; on the one hand I don't think they should (and some models don't look that great if they're caught between two such frames), but on the other hand some smoothness is lost in dying animations.
It's fast.
It can be made user-configurable.
I'm not committed to switching over to this system in any live codebase just yet, but I'm inclining towards thinking that it does represent a good way forward with general handling of interpolation. Anyone have any opinions?
Posted by
mhquake
at
11:06 PM
1 comments
DirectQ - Lightmaps and More - 3rd January 2012
The death-knell for my Windows XP/D3D9/Intel graphics PC finally sounded today when I switched it on and had a ton of errors pop up about failing fans and other such fun. I managed to keep it running for long enough to pull some stuff off it, but I'm wary about switching it on again.
Given the age of the machine, it's not worth hunting down replacements and fixing it up, so it's gone to the great spare parts bin in the sky. The replacement is - of course - Windows 7, 64-bit, D3D11-capable. As it's a work PC, and therefore business class, it's graphics capabilities aren't the best, but with an AMD Radeon 6450 it still hits just over 1000 FPS in timedemo demo1 with DirectQ. It's also nice to have an AMD to test stuff on.
So the way it's looking now is that the next release of DirectQ is going to be the final one where I can guarantee that kind of legacy hardware support. Following that it seems timely to bump to version number to 1.9.0 and officially raise the hardware requirements.
So there will be one more lower-spec release of DirectQ, which will be 1.8.9, following which 1.9.0 will have slightly raised hardware requirements: we're going to need Shader Model 3 or higher, hardware T&L, and a few other things. As these shake out I'll talk more about them.
One advantage of these raised requirements is that I can make use of features that previously I had to either offer an optional code path for or just ignore. This means things like vertex-texture fetch, more extensive instancing, full-on shader branching, raised instruction limits, extra texture formats and so on.
A specific first thing to do is in relation to texture formats, and their use in lightmaps. By being able to work on the basis that 64-bit textures are going to be now always available I can do things with lightmaps that were previously either impossible or messy. I've already got a setup in my experimental engine where I can bypass an intermediate array and skip over a LOT of checking and calculating; the end result is that the renderer is cleaner and faster, despite the extra overhead from reading a 64-bit texture.
The roadmap from there looks like a move to D3D11 for 2.0.0; I'm almost definitely committed to porting the experimental engine to D3D11 sooner rather than later, and a lot of the work on DirectQ over the past year has been carried out with that move in mind: switcing to shaders and vertex buffers for everthing, for example.
Regarding my intention to try a deferred renderer, I have carried out some initial work which was not too promising. A basic setup with 3 simultaneous render targets - one recieving diffuse, another lightmaps and the third fullbrights, then blending them together to the backbuffer, caused a huge performance drop. This was with D3D9; it's possible that D3D11 will offer more performant support.
Posted by
mhquake
at
7:19 PM
1 comments
Monday, January 2, 2012
DirectQ Updates - 2nd January 2012
New year, more work being done. With the RMQ engine ready for release I'm taking some time to tidy up a few things in DirectQ. Some of the major outstanding defects will need a good many more passes over the code to correct, but we're getting there, slowly but surely.
A texture animation bug where some switches/buttons wouldn't go into their alternate animations has been fixed.
GUI/HUD texture loading has been cleaned a little.
The sound engine is maybe 25% to 50% faster owing to removal of some calculations from inner loops.
I'm considering removing 8-bit/mono sound from the sound engine; this is for the same reason as why I removed 16-bit colour from the video modes - it's now 2012 and everybody has sound cards that can handle 16-bit stereo sound. Retaining a legacy mode that nobody is forcibly restricted to any more doesn't make sense - it's like retaining support for EGA graphics!
Onward!
Posted by
mhquake
at
8:44 PM
6
comments
