Flushing the cache

April 26, 2013 – 4:14 am

Random things that have happened over the last few months, but never deserved their own entry…

We visited Miami (very cool, relaxed vibe. Perfect weather in December, cannot imagine living there in August), we visited New York (very busy, very loud, very crowded, still cool. Was nice to meet with some ex Starbreeze folks again)…

… and we “shipped” a game:

It’s been a great ride so far and a completely new experience for me. Shortly after returning from post-Darkness 2 vacations we started experimenting with new projects. In March 2012 we had an idea and not much else. ~6 months later we hit a closed beta. It still sounds a little bit unreal. Did I mention our team was less than 10 programmers for most of that time? I’ve seen it in the past, but I’m still amazed by the efficiency you get with small and experienced team. In many cases it took minutes to go from idea to implementation.
I will not pretend I knew much about the whole free-2-play thing, because I didn’t it. I still don’t, but I learned a thing or two on the way. That layer apart, it’s been a really interesting technical challenge. We did not have all the third-party mechanisms you can rely on usually, no Steam (well, we are on Steam, but it’s only one of the options), no Xbox Live, no PSN, all in-house tech.
The best (…and worst) thing about this kind of project? We’re never really done. We made some really major changes since first starting in October. For example, at one point we decided it was time to finally bite the bullet and retrofit a host migration. We changed our lobby idea few times, we keep adding new stuff obviously. The other thing that’s cool & scary at the same time is number of people playing (we’re usually sitting in the Steam Top 10 + we have our “own”, non-Steam players) and how it affects rare bugs. Suddenly these once-in-a-blue moon cases happen few times a day (don’t even want to think how it looks for DOTA2 guys). No theory is too crazy… I’ve seen repeatable crashes caused by same packet dropped twice, then delayed. You can’t really hope to repro these bugs, it boils down to sifting through megabytes of logs and then trying to create a test for your hypothesis. Challenging, but fun (…I’m probably saying that because most of the heavy offenders seem to be fixed now).

Choices & consequences

April 19, 2013 – 5:32 am

Spent a little bit time tweaking RDE vector class again. As I already mentioned, the container itself is not terribly fascinating, there are not too many choices here. There’s another battle going on the lower level though, it’s interesting to see how an innocent instruction like size() can be a cause of a slowdown. Typically, a vector class has 3 properties it needs to keep track of:

  • buffer properties (pointer and size),
  • number of stored elements

Read the rest of this entry »

A world without Lucas Arts

April 4, 2013 – 3:03 am

LucasArts logoWorking in a game industry for more than few years tends to desensitize one to all the news about mass layoffs & companies going bust. Sadly, it happens so often, we’re slowly becoming used to it. I first found out about Disney shutting down Lucas Arts at work. Sure, I was surprised, it’s a big news after all, but I was busy, so didn’t think twice about it… I finished work, came home, read all the updates and then it suddenly hit me. LucasArts is no more.

It wasn’t the same company as in the 80s/90s and the last LA game I played was The Force Unleashed, but it is an end of an era… So many great memories. I was a huge fan of adventure games when growing up and Sierra & Lucas were probably my favorite companies. I must admit I’m not that crazy about Monkey Island, riddles were a little too abstract for my taste (or perhaps I was too young/my English sucked too much to appreciate it), but finished it and enjoyed it obviously. Indiana Jones, though – oh, that’s a different story. I still remember completing the first chapter, watching the movie-like Indy map sequence and thinking to myself: “best game ever”. Little did I know… My favorite LucasArts adventure game (or maybe the best adventure game ever?) is actually Day of the Tentacle. Surreal, yet logical, just ingenious.

Then there are all the space shooter games… I’m ashamed to admit Tie Fighter was the first game I cracked (in my defense there was absolutely no way to obtain a legal copy of that game in Poland back then and I never released the crack to public)…
I loved LA games as a kid and never thought too much about technology behind them. It was years later when I learned about all the cool stuff like SCUMM… Hell, those guys made an MMO running on C64

