This is mostly a scrap pad for my TODO's, in a place others can see them. Feel free to add--preferably signed--comments, though actual discussion is better held on the list than in a CVS document. :) Some of this is stuff I'm not quite sure how to deal with, but too low-pri to bother bringing up on the list. ******** Resizing tiled textures is a pain: if we scale down a tiled 1024x1024 texture to 256x256, then we need *four pixels* of buffer between each tile to ensure they don't bleed into each other. Splitting all tiled bitmaps into lots of smaller textures sounds good (for other reasons, too). But for fonts, this would create hundreds of tiny textures; that would be a big performance hit (four fonts could mean over 1000 textures, and we'd have to flush the render queue *every character*--ack.) Maybe BitmapTexts could pre-render their text; then they'd be nearly free, but changing text would be more expensive. Maybe too expensive; eg. the Oni timer changes every frame. But we might have to do that anyway with fallback fonts; how is Jared implementing that? If we split tiled sprites apart, we can also tile BGAs with only one render. Then we might be able to use multitexturing (if available) for BGAs, to render multiple BGALayers in one pass; they get slow. Tricky to interface, though. Hmm. The two times we want to scale textures (to fill the texture) are 1: when we're going to wrap the texture and 2: when the image is larger than our maximum texture (eg. due to the max texture size option). In case #1, we want the resize filter to wrap. In case #2, we don't. We should only wrap filtering if the texture is going to be wrapped. Is it actually noticable? Might reduce seams in tiled BGAs. ******** Make RageTextureManager handle refcounting, not RageTexture. ******** D away with m_HoldNotes completely, and change the default storage scheme to 4s. Make every place that accesses holds either use them in 4s, or specifically request a hold list, when needed (such as when rendering). If we can do this, we can do away with most of the 2sand3s and 4s conversions, which will aid const-correctness a *lot* (which has been bothering me), and simplify the code further. One problem: The renderer (NoteField::DrawPrimitives) wants HoldNotes. That's not a problem (we can still create an array of HoldNotes when requested, and it can be cached); the problem is that it'd need to be recreated whenever the data changed, which would make editor recording slow, at least. Maybe even the NoteField::DrawPrimitives could deal with 4s, generating HoldNotes as needed. I'm not sure if that'd be too slow, I'll have to try it and see. ******** Thinking ahead: BM support in .SM's and internally. Background: allow an arbitrary number of keyed notes; any tap can be keyed to any effect. BM doesn't have hold notes, but I'd like to support them anyway. Right now, a '0' indicates nothing, a '1' indicates a tap note, a '2' indicates the beginning of a hold note (which is duplicated in the hold note list). We want more than 255 keyed sounds, so make this a short, with -1 indicating nothing and 1+ indicating a tap note for a given key. Put autoplay sounds on 0 (keyed sounds to always play). DDR can be stored the same way; just put every key on 1 and don't attach a sound to it. Hold notes are tricky. Any note should be able to be held, even keyed ones, and we need some equivalent to "4s mode". We could make TapNote a struct { short key; bool hold; }. How to handle playing keyed hold notes with regular sound effects? (We aren't using MIDI ...) Make sure hold notes work together with autoplay sounds. (Lots of songs have hard versions with a lot of sounds being played by the player, and easier ones with many of those sounds on autoplay; it should sound the same.) Hmm. So, tentative steps: 1: Make TapNote a struct, adding 'enum type', with values "TN_NONE", indicating nothing (the short should always be 0 here), TN_TAP, indicating a tap note, TN_HOLD, indicating a hold note head, TN_AUTOPLAY, indicating an autoplay note. 2: Remove TAPNOTE_HOLD_HEAD, using TN_HOLD instead. At first, only the tap head of the hold note will have a TapNote entry (like it is now). This will make NoteData larger. It's already too large, so this should all be done after abstracting it (so we don't have to overallocate everything). --- And the other part: how to store this in .SMs? We need to be backwards- compatible, of course, so perhaps we should add an #SMVERSION tag; this would be #SMVERSION:2. Currently, we do 10000 to indicate one tap. We can't use single digits, since we might have any number of keyed notes, so 1 0 0 0 0 which would allow higher numbers; but how to represent holds? Perhaps +1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 0 0 0 0 which would be the equivalent of 20000 00000 00000 30000 except indicating that the hold is keyed to key 1. This would make .sm's more than twice as large. I don't think that should be a consideration; if it becomes a problem, we can add gzip support. Better to do it cleanly and have readable data files than to go to lengths to "compress" manually, like DWI's do, and have data that's impossible to read and almost impossible to parse. Where to put autoplay notes? They can't go in their own tag, since they're specific to notes, not songs. They also need all of the data that regular notes need (hold length, if any, and what they're keyed to), so it'd be cumbersome and ugly to put them out-of-line; having them inline with the rest of the notes would be nice. Hmm ... ******** Stuff in the song cache never dies unless the version changes. We don't want to erase songs we didn't load; I frequently move song paths out of the search path while debugging (for fast loads) and I don't want that to lose cache. Hmm. Access time? Similar problem with high scores; I don't load all of my songs when debugging, and I lose my scores. (Not sure how to do this either.) ******** It'd be nice if we could go straight from one screen to another, tweening one off and the other on simultaneously (with a delay so it doesn't look like a jumble, but in parallel). Here's the idea: Old menu displays its keepalive, and preps the new screen. Then, add the new screen to the top of the screen stack, the old menu hides its keepalive, then the old screen tweens out while the new screen tweens in. (This will need some tuning; they shouldn't both start at once, since there'll be too much onscreen and it'll just look like a jumble.) ******** We have different kinds of things we want to trace, and different places to put them. We have normal debug traces. There are lots of these, and they go to the console and log.txt. Since there are so many, we only want to include recent ones in crash dumps. (All other types of output should also go here.) We have at least two kinds of warnings: 1. Things that are possibly our fault, that we want to receive bug reports about, but that aren't fatal. DirectShow failing to start for an unknown reason (this does not include missing codecs), unexpected data from a USB device (eg. requesting 3 bytes from a Pump pad and only getting 2). 2. Errors that probably aren't our fault (that we don't want bug reports about). These are predictable problems, that we expect to happen, but also aren't fatal. MSD parse errors, missing song files, missing announcer directories (except in Empty; that'd be our fault = #1); DS failing due to a missing codec. Since we expect these to happen, we can also include a "tip" section for the warnings; for example, if an AVI is missing a codec we know about, we can point the user to the codec's website. Some users won't want to see one or both of these at all. ("Okay, it can't load the doubles steps; I don't want to have to edit the song to fix it, so stop bugging me about it." "Okay, I've reported that bug; stop bugging me.") We have important debug traces; these are normal (unlike Warn), but should always be included in crash dumps. This includes things like video card info and which input devices were found. All of these should go to the debug log. Right now, warnings are easily lost in the debug flood, and aren't seen by non-developers running release builds. Put warnings in a grayed-out edit box, so they can be easily copied. I'll probably do this once I figure out a good place to display warnings. ********* I don't like needing to use the keyboard to configure my pad; it's awkward. A mode to simply scan through inputs isn't good; it'll be too easy to make false inputs. Delayed inputs suck (eg. bmdx). Not sure how to deal with this, but it's minor. If we get BM support, configuring two controllers one key at a time would be a pain. (KM even more so.) In that case, we can just scan, though. Low-pri; I'll do this if I figure out a good way. ******** BPM sync for menus; this would allow full BGAs, synced menu effects, and so on. Novelty; low-pri. ******** Option to turn off menu music (except for song previews). ******** Don't export *.old in SMZIPs. ******** Note that a 16bpp framebuffer bit depth can give extremely high quality output; it's usually only worse than 32bpp if you're looking for it. (It becomes more important with heavy filtering; for example, Q3's transparent layered smoke puffs.) Using 32bpp textures and a 16bpp framebuffer will dither in hardware, which is much higher quality than dithering the texture itself and is free. Jumping to a 32bpp framebuffer is *much* more expensive than jumping to 32bpp textures, and doesn't improve quality nearly as much. ex. in the graphics menu, 1024x768, vsync off, I get 170 fps in 16 bpp textures, 16 bpp framebuffer. Jumping to 32bpp textures costs 20 fps. 32 bpp framebuffer costs 62. Both costs 74. We're more fillbound than most apps: we don't draw very many polys, but backgrounds can result in multiple fullscreen passes. (Okay, this isn't a TODO ...) ******** Overload theme metrics on the commandline. --metric "Common::InitialScreen=ScreenDemonstration" --metric "ScreenDemonstration::NextScreen=Exit" could be used to run a demo and exit, for benchmarking and profiling. ******** Defaulting to the default refresh rate sucks; it gives 60Hz on most systems. The reason we do this instead of using the max is that Windows often doesn't really know the max refresh rate, but it always thinks it does, so on some systems the max refresh rate will just desync the monitor. We can find out the active refresh rate: DEVMODE dm; memset(&dm, 0, sizeof(dm)); dm.dmSize = sizeof(dm); EnumDisplaySettings(NULL, ENUM_REGISTRY_SETTINGS, &dm); LOG->Info("%ix%i, %i %i %i", ydm.dmPelsWidth, dm.dmPelsHeight, dm.dmBitsPerPel, dm.dmDisplayFlags, dm.dmDisplayFrequency); If the resolution and bit depth we're setting is <= the current, we should be able to use the same refresh rate, too. Many people probably don't play games in higher resolutions than they normally run their desktop at, so this is often going to be true. If this gives anything above 60 at all, it's a big win for sensible defaults. 70 is infinitely better than 60. I don't want to change the default refresh right now, until things settle down a bit more--don't want to introduce new problems.