Jump to content

  • Log In with Google      Sign In   
  • Create Account


Deortuka

Member Since 23 Jan 2012
Offline Last Active Oct 03 2013 12:25 AM

#5068514 48 Hour Challenge Result

Posted by Deortuka on 09 June 2013 - 05:49 PM

I have been doing a major rewrite of the game engine I have been working on for two years. It is tedious and most of the time it is just plain boring. I decided to create a small side project just to make things interesting for a bit. I remembered hearing about a 48 hour game coding challenge and decided to try it out for myself. Although I gave myself 48 hours, I only put in about 6 hours of actual work.

 

It is a simple text based game called Galaxy Quest. There is room for a ton of improvement, but I decided to stop coding at the end of the time limit. I thought I would share the end result with those of you who may be interested.

 

The moral of this story: When things begin to seem mundane, do something simple and silly to make it fun again!

 

Attached File  Galaxy Quest.zip   102.31KB   103 downloads

 

Edit: Although I have come close, I have not beat this game yet. If you are interested in trying, a good hint is to avoid fighting until you have a few upgrades.




#5059051 Alternatives to singletons for data manager?

Posted by Deortuka on 03 May 2013 - 04:55 PM

Personally, I believe there is a widespread misconception that everything must be wrapped in a class when programming in C++. I try to stick to the keep it simple philosophy which is to simply keep things as simple as possible, but not any simpler. Singletons are a completely over-engineered version of a global variable. I avoid the use of singletons by creating a file-scope variable that is accessed via a method. It is a clean and simple solution. As for your dilemma, I recommend this approach in order for you to get a reference to the manager (although I prefer to pass references as parameters to the methods that need them whenever possible).




#5057481 Viewing in memory textures in VS 2012

Posted by Deortuka on 28 April 2013 - 10:16 AM

Thanks for the tip unbird!




#5057477 VS2012 & Git Problem

Posted by Deortuka on 28 April 2013 - 10:02 AM

I sucked it up and just learned how to use Git from the console. Everything works fine that way. Thanks for the advice.




#5057451 Viewing in memory textures in VS 2012

Posted by Deortuka on 28 April 2013 - 05:46 AM

You can view all resources from within the graphics debugger. It seems that textures are accessed by an ID number (i.e. obj:21) and I am not aware of a way to actually watch or name a texture. You can click on Object Table view and scan for the texture that has the right format, height, and width, or, if you create or bind it a specific point, you can locate the the link to the texture in the Event List. Once you find the texture, just click on the ID number (i.e. obj:21) and the texture will open up as a *.dds image.

 

Untitled.png




#5054672 Multithreading and ID3D11DeviceContext::Map

Posted by Deortuka on 18 April 2013 - 01:24 PM

Out of curiosity, I decided to see where the CPU was spending the most time at.

 

 

The user interface thread:

Untitled1.png

 

 

The main rendering thread:

 

Untitled2.png

 

In my opinion, it seems that letting a separate thread incur the expense of the Map/memcpy calls on a deferred context and then letting the main thread spend relatively no time executing the resulting command list is a pretty nice solution. The alternative would be to lock the immediate context in order to perform the Map/memcpy/Unmap. Doing that would, in a sense, force both the user interface thread and the main thread to pay the price.

 

Did the AMD developer give any reasons for discouraging the use of deferred contexts?




#5054271 Multithreading and ID3D11DeviceContext::Map

Posted by Deortuka on 17 April 2013 - 12:48 PM