LucasArts of today wasn’t the same company and as I mentioned, I no longer followed their releases religiously… It’s still a bit of a symbol to me. Their games provided me with hundreds of hours of pure, unadulterated fun. I can still remember the feeling of booting new game, seeing the logo and getting ready for adventure. It’s a sad day. To all Lucas Arts employees – thank you. /salute.

The undefined flag

February 9, 2013 – 7:33 am

This week I had one of the most interesting debugging sessions in a while. Here’s the minimal code snippet exhibiting the problem. It doesn’t really make much sense in itself, but I tried to remove everything not related to the bug itself (the actual case was much more convoluted):

struct SVar
{
    SVar() : m_v(23000) {}
    void operator=(unsigned short t)
    {
        if(_rotr16(m_v, 1) != t)
        {
            m_v += t * 100;
        }
    }
    unsigned short    m_v;
};
static const int MAX_T = 1000;
struct Lol
{
    __declspec(noinline) void Cat(int t)
    {
        unsigned short newT = static_cast<unsigned short>(t < MAX_T ? t : MAX_T);
        m_t = newT; // ***********
        printf("New t = %d\n", newT);
    }
    SVar m_t;
};
void LolCat()
{
    Lol lol;
    lol.Cat(100);
    lol.Cat(100);
}

Focus on the Cat method. First, let’s imagine that line 19, marked with stars, is commented out. All that our method does is select smaller of given argument (always 100 in our case) & MAX_T (1000) and print it on the screen. Not surprisingly, it’ll print 100 twice (as 100 is smaller than 1000 obviously). Now, uncomment the assignment (m_t = newT) and things get more interesting. When compiled in release, 64-bit mode, the program now prints 100 & 1000 (tested with both MSVC 2008 & 2010)! It doesn’t make much sense, assignment operator never modifies newT, what’s going on here? We clearly need to dig deeper…

Read the rest of this entry »

x86/x64 MSVC plugins

January 26, 2013 – 2:50 am

Recently I had to write a tiny MSVC plugin to help visualizing one of our structures. It’s been a while since I’ve done it last time, so I started Googling for help. The good news is — it’s now much easier to find information/articles (mostly unofficial). The bad news — there are still many dark corners & I ran into a problem that took me a while to figure out.
Our game runs both in 32-bit (x86) & 64-bit mode (x64, we have two executables). I wanted my plugin to work with both, which might be tricky if your structures contain pointers. Example:

struct Foo
{
    void* data;
    Bar bar;
};

Now, imagine we’d like to get information about ‘bar’ member (given Foo pointer), offset will be different in x64/x86 builds. Luckily, there’s a method in DEBUGHELPER class that lets us retrieve configuration — GetProcessorType. Sample usage:

template<typename PtrType>
struct Foo
{
    PtrType    data;
    Bar        bar;
};
//DEBUGHELPER *pHelper
enum MPT
// stolen from https://github.com/dawgfoto/cv2pdb/blob/master/src/dviewhelper/dviewhelper.cpp
{
    MPT_X86 = 0,
    MPT_IA64,
    MPT_AMD64,
    MPT_Unknown
};
const int cpuType = pHelper->GetProcessorType(pHelper);
DWORD bytesRead(0);
const DWORD bytesToRead = (cpuType == MPT_X86 ? sizeof(Foo<uint32>) : sizeof(Foo<__int64>));
Foo<__int64> foo; // bigger type by default (also -- stricter alignment reqs)
if (pHelper->ReadDebuggeeMemoryEx(pHelper, pHelper->GetRealAddress(pHelper),
    bytesToRead, &foo, &bytesRead) != S_OK || bytesRead != bytesToRead)
{
    return E_FAIL;
}
Bar* bar = &foo.bar;
if(cpuType == MPT_X86)
{
    Foo<uint32>* ff32 = reinterpret_cast<Foo<uint32>* >(&foo);
    bar = &ff32->bar;
}
// Process bar now, it's pointing to a correct location both for 32 & 64 bit builds.

