warning C4244: 'argument' : conversion from 'uint64' to 'size_t', possible loss of data
1
2
3
4
5
Win32BeginRecordingInput(/*...*/)
{
    //...
    CopyMemory(ReplayBuffer->MemoryBlock, State->GameMemoryBlock, State->TotalSize);
}
The signature for CopyMemory
1
2
3
4
5
6
7
8
// WinBase.h
#define CopyMemory RtlCopyMemory

// WinNT.h
#define RtlCopyMemory(Destination,Source,Length) memcpy((Destination),(Source),(Length))

// wchar.h
void * memcpy(void *dest, const void *src, size_t count);
The parameter State
1
2
3
4
5
6
// win32_handmade.h
struct win32_state
{
    uint64 TotalSize;
    //...
};
The correcton is
1
2
3
// warning C4244: 'argument' : conversion from 'uint64' to 'size_t', possible loss of data
//CopyMemory(ReplayBuffer->MemoryBlock, State->GameMemoryBlock, State->TotalSize);
CopyMemory(ReplayBuffer->MemoryBlock, State->GameMemoryBlock, size_t(State->TotalSize));
I thought to use SafeTruncateUInt64 but that would truncate the unsigned value as well.
What I want is Stroustrup's narrow_cast template.
1
2
3
4
5
6
7
8
9
template < class R, class A >
R narrow_cast(const A& a)
{
    R r = R(a);
    if (A(r) != a) {
        throw std::runtime_error("info loss");
    }
    return r;
}
This implementation entails using exceptions. A trivial change would use an assert. Like SafeTruncateUInt64, the purpose of the template is to tell you when the conversion doesn't work and alters data. The template works for multiple types. Usage would be
1
2
3
4
5
CopyMemory(
    ReplayBuffer->MemoryBlock
    , State->GameMemoryBlock
    , narrow_cast< size_t >(State->TotalSize)
    );
That, of course, is blasphemy in HMH.

Similarly, in Win32BeginInputPlayBack, we have
1
CopyMemory(State->GameMemoryBlock, ReplayBuffer->MemoryBlock, State->TotalSize);
And then in WinMain there is
1
2
3
4
5
6
7
ReplayBuffer->MemoryBlock = MapViewOfFile(
    ReplayBuffer->MemoryMap
    , FILE_MAP_ALL_ACCESS
    , 0
    , 0
    , Win32State.TotalSize      // warning
    );

This last entails the memory mapped file usage.

The short version is that, on a 32 bit platform, this use of memory mapped files doesn't work. I believe this was mentioned in passing on the stream.

The reason for using memory mapped files is to improve the speed of the input record-playback feature, which itself required saving a copy of the game memory. The stream uses more memory than a 32 bit system can handle without help. (You can already see the writing on the wall here.)

To get record-playback to work at all the game memory size was dropped down to about 1 Gb and then when saving that to a WesterDigital passport drive the speed was...unpleasant.

Using memory mapped files consumes virtual memory space so the game memory has to be reduced to accommodate it and this reduces the game memory by half for each memory mapped file. Without any paging support to manage the files there is no practical benefit for their use. Finally, you can avoid them altogether by imply reducing the game memory footprint to a manageable level (64 Mb) and record-playback runs fairly well.

I've dropped the game memory down to 64 Mb for both transient and permanent memory and hope that will last for a good while. In tests with higher values the record-playback becomes unproductive; it is only for testing purposes.

- tim