I have somewhat figured out...

  • Its appears that when you call Map() on a deferred context, memory is allocated by the API and a pointer to that memory is what is returned (immediately) in the D3D11_MAPPED_SUBRESOURCE struct.
  • All writes are actually written to this temporary memory.
  • The Map() and Unmap() calls are recorded to the command list.
  • Executing the command list results in the actual resource data being updated with the data written to the temporary memory.
  • Releasing the command list will free this temporary memory.
  • Command list interfaces are free threaded, just like the device interface. (This means I don't have to sync the command list for creation on the interface thread and execution on the rendering thread.)

This may not be 100% correct but I am positive I am close to accurate. I have not figured out if the temporary memory is allocated on the GPU, but it's location is pretty far off from all other pointer locations I am using in that section of code (i.e. 0x0b5----- returned from deferred context Map() call vs. 0x16b----- that appears everywhere else, including what the immediate context Map() call returns). I look forward to your feedback.




#5053961 Multithreading and ID3D11DeviceContext::Map

Posted by Deortuka on 16 April 2013 - 02:39 PM

I managed to rewrite the code faster than I anticipated. Using the deferred context did, in fact, take care of the threading problem. However, the user interface texture does not update. I believe that the Map() and Unmap() calls made on the deferred context must be executed by the immediate context via a command list. If this is the case, I am utterly confused and here is why...

 

DeferredContext->Map(); // Get a pointer to sub resource data
memcpy(); // copy some new data to that location
DeferredContext->Unmap();
DeferredContext->FinishCommandList(); // Map(), and Unmap() get saved to this but NOT the memcpy call
...
...
...
ImmediateContext->ExecuteCommandList(); // What happens to the memcpy call to modify the data that Map() gives access to???

 

At the same time, I know that calling Map() does return a usable pointer immediately. If it did not, I would get an access violation from immediately trying to update the texture data.

 

There is very little information in the D3D docs about how Map() and Unmap() work with deferred contexts. Any help in the right direction would be greatly appreciated.




#5052418 C++ TBB simple/silly 2D->1D array question

Posted by Deortuka on 12 April 2013 - 07:02 AM

BGRA8& pixel = buffer[(y * width) + x];



#5050159 VS2012 Graphics Debugger

Posted by Deortuka on 04 April 2013 - 05:50 PM

Having swappable threading is amazing because it forces you to truly understand what is going on under the hood and gives you an amazing debugging tool at the same time. This new and basic understanding of the pipeline stages and immediate vs deferred contexts made me realize how sloppy and inefficient my previous rendering code was. My laptop (a.k.a. development rig) has a really crappy Intel graphics card (DX10 - limited multi-threading) and a pretty beefy (at least for a laptop) nVidia graphics card (DX11). It is really nice to see my engine run optimally on each graphics card.

 

Tip #1

State management was, or rather still is, a huge challenge. In order to avoid unnecessary state changes (single-threaded) I came up with a somewhat simple approach. Every state change function (ex. IASetIndexBuffer()) will set a "desired" variable if it differs from a "current" variable, as well as pushing a function pointer (ex. ApplyIASetIndexBuffer()) to a vector named "m_PendingStateChanges". The variables I just mentioned are simply the variables that I would pass to the actual API function.

 

ApplyState() just iterates though the vector, utilizing all function pointers it contains to call only the necessary Apply*() functions. Those Apply*() functions simply submit the state change to the API and then update the "current" and "desired" variabes. This system of handling state changes eliminates unnecessary stage changes as well as eliminating having to test each individual pipeline state once ApplyState() is called.

 

Currently I do this on a per context basis, passing "true" to FinishCommandList() in order for all the deferred contexts to behave nicely with each other. The D3D11 docs explicitly state that this is inefficient. I am currently trying to find a way to pull state changing away from the context level and closer to the device level. In theory, there is only one immediate context so there should only be one "pipeline state manager". Finding a way for this to play nice with multiple contexts in a multi-threaded scenario, as well as not breaking hot swapping, is the challenge.

 

Tip #2

When things work good, fire up the graphics debugger and save a snapshot. Clicking the device context of a good draw call will give you detailed information of the entire pipeline. Since you have no visual pipeline stages while using deferred contexts, having a snapshot of what the device contexts should look like (state-wise) is the next best thing. Even without a good snapshot saved, you can scan over the values and possibly spot the problem.

 

Tip #3

Consider using http://threadingbuildingblocks.org/ over managing threads manually. This book http://www.amazon.com/Intel-Threading-Building-Blocks-Parallelism/dp/0596514808/ref=sr_1_1?ie=UTF8&qid=1334719817&sr=8-1 is a must have should you choose to use TBB. Scalable, automatic (and even manual) task dispatching, and concurrent safe containers and allocators are just a few of the things this library offers.

 

These are just the top 3 I could come up with off the top of my head.




#5049831 VS2012 Graphics Debugger

Posted by Deortuka on 03 April 2013 - 11:35 PM

Well I took the advice you gave me when I began rewriting my rendering code and made the engine able to hot swap between multi-threaded rendering with deferred contexts and single threaded rendering using only the immediate context. Turns out using the graphics pipeline viewer was no help at all to me... I was accidentally swapping the stride and offset values when I set the vertex buffer for the input assembler. I had to compare IDeviceContext states of the new build and a build before the rewrite in order to catch that mistake.




#5047135 Problem rendering 2D images in Direct3D 11

Posted by Deortuka on 26 March 2013 - 09:54 PM

Sounds like you are multiplying those vertices with the transformation matrices in your vertex shader. You should create and use a separate vertex shader (which simply omits that multiplication) for those polygons.




#5043876 How to efficiently handle game objects in a client server model on the host s...

Posted by Deortuka on 16 March 2013 - 09:38 PM

I came up with a very simple way of getting around this issue in my engine when I realized I would have two maps loaded (one for server side calculations and one for client rendering) on the client that was hosting the server. I decided to treat all map resources (maps, models, textures, entities, etc...) as a set of data separate from the client and the server. If the client is connecting to a remote server, it uses the "global" data set to maintain it's copy of the server set. If a server is running as dedicated, it uses this "global" data set to update the game state. If a client is hosting a multiplayer or running a single player game (i.e. connecting to a local server), the server-side code will modify the "global" data set and the client-side code will read from it.

 

Edit: I noticed you came up with the same idea after I wrote this. I recommend that you go for it.




#5023852 General Questions About Networking in FPS Games

Posted by Deortuka on 21 January 2013 - 05:56 AM

1. You definitely want to pack as much information as you can (but not too much) per send call. So that is a yes to sending multiple commands per message. It will take some tweaking and testing on your part to get an optimal number of commands to send per message. I recommend sending as much as you possibly can and start cutting back if you begin to have bandwidth issues.

 

2. Sending binary data is in fact the best way to do it. Strings have to be converted into binary form to be sent through a socket and then converted back into string on the receiving end.

 

3. Well the numbering system I described works both ways. The server numbers outgoing packets so that the client will know if it is relevant or if it is old and needs to be discarded. The client should also let the server know the number of the last packet it received when it sends it's data to the server. This way the server knows if it should resend an old packet or not. This requires that your server save a copy of the last few packets sent out.

You could take this a step farther and have the server label packets as critical or optional. This way if an optional packet (say for instance spark effect) has not been acknowledged as being received as a client, it will not be resent. However, a critical packet (such as a "player died" notification) will be resent if the client didn't acknowledge receiving it. This will prevent the the client from interacting with something that shouldn't be there (and in return sending messages to the server about that interaction).

 

To re-simplify things:

1. Server keeps track of connected clients.

2. For each client, server has a vector of the last 10 (random number i picked out) packets sent to that client.

3. When server sends a packet (which should contain as many messages as possible) to a client it "numbers" it and adds it to that vector (over-writing the oldest one to prevent unnecessary heap allocations)

4. When server receives a packet from a client (which should contain the number of the packet last received on that client's end) it checks the "last packet received" number in it.

5. When server is ready to send information to a client, it determines if it should resend one or more old packets (stored in the vector described in #2) and sends those old packets along with any newer ones (all as one combined packet) to the client.

 

I hope this clears things up a bit.




#5023549 General Questions About Networking in FPS Games

Posted by Deortuka on 20 January 2013 - 11:52 AM

1. You will send the the data from the server to the client via sockets. It would be wise to make sure the only information you send is updates. For example, you send a power up's position to a client and now that client will see that power up at that location. The only time you will need to update the position of that power up to that client again is if it has moved. This approach will help minimize the bandwidth needed by the server.

 

2. This is entirely up to you. A good start is to send everything in "messages". For example, a player position message would be sent as:

[PLAYER_POSITION_MESSAGE]
[
    [PLAYER ID] (32 bits)
    [POSITION_X] (32 bits)
    [POSITION_Y] (32 bits)
]

 

3. You do not want to send information like this from the server. You would want to send a message like above with the information needed to replicate the effect (ex. effect type, position, etc.) and let the client take that information to create the effect. In fact, this is the only way that the server should even know about effects. The individual particles (or anything else that is not strictly "gameplay" related) should never be handled by the server. You will also need to have the server "number" it's outgoing messages to address the not guaranteed issue. Clients test the number of the incoming packet against the number of the last received in order to tell if it should be processed or discarded.

 

I seriously over simplified the answers to your questions, but the overall idea really is that simple. I hope this helps.






PARTNERS