Junkers

January 16, 2013 – 3:04 am

Today I was contacted by Syama Pedersen and asked about participation in an indie game project called Junkers. I don’t really have too much spare time these days, so it’s not possible, but I thought I’d at least help spreading the word, perhaps someone would be interested in joining their team. Demo reel can be found here, contact Syama if you’d like to help.

Coding in a debugger

October 29, 2012 – 1:24 am

Recently, I spent some time debugging all kinds of crazy, once-in-a-blue-moon type of bugs, usually MP related and often happening in final/release builds only. The annoying problem with these is they tend to “hide” when you try to repro them and then pop up 5 minutes later when you’re investigating something else. That’s why it’s often crucial to catch every chance you get and try to extract as much information as possible out of every case. It might not be your perfect scenario, you’re not prepared, you’ve just disabled your diagnostics, you might be running an optimized build, but it should still be possible to at least verify some theories. You can’t afford to shut the game down, modify the code and run again, bug happens too rarely to hope we’ll get it soon. In situations like this, being able to modify code/data “on the fly”, using debugger might prove priceless. I’ll focus on x86 here, but in most cases it’s just a matter of finding respective opcodes for your platforms. Here’s a small collection of tricks I like to employ on special occasions: Read the rest of this entry »

Copy or move

October 18, 2012 – 5:14 am

I’ve been optimizing RDE’s vector class a little bit recently. In all honesty, it’s probably the least interesting container, there are only so many ways to do things, so it all boils down to working at the instruction level, trying to eliminate everything that’s not needed. Those few cycles don’t even matter in the grand scheme of things, but it can be an interesting learning experience at times. One of the functions I was interested in was erase. Let’s take a quick look at the code:

iterator erase(iterator it)
{
        // [Preconditions, invariants etc.]
        // Move everything down, overwriting *it
        internal::move(it + 1, m_end, it, int_to_type<has_trivial_copy<T>::value>());
        [...]

Line 5 is where the magic happens. We’re erasing an element at position ‘it‘, so we need to move all elements following it one element “down” (it+1 overwriting it, it+2 overwriting it+1 etc). internal::move will select an optimal way of moving the data, either memmove or one-by-one, using assignment operator. Now, you might be wondering if we have to use move here, wouldn’t copy suffice (if you need to refresh your memory on differences — Google memcpy vs memmove)?

Read the rest of this entry »

Pointers to member functions

September 9, 2012 – 6:24 pm

Pointers to member functions is one of those aspects of C++ that I use every 2 years or so. I know how they work, but I usually need some refreshing on the details (especially the syntax kills me every time, although I might actually remember it now). One of the gotchas with them is – you’re not supposed to cast it to void* (see this question for example). That’s one of those guidelines that I read long time ago, absorbed and never gave a second thought. Recently, however, I’ve been working with some legacy code that required me to investigate this issue more carefully.

Read the rest of this entry »

A Tale of 3 Characters

September 4, 2012 – 3:03 am

Had an interesting little debugging adventure recently. Few weeks ago we started getting QA reports about some of the visual effects looking all weird. I tested it locally, it all seemed fine. QA dug deeper and they discovered it only occured with disc builds (not necessarily running from a DVD, just after installing fully preprocessed build), P4 version was good. The problem with installed builds is they are very close to final, fully optimized, all assets in final version with no debug info etc, so it’s pain in the ass to debug. I started with running it under PIX and discovered that for some reason shader was not getting all the parameters it expected, so it was using whatever random data was in registers. Seemed like a preprocessing bug. Next step was trying to discover the difference between my local data (OK) and the one disc build was using. I discussed it with the engine guys and found out that we recently switched the build/cache machine to 64-bit. (I’m running both, but mostly 32). Switched to 64-bit locally, forced preprocessing and finally got it to ‘break’ locally. At this point it was quite clear there was a problem with preprocessing in 64-bit builds. It went quick from here and after few minutes I got to the bottom of the issue.

Read the rest of this entry »