So, what I discovered today, is that XNA 4.0 has added support for the Microphone. While perusing the docs, I noticed that the Microphone class has a GetData() method which returns an array of bytes.
Hmm, I thought. You can certainly shove your Microphone up your ass, however, if XACT now has a way to GET bytes, it surely must have a way to GIVE bytes directly to the underlying audio system. This, obviously, is exactly what one needs to be able to do to create audio proceduraly. Henceforth, XACT was all about cueing up and playing wav files.
A response from a MVP on a msdn forum post about procedural audio snarkily said it was a feature only useful for emulators. Apparantly it's also useful when you have a Mic present, or are otherwise sourcing audio in real time.
The end-all is that my humble NES emulator now runs on Windows Phone 7, and (still to be tested now that I have cause to shell out for a CC license) Xbox 360.
The 360 makes me think... Obviously a NES emulator would not be allowable. But, as a content delivery platform, it might actually find a niche. There's a ton of decent NES homebrew out there, and I wonder if I could bring any of it to the XBox marketplace. Time to chat up some homebrew'ers, perhaps.
Regardless, I have forked the project, and am now working outside of the open sourced code I stuck on googlecode. I will still update the googlecode, but focus on the Silverlight (software rendering) side, rather than the D3D10 Silliness I was engaged in (though I'll come back to that, because there's plenty of GPU fun to be had there).
In other news, I refactored the emu itself somewhat, in a large code/namespace cleanup -- but also took care of some performance snags along the way. Instancing it *outside* of Unity (ie; new-ing it all up) exposed how silly I'd let the object graph become. The namespaces were always silly. Not a hard thing to clean up, but something I've put off, for no good reason.
I knocked out a "touchpad" for the Windows Phone port. It's hard to play mario when you have to click one gamepad button at a time with the mouse. I need me some real hardware to play with.
Wednesday, May 12, 2010
Monday, May 10, 2010
Summer of code (not so much)
Hard to get focused on writing code with the nice weather, and being uber-busy. Started with this, though, since the Windows Phone CTP is out. So far, it seems to work full speed, though getting sound out will be a fuss. This is XNA for Windows Phone 7, haven't played with the silverlight for the phone yet. Exposing the emulations video and audio as a muxed stream might just be the ticket, and something I've thought about before. (ie; the NES actually outputs an .avi which is then plugged into 'media player' of choice)
Drawing is only a bit of a chafe, I have to keep at least two textures, and swap them out (one texture is drawn, while the other is filled via SetData<>()). You can't be manipulating the same piece of memory with both CPU and GPU at the same time, this is logical. It's nice that XNA now hands my textures "back" after rendering, so I can call SetData again, before I was having to Dispose() of the old and create a new one each time to get them to update dynamically, which is hardly how to do it.
Controls.. The TouchPanel interface is easy to work with, just need to draw myself a control pad on screen, and map touches to it.
Audio... Eh.. Not sure how well it will work, I'll try creating WAVs in memory, and see if I can schedule them in a tricky enough way to make it sound decent.
I should be able to get something people can use on their Phones in the fall, and maybe it'll be this Christmas' shopping seasons must have killer app?
Drawing is only a bit of a chafe, I have to keep at least two textures, and swap them out (one texture is drawn, while the other is filled via SetData<>()). You can't be manipulating the same piece of memory with both CPU and GPU at the same time, this is logical. It's nice that XNA now hands my textures "back" after rendering, so I can call SetData again, before I was having to Dispose() of the old and create a new one each time to get them to update dynamically, which is hardly how to do it.
Controls.. The TouchPanel interface is easy to work with, just need to draw myself a control pad on screen, and map touches to it.
Audio... Eh.. Not sure how well it will work, I'll try creating WAVs in memory, and see if I can schedule them in a tricky enough way to make it sound decent.
I should be able to get something people can use on their Phones in the fall, and maybe it'll be this Christmas' shopping seasons must have killer app?
Wednesday, March 17, 2010
What now?
I just got back from the BaltoMSDN meeting, Geoff Snowman put on a good presentation about the ins and outs of crypto, and the only thing I can think to add to it is that when you're dealing with symmetric key encryption, the message needs to be secret as well as the key. If you can derive the message from the encrypted message, using the key, you can derive the key from the encrypted message using the original message.
A lot of hard-drive based crypting programs were compromised when people realized if the drive was formatted in FAT, they could look for the allocation table in the usual spot, get the encrypted version, then derive the key from the (known, static) unencrypted version. So, encrypting the data isn't enough, the contents must be scrambled too. Which goes to the point I tried to make about security holes never being found in the cryptology - the math is solid - they're always in the implemenation.
You can hash your users passwords in the database all you want, but if every client app logs into the database with the same user/pass combo, then that's the vector an attacker is going to use, and all your high talk about salting your hash is really just masturbation.
But, as far as important news.. I got around to updating the mono port of my emulator, which took about 5 minutes, really, since Unity does all the hard work, it's just a matter of registering the appropriate platform specific classes. What was weird, was that the 'callback' member was missing from the SDL bindings in the Tao framework.. What's even weirder, is that the framework hasn't been updated since 2005, so where the hell did it go and how could it have got gone? It could be something to do with running in a VM, but I never really knew type members could just 'disappear' like that. Very odd.. No sound for linux (well, flakey OpenAL based sound).
I also started work on an assembler. Once I've populated my 'instruction' structure I already use in my debugger, emitting the bytes is all but automatic. All I needed is a parser, and it's pretty easy work, regex does most of the heavy lifting as far as identifying addressing modes, which is really the only 'problem' to solve.
The only work left is to add a pass to replace labels with actual addresses, and a little more focus on making sure the syntax is in line with other popular 6502 assemblers.
My goal is to host this as a server side component, and to leverage some of the 'online IDE' ajax type stuff out there to put together an easy to use online environment for the homebrew community.
But it'll probably be on hold for awhile as I plan to be busy at work, but I may get it online this weekend.
A lot of hard-drive based crypting programs were compromised when people realized if the drive was formatted in FAT, they could look for the allocation table in the usual spot, get the encrypted version, then derive the key from the (known, static) unencrypted version. So, encrypting the data isn't enough, the contents must be scrambled too. Which goes to the point I tried to make about security holes never being found in the cryptology - the math is solid - they're always in the implemenation.
You can hash your users passwords in the database all you want, but if every client app logs into the database with the same user/pass combo, then that's the vector an attacker is going to use, and all your high talk about salting your hash is really just masturbation.
But, as far as important news.. I got around to updating the mono port of my emulator, which took about 5 minutes, really, since Unity does all the hard work, it's just a matter of registering the appropriate platform specific classes. What was weird, was that the 'callback' member was missing from the SDL bindings in the Tao framework.. What's even weirder, is that the framework hasn't been updated since 2005, so where the hell did it go and how could it have got gone? It could be something to do with running in a VM, but I never really knew type members could just 'disappear' like that. Very odd.. No sound for linux (well, flakey OpenAL based sound).
I also started work on an assembler. Once I've populated my 'instruction' structure I already use in my debugger, emitting the bytes is all but automatic. All I needed is a parser, and it's pretty easy work, regex does most of the heavy lifting as far as identifying addressing modes, which is really the only 'problem' to solve.
The only work left is to add a pass to replace labels with actual addresses, and a little more focus on making sure the syntax is in line with other popular 6502 assemblers.
My goal is to host this as a server side component, and to leverage some of the 'online IDE' ajax type stuff out there to put together an easy to use online environment for the homebrew community.
But it'll probably be on hold for awhile as I plan to be busy at work, but I may get it online this weekend.
Sunday, March 14, 2010
Silverlight Updates
I've spent a bunch of work bringing the Silverlight version up to speed, and have managed to clean up some code along the way..
New in Silverbulb:
Debugger support (currently just have the Machine Status view implemented)
Pattern and Name tables viewer
FPS Limiter for slow machines (sets Silverlight's max FPS only)
SRAM read/write to IsolatedStorage (automatic on poweron/poweroff)
SRAM read/write relies on the iNes header having the SRAM bit set properly. I haven't done anything to handle requesting more storage, and also should provide a way to purge out old saves, and a way to load/save them to a local file. Some sort of "cart data" management dialog is in order, though this should completely abstract the filesystem from the user.
New in Silverbulb:
Debugger support (currently just have the Machine Status view implemented)
Pattern and Name tables viewer
FPS Limiter for slow machines (sets Silverlight's max FPS only)
SRAM read/write to IsolatedStorage (automatic on poweron/poweroff)
SRAM read/write relies on the iNes header having the SRAM bit set properly. I haven't done anything to handle requesting more storage, and also should provide a way to purge out old saves, and a way to load/save them to a local file. Some sort of "cart data" management dialog is in order, though this should completely abstract the filesystem from the user.
Monday, March 8, 2010
Silverlight MediaStreamSource AudioBufferLength
Having end-users who've toyed with my emulator randomly complaining about sound pops or skips, I decided to sick a slider which would allow them ot set the AudioBufferLength of the MediaStreamSource which generates the sound.. However, it doesn't seem to affect the stream in real time. Hmm, I don't want to have to tear down and recreate the audio in real time, if avoidable. I wonder if there's a solution out there? Anybody?
Wednesday, March 3, 2010
What's in a name?
I was doing a little reading up on the System.Threading.Tasks namespace, and threading changes in general in .Net 4.0, and got a little bit confused by this. He says of ThreadPool.QueueUserWorkItem(), and I'll quote here:
Collection, Dictionary, Cluster, Glob, Bag, Bucket, whatever - define those structures however you want. Those words are rooted in neckbeard yap. Queue, priority queue, and stack have rigid mathematical definitions and cannot behave any other way. At least, not without cheesing me off.
The FIFO order is not something that is documented or guaranteed, but my personal guess is that too many applications rely on it, so I don’t see it changing any time soon.Not documented? Not guaranteed? Are you kidding me? It's called a Queue. Queue means FIFO. If it behaved any other way they'd be buried in bug reports. My personal guess is that even the best of us can get confused and miss basic principles. Not to insult the Moth, there is a ton of good information regarding parallelism on his blog, and I certainly recommend it.
Collection, Dictionary, Cluster, Glob, Bag, Bucket, whatever - define those structures however you want. Those words are rooted in neckbeard yap. Queue, priority queue, and stack have rigid mathematical definitions and cannot behave any other way. At least, not without cheesing me off.
Tuesday, March 2, 2010
CMAP For VS 2010
Just got back from the CMAP meeting, Antonio Chagoury did a presentation on some of the lipstick and mascara applied to VS 2010. I've been using it for awhile, and had already been taking advantage of some of the new IDE tweaks (the zoom feature kind of gets on my nerves, to be honest, I keep trying to scroll the code window up and end up staring down a semicolon 200 pt font ).
Some of the ASP.Net and WebForms stuff he showed off was informative, however, since I've not had too much professional exposure to that stuff. Would like to hear more about the threads debugger, profiling, debugging 64 bit apps, TFS and ClickOnce enhancements, but hey, you can only get through so much in an hour, and to be fair, not many folk nerd out on stuff like that how I do.
Free pizza. Free soda. Good times had by all. I just may start showing up at more CMAP events.
Some of the ASP.Net and WebForms stuff he showed off was informative, however, since I've not had too much professional exposure to that stuff. Would like to hear more about the threads debugger, profiling, debugging 64 bit apps, TFS and ClickOnce enhancements, but hey, you can only get through so much in an hour, and to be fair, not many folk nerd out on stuff like that how I do.
Free pizza. Free soda. Good times had by all. I just may start showing up at more CMAP events.
Horribly broken mid-screen bankswitching/mirroring
Oops.. Well, at least hardware and software renderers are rendering the same output. Not quite sure how I broke it, but I think I may be mapping VROM over top of VRAM, and thusly getting junk written on top of it. I need to impement a cart event debugger/logger, ASAP!
Update: Nah, the code that could have banskwitched a VROM into VRAM was incorrect, but unreachable in that mapper (Afterburner is the only game/cart/mapper that I know of that has nametables in rom only and bankswitches them). The code is a little bit complicated, because what I do is keep an array which holds the starting address for each 4k block of VROM, and bankswitching updates these pointers. To top that off, every time a bankswitch happens, this array is pushed onto the next level of a 2D array, the bankswitch cache, which holds every bankswitch that happened in a frame. I do this, because for the GPU version, I don't send pixels, but I send the information needed to draw the pixels (the current ppu status bytes, the bankswitch cache, a similarly implemented palette cache, the rom itself, and the vram).
So, a bankswitch, or a change to the mirror bits, triggers a call to UpdateBankSwitchCache, which pushes the current banks onto the stack. To cut down on redundancy, I decided to only to process a mirroring change, if the new mirroring was different (for a explanation of NES mirroring see the nesdev wiki). Well, the NES nerds might already see what I did wrong. if (this.mirroring != mirroring) is not enough. For one-screen mirroring, the location of the screen is also important. To me, this makes if (this.mirroring != mirroring && this.oneScreenOffset != oneScreenOffset).
So, the moral of the story, is make sure you are taking a step back and considering *all* pertinent variables before you optimize something. I'm also guilty of premature optimization, as the bankswitch cache is as of yet in no danger of overflowing. Theoretically, there could be thousands of bankswitches during a frame, it's up to the cart and mapper. Realistically, it's very unlikely, at least on conventional mappers in commercial carts.
So TMNT and others display just fine, once again. Happy turtlin'.
Update: Nah, the code that could have banskwitched a VROM into VRAM was incorrect, but unreachable in that mapper (Afterburner is the only game/cart/mapper that I know of that has nametables in rom only and bankswitches them). The code is a little bit complicated, because what I do is keep an array which holds the starting address for each 4k block of VROM, and bankswitching updates these pointers. To top that off, every time a bankswitch happens, this array is pushed onto the next level of a 2D array, the bankswitch cache, which holds every bankswitch that happened in a frame. I do this, because for the GPU version, I don't send pixels, but I send the information needed to draw the pixels (the current ppu status bytes, the bankswitch cache, a similarly implemented palette cache, the rom itself, and the vram).
So, a bankswitch, or a change to the mirror bits, triggers a call to UpdateBankSwitchCache, which pushes the current banks onto the stack. To cut down on redundancy, I decided to only to process a mirroring change, if the new mirroring was different (for a explanation of NES mirroring see the nesdev wiki). Well, the NES nerds might already see what I did wrong. if (this.mirroring != mirroring) is not enough. For one-screen mirroring, the location of the screen is also important. To me, this makes if (this.mirroring != mirroring && this.oneScreenOffset != oneScreenOffset).
So, the moral of the story, is make sure you are taking a step back and considering *all* pertinent variables before you optimize something. I'm also guilty of premature optimization, as the bankswitch cache is as of yet in no danger of overflowing. Theoretically, there could be thousands of bankswitches during a frame, it's up to the cart and mapper. Realistically, it's very unlikely, at least on conventional mappers in commercial carts.
So TMNT and others display just fine, once again. Happy turtlin'.
Monday, March 1, 2010
Silverlight, Binding, Notifications and Threads
It's always good practice to not do anything on the UI from anything but the thread it was created on, but with Silverlight it is law. This extends all the way to objects which implement INotifyPropertyChanged. If they are to raise the PropertyChanged event, they must raise it on the UI thread - or you have broken the law and go to jail without passing Go and collecting $200.
So, my solution is to make sure all my view models have a Dispatcher through which to send PropertyChanged events. This has to be bound when they are created, which makes the way I was resolving them from Unity a bit ugly - I must now register a Dispatcher instance in the unity container for it to be all good, or bind it later, when the view model is resolved.
Another rub is the lack of command binding in Silverlight, when it comes to reusing the view models I had already implemented for silverlight/gtkInstigator. I had stored my commands in a dictionary, and they were resolved through a value converter which would pull them out of the command dictionary by name. Well, for Silverlight, I am putting the command name into the Tag, and use the following code in the click handler:
Problems still to solve: refreshing the enabled/disabled statuses of my buttons, by watching for a PropertyChanged event with "Commands" as a parameter, then iterating through the commands dictionary and available buttons, and setting enabled/disabled to the return of CanExecute.
Passing parameters to commands is another problem which I've yet to have need to solve - all of my commands are simple parameterless actions, so far.
At least ICommand is there, and command binding will likely show up, so using the commanding pattern still makes a lot of sense, even if you have to roll up your sleeves.
So, my solution is to make sure all my view models have a Dispatcher through which to send PropertyChanged events. This has to be bound when they are created, which makes the way I was resolving them from Unity a bit ugly - I must now register a Dispatcher instance in the unity container for it to be all good, or bind it later, when the view model is resolved.
Dispatcher dispatcher;
public Dispatcher Dispatcher
{
get { return dispatcher; }
set { dispatcher = value; }
}
protected virtual void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
if (dispatcher != null)
{
dispatcher.BeginInvoke(new CommandExecuteHandler(SendProperty), propName);
}
}
}
void SendProperty(object prop)
{
string propName = prop as string;
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propName));
OnPropertyChanged(propName);
}Another rub is the lack of command binding in Silverlight, when it comes to reusing the view models I had already implemented for silverlight/gtkInstigator. I had stored my commands in a dictionary, and they were resolved through a value converter which would pull them out of the command dictionary by name. Well, for Silverlight, I am putting the command name into the Tag, and use the following code in the click handler:
private void Button_Click(object sender, RoutedEventArgs e)
{
var button = sender as Button;
var dc = DataContext as IViewModel;
if (button != null && dc != null)
{
if (dc.Commands.ContainsKey(button.Tag as string))
{
ICommandWrapper command = dc.Commands[button.Tag as string];
if (command.CanExecute(null))
{
command.Execute(null);
}
}
}
}Problems still to solve: refreshing the enabled/disabled statuses of my buttons, by watching for a PropertyChanged event with "Commands" as a parameter, then iterating through the commands dictionary and available buttons, and setting enabled/disabled to the return of CanExecute.
Passing parameters to commands is another problem which I've yet to have need to solve - all of my commands are simple parameterless actions, so far.
At least ICommand is there, and command binding will likely show up, so using the commanding pattern still makes a lot of sense, even if you have to roll up your sleeves.
Sunday, February 28, 2010
Easy peasey
I'd hokied something up in Silverlight 2, which had me creating individual PNGs in memory and popping them into an image. It was, as you can image, quite painfully slow, but made a cute demo.
I don't know why it took so long to get around to it, but this took all of 15 minutes.. Just dummied up the sound/control/filesystem bindings for the moment. I'll do sound now, Pete Brown posted exactly what I need on his blog for his c64 emulator, so I'll go ahead and share and share alike. I hope my copy/paste keys work.
(Update: they work just fine.. Now with sound. I will not make this playable, in fact the rom image used has the main game engine disabled for now. This is a demonstration only, go pay 5 bucks on WiiWare if you want to play!)
But, if anyone is out there, tell me both how crappy/awesome your pc is and how fast it is running, both in and out of the browser (right click and install it).
I don't know why it took so long to get around to it, but this took all of 15 minutes.. Just dummied up the sound/control/filesystem bindings for the moment. I'll do sound now, Pete Brown posted exactly what I need on his blog for his c64 emulator, so I'll go ahead and share and share alike. I hope my copy/paste keys work.
(Update: they work just fine.. Now with sound. I will not make this playable, in fact the rom image used has the main game engine disabled for now. This is a demonstration only, go pay 5 bucks on WiiWare if you want to play!)
But, if anyone is out there, tell me both how crappy/awesome your pc is and how fast it is running, both in and out of the browser (right click and install it).
Friday, February 26, 2010
More D3D fullscreen notes
Firstly, I was keeping both my full and windowed swapchains alive, and getting a SEHException trying to dispose, so firing up the DebugView told me:
Fair enough. I decided to change the code to create a new form, new swap chain, and new render target for every switch to fullscreen, and to Dispose it when switched back. If the application tries to exit while fullscreen, the swapchain is switched to windowed and the form destroyed before this can happen. Seems fine to me so far.
But DebugView had more to say:
Presentation inefficiencies!? Oh noes what can this mean! It means that the swapchain is blitting the backbuffer to the front buffer each time I call Present(), instead of page flipping. This isn't what I signed up for!
Luckily the DXGI error itself is full of friendly advice. Am I calling ResizeBuffers appropriately? I'm not calling it at all, I'm creating the window explicitly for fullscreen, then killing it. Am I specifying a MODE_DESC that's not available? Well, R8G8B8A8_UNORM, 1680x1050 at 60Hz is my monitors native display mode, so I didn't think so. Though, the truth is I was. Selecting the same mode from the result set of GetDisplayModeList works fine.
The above code is pretty sloppy, I'll bind the mode collection to a nice combo box and allow the user the choice. It's also hard coded to the primary display adapter, and primary display - which is probably the right choice in most cases, but I'll make this stuff configurable too. It's selecting the highest available mode, which with my hardware happens to be my LCD's native resolution and refresh rate.
Since the output of a NES is so low res, stretching it to a 1680x1050 back buffer is overkill, but nothing the hardware cant handle. Running it in 640x480 mode visually looks exactly the same, as the image is scaled fullscreen when it's presented. I'm considering a little algorithm to look for the first available display mode that has a larger resolution than the last stage in rendering, since it should be the least needed to do the job.
On the topic D3D10/11 surfaces which you can embed in WPF, and overlay with content, it's doable, and I'll play around with another swapchain which renders to a shared resource, which is then presented by D3D9 into WPF's D3DImage. Simple on the surface, but getting it to work properly seems full of little gotchas.
There's no reason to be working in D3D10 anymore, a port to D3D11 should be extremely painless. It's all the same if you aren't using the new stuff. Right now I'm on the fence between continuing down the D3D path, or porting everything over to OpenGL and going that way. It'll likely be the latter, but the last time I tried I had trouble setting it up. Tao Framework doesn't seem to be updated anymore. I think the best way to go would be a native dll for opengl.
Oh, one more thing on the topic of this hybrid fullscreen solution:
You want to associate your factory with the window it's drawing to, as my switch back to windowed wasn't working right if my current focus was on a different display (ie; my mouse pointer is resting over on the second monitor). I also tell D3D to ignore AltEnter, since I'm handling and calling SetFullScreenState by myself and I don't need it in my face - D3D would put the "hidden" form back into windowed mode and do nothing else.
[3308] DXGI Error:
[3308] Swapchain Released while fullscreen. Switch it to the windowed state first.
Fair enough. I decided to change the code to create a new form, new swap chain, and new render target for every switch to fullscreen, and to Dispose it when switched back. If the application tries to exit while fullscreen, the swapchain is switched to windowed and the form destroyed before this can happen. Seems fine to me so far.
But DebugView had more to say:
[3308] DXGI Warning:
[3308] IDXGISwapChain::Present: Fullscreen presentation inefficiencies incurred due to application not using IDXGISwapChain::ResizeBuffers appropriately, specifying a DXGI_MODE_DESC not available in IDXGIOutput::GetDisplayModeList, or not using DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH. DXGI_SWAP_CHAIN_DESC::BufferDesc = { 1680, 1050, { 60, 1 }, R8G8B8A8_UNORM, 0, 0 }; DXGI_SWAP_CHAIN_DESC::SampleDesc = { 1, 0 }; DXGI_SWAP_CHAIN_DESC::Flags = 0;
Presentation inefficiencies!? Oh noes what can this mean! It means that the swapchain is blitting the backbuffer to the front buffer each time I call Present(), instead of page flipping. This isn't what I signed up for!
Luckily the DXGI error itself is full of friendly advice. Am I calling ResizeBuffers appropriately? I'm not calling it at all, I'm creating the window explicitly for fullscreen, then killing it. Am I specifying a MODE_DESC that's not available? Well, R8G8B8A8_UNORM, 1680x1050 at 60Hz is my monitors native display mode, so I didn't think so. Though, the truth is I was. Selecting the same mode from the result set of GetDisplayModeList works fine.
var modes = dxgiFactory.GetAdapter(0).GetOutput(0).GetDisplayModeList(DXGI.Format.R8G8B8A8_UNorm, DXGI.DisplayModeEnumerationFlags.Scaling | DXGI.DisplayModeEnumerationFlags.Interlaced);
DXGI.ModeDescription desc = modes[modes.Count() - 1];
The above code is pretty sloppy, I'll bind the mode collection to a nice combo box and allow the user the choice. It's also hard coded to the primary display adapter, and primary display - which is probably the right choice in most cases, but I'll make this stuff configurable too. It's selecting the highest available mode, which with my hardware happens to be my LCD's native resolution and refresh rate.
Since the output of a NES is so low res, stretching it to a 1680x1050 back buffer is overkill, but nothing the hardware cant handle. Running it in 640x480 mode visually looks exactly the same, as the image is scaled fullscreen when it's presented. I'm considering a little algorithm to look for the first available display mode that has a larger resolution than the last stage in rendering, since it should be the least needed to do the job.
On the topic D3D10/11 surfaces which you can embed in WPF, and overlay with content, it's doable, and I'll play around with another swapchain which renders to a shared resource, which is then presented by D3D9 into WPF's D3DImage. Simple on the surface, but getting it to work properly seems full of little gotchas.
There's no reason to be working in D3D10 anymore, a port to D3D11 should be extremely painless. It's all the same if you aren't using the new stuff. Right now I'm on the fence between continuing down the D3D path, or porting everything over to OpenGL and going that way. It'll likely be the latter, but the last time I tried I had trouble setting it up. Tao Framework doesn't seem to be updated anymore. I think the best way to go would be a native dll for opengl.
Oh, one more thing on the topic of this hybrid fullscreen solution:
dxgiFactory.SetWindowAssociation(fullForm.Handle, DXGI.WindowAssociationFlags.IgnoreAltEnter);
You want to associate your factory with the window it's drawing to, as my switch back to windowed wasn't working right if my current focus was on a different display (ie; my mouse pointer is resting over on the second monitor). I also tell D3D to ignore AltEnter, since I'm handling and calling SetFullScreenState by myself and I don't need it in my face - D3D would put the "hidden" form back into windowed mode and do nothing else.
Thursday, February 25, 2010
D3D 10 Full Screen Shenanigans
So I've been compiling release builds of my emulator lately, and been hosting them here ), and decided to add some full screen support. (sidebar; if anyone knows of some decent free hosting with Frontpage extensions, suitable for hosting ClickOnce packages let me know. It's shocking that not even MSFT's codeplex does
So, since I'm already happily rendering in a HwndHost, inside a WPF form, I figured it'd be a simple call to swapChain.SetFullScreenState(true, null). No such luck! It blowed up and I had to fire up DebugView to see what's going on in there. (sidebar the 2nd; VS 2010 RC doesn't seem to capture the native debug output, perhaps a bug?).
So, what's the story in there?
[5576] IDXGISwapChain::SetFullscreenState: Fullscreen is not allowed for swapchains targetting a child window. Applications are advised to create a second swapchain targetting a non-child window.
Oh yeah? Well scroo yoo too DXGI. I guess that makes sense.
Since I'd been advised to create a second swapchain, I decided that I would do just that. But, it needs to target a non-child window. Which is easy enough, it's a matter of creating a new (WinForms) Form and using it's window handle in the mode description.
So, to create the new swapchain, it's as easy as setting up a DXGI factory and doing the such:
_device is my D3D device, created earlier.
So, I have 3 SwapChain objects, one Windowed, one FullScreen, and one which holds a reference to the Active one, and this is toggled.
But it's not that easy! Firstly, if I create the new swapchain during app startup, the screen will blank out for a moment. So, I defer it until the first time it is needed. Secondly, switching the swap chain isn't enough. My code is set to render to a resource (texture) which is created from the orignal swap chain. So, the resource and RenderTargetView need to be rebuilt:
The last two lines are specific to my app, they set the RenderTarget on the last stage in my rendering chain, thus making it render to the swapchains buffer.
There's more that must be done, the ViewPort should be resized to match the new swapchain.
So, the strangest thing is in switching back to windowed mode. After setting the ActiveSwapChain, it was bombing out of the Toggle method I had wrote, leaving the app in a weird hybrid state. I could only correct it by including a Thread.Sleep(500) in there. I don't like this, obviously. My gut tells me that the emulator is requesting frames to be drawn in the interim, but it really should not be so, as the update calls should be blocked by a lock(this) during the swap.
Well, I'm not a DXGI guru yet, so I guess I'll just keep to the googling and hacking until I figure it out. This does seem to work cleanly, however, despite the icky Sleep call.
Anyone wanting to try my emulator may do so at the link above. If you want to play nintendo games for free, you're probably better off elsewhere. It's still chock full of bugs, the new UI was composed over the last few days from the detritus of fishbulb's past. But, there it is. 10NES.
In other news, if you happen to be looking for a talented coder to join your team in the Baltimore/Washington/Annapolis area(s), feel free to get in touch with me.
So, since I'm already happily rendering in a HwndHost, inside a WPF form, I figured it'd be a simple call to swapChain.SetFullScreenState(true, null). No such luck! It blowed up and I had to fire up DebugView to see what's going on in there. (sidebar the 2nd; VS 2010 RC doesn't seem to capture the native debug output, perhaps a bug?).
So, what's the story in there?
[5576] IDXGISwapChain::SetFullscreenState: Fullscreen is not allowed for swapchains targetting a child window. Applications are advised to create a second swapchain targetting a non-child window.
Oh yeah? Well scroo yoo too DXGI. I guess that makes sense.
Since I'd been advised to create a second swapchain, I decided that I would do just that. But, it needs to target a non-child window. Which is easy enough, it's a matter of creating a new (WinForms) Form and using it's window handle in the mode description.
So, to create the new swapchain, it's as easy as setting up a DXGI factory and doing the such:
fullSwapChainDescription = new DXGI.SwapChainDescription();
fullSwapChainDescription.ModeDescription = fullDesc;
fullSwapChainDescription.SampleDescription = sampleDescription;
fullSwapChainDescription.BufferCount = 2;
fullSwapChainDescription.Flags = DXGI.SwapChainFlags.None;
fullSwapChainDescription.IsWindowed = false;
fullSwapChainDescription.OutputHandle = myForm.Handle;
fullSwapChainDescription.SwapEffect = DXGI.SwapEffect.Discard;
fullSwapChainDescription.Usage = DXGI.Usage.RenderTargetOutput;
FullSwapChain = new DXGI.SwapChain(dxgiFactory, _device, fullSwapChainDescription);
_device is my D3D device, created earlier.
So, I have 3 SwapChain objects, one Windowed, one FullScreen, and one which holds a reference to the Active one, and this is toggled.
But it's not that easy! Firstly, if I create the new swapchain during app startup, the screen will blank out for a moment. So, I defer it until the first time it is needed. Secondly, switching the swap chain isn't enough. My code is set to render to a resource (texture) which is created from the orignal swap chain. So, the resource and RenderTargetView need to be rebuilt:
if (resouce != null) resource.Dispose();
resource = Texture2D.FromSwapChain(ActiveSwapChain, 0);
RenderTarget = new D3D10.RenderTargetView(_device, resource
);
tileFilters[tileFilters.Count - 1].RenderToTexture(resource);
tileFilters[tileFilters.Count - 1].RenderTarget = RenderTarget;
The last two lines are specific to my app, they set the RenderTarget on the last stage in my rendering chain, thus making it render to the swapchains buffer.
There's more that must be done, the ViewPort should be resized to match the new swapchain.
So, the strangest thing is in switching back to windowed mode. After setting the ActiveSwapChain, it was bombing out of the Toggle method I had wrote, leaving the app in a weird hybrid state. I could only correct it by including a Thread.Sleep(500) in there. I don't like this, obviously. My gut tells me that the emulator is requesting frames to be drawn in the interim, but it really should not be so, as the update calls should be blocked by a lock(this) during the swap.
Well, I'm not a DXGI guru yet, so I guess I'll just keep to the googling and hacking until I figure it out. This does seem to work cleanly, however, despite the icky Sleep call.
Anyone wanting to try my emulator may do so at the link above. If you want to play nintendo games for free, you're probably better off elsewhere. It's still chock full of bugs, the new UI was composed over the last few days from the detritus of fishbulb's past. But, there it is. 10NES.
In other news, if you happen to be looking for a talented coder to join your team in the Baltimore/Washington/Annapolis area(s), feel free to get in touch with me.
Tuesday, January 5, 2010
Sidetracked..
I took a break from the NES emulator because I started to get annoyed with some fussy texel/pixel alignment troubles with my scheme for rendering WPF content onto a texture. I'll do a 180 soon enough and get back on it. I threw the code together in a big hurry, and did lots of late night hacking to figure out how to make my idea workable. It really needs to be redone soup-to-nuts now that I've done 'proof of concept'.
In the meantime, I've started laying down the foundation for a decision tree I plan to use as the core of a chess engine. My goal with this is to exploit modern GPUs and see what it takes to parallelize, and maybe even polish up my endgame tactics. Chess is the goal, but I'll start with a game with a simpler ruleset - like checkers.
I plan to have the engine work entirely procedurally, and later add on a state-based mechanism to force moves in certain scenarios - which seems to be the key to an engine which plays not only a good game of chess, but an enjoyable one, rather than playing the same closed game over and over, which tends to be the case with 'lesser' chess computers.
I also started noodling with a tone/noise generator for Droid, hopefully something I can even commercialize for a few bucks. I'd actually really like to port over the sound code from my emulator, a well done chiptune player for droid would sell. Still just at the noodling stage, though.
In the meantime, I've started laying down the foundation for a decision tree I plan to use as the core of a chess engine. My goal with this is to exploit modern GPUs and see what it takes to parallelize, and maybe even polish up my endgame tactics. Chess is the goal, but I'll start with a game with a simpler ruleset - like checkers.
I plan to have the engine work entirely procedurally, and later add on a state-based mechanism to force moves in certain scenarios - which seems to be the key to an engine which plays not only a good game of chess, but an enjoyable one, rather than playing the same closed game over and over, which tends to be the case with 'lesser' chess computers.
I also started noodling with a tone/noise generator for Droid, hopefully something I can even commercialize for a few bucks. I'd actually really like to port over the sound code from my emulator, a well done chiptune player for droid would sell. Still just at the noodling stage, though.
Subscribe to:
Comments (Atom)