Revert to enclose the menus and player in the div
Also add styles for the new views_container
This commit is contained in:
parent
3b1514fea1
commit
a3cef4f28c
|
@ -19,12 +19,14 @@
|
|||
#cineraIndex.hero dt,
|
||||
|
||||
.cineraMenus.hero,
|
||||
.cineraMenus.hero > .menu .refs,
|
||||
.cineraMenus.hero > .menu .filter_container,
|
||||
.cineraMenus.hero > .menu > .refs,
|
||||
.cineraMenus.hero > .menu > .filter_container,
|
||||
.cineraMenus.hero > .menu > .refs .ref,
|
||||
.cineraMenus.hero > .menu > .filter_container .filter_mode,
|
||||
.cineraMenus.hero > .menu > .credits_container,
|
||||
.cineraMenus.hero > .menu > .credits_container .credit,
|
||||
.cineraMenus.hero > .menu > .view,
|
||||
.cineraMenus.hero > .menu > .views_container .view,
|
||||
.cineraPlayerContainer .markers_container.hero,
|
||||
.cineraPlayerContainer .markers_container.hero > .marker {
|
||||
background-color: #161616;
|
||||
|
@ -43,12 +45,13 @@
|
|||
#cineraResults .markerList.hero > .marker,
|
||||
|
||||
.cineraMenus.hero,
|
||||
.cineraMenus.hero > .menu .refs,
|
||||
.cineraMenus.hero > .menu .filter_container,
|
||||
.cineraMenus.hero > .menu > .refs,
|
||||
.cineraMenus.hero > .menu > .filter_container,
|
||||
.cineraMenus.hero > .menu > .refs .ref,
|
||||
.cineraMenus.hero > .menu > .filter_container .filter_mode,
|
||||
.cineraMenus.hero > .menu > .credits_container,
|
||||
.cineraMenus.hero > .menu > .credits_container .credit,
|
||||
.cineraMenus.hero > .menu > .views_container,
|
||||
.cineraPlayerContainer .markers_container.hero,
|
||||
.cineraPlayerContainer .markers_container.hero > .marker {
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
|
@ -100,6 +103,8 @@
|
|||
.cineraMenus.hero > .menu > .filter_container .filter_mode:hover,
|
||||
.cineraMenus.hero > .menu > .filter_container .filter_content.focused,
|
||||
.cineraMenus.hero > .menu > .credits_container .credit *.focused,
|
||||
.cineraMenus.hero > .menu > .view:hover,
|
||||
.cineraMenus.hero > .menu > .views_container .view:hover,
|
||||
.cineraPlayerContainer .markers_container.hero > .marker:hover > .cineraContent {
|
||||
background-color: #222;
|
||||
}
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
<!-- __CINERA_INCLUDES__ -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="cinera">
|
||||
<div>
|
||||
<!-- __CINERA_MENUS__ -->
|
||||
<!-- __CINERA_PLAYER__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p><a href="https://drive.google.com/file/d/0B6DxtsjMnvF8R2Mzckc1R3dhbzQ/view?usp=sharing">Handmade Hero Day 001 - Setting Up the Windows Build</a> - by <a href="https://twitter.com/Mannilie">Emmanuel Vaccaro</a></p>
|
||||
</article>
|
||||
</div>
|
||||
<!-- __CINERA_SCRIPT__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p><a href="https://drive.google.com/file/d/0B6DxtsjMnvF8R2Mzckc1R3dhbzQ/view?usp=sharing">Handmade Hero Day 001 - Setting Up the Windows Build</a> - by <a href="https://twitter.com/Mannilie">Emmanuel Vaccaro</a></p>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
<!-- __CINERA_INCLUDES__ -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="cinera">
|
||||
<div>
|
||||
<!-- __CINERA_MENUS__ -->
|
||||
<!-- __CINERA_PLAYER__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p><a href="https://drive.google.com/open?id=0B6DxtsjMnvF8MjN1eER5NW8xV3M&authuser=0">Handmade Hero Day 002 - Opening a Win32 Window</a> - by <a href="https://twitter.com/Mannilie">Emmanuel Vaccaro</a></p>
|
||||
</article>
|
||||
</div>
|
||||
<!-- __CINERA_SCRIPT__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p><a href="https://drive.google.com/open?id=0B6DxtsjMnvF8MjN1eER5NW8xV3M&authuser=0">Handmade Hero Day 002 - Opening a Win32 Window</a> - by <a href="https://twitter.com/Mannilie">Emmanuel Vaccaro</a></p>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
<!-- __CINERA_INCLUDES__ -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="cinera">
|
||||
<div>
|
||||
<!-- __CINERA_MENUS__ -->
|
||||
<!-- __CINERA_PLAYER__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p><a href="https://drive.google.com/open?id=0B6DxtsjMnvF8VXF0d2F0ZjNzUE0&authuser=0">Handmade Hero Day 003 - Allocating a Back Buffer</a> - by <a href="https://twitter.com/Mannilie">Emmanuel Vaccaro</a></p>
|
||||
</article>
|
||||
</div>
|
||||
<!-- __CINERA_SCRIPT__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p><a href="https://drive.google.com/open?id=0B6DxtsjMnvF8VXF0d2F0ZjNzUE0&authuser=0">Handmade Hero Day 003 - Allocating a Back Buffer</a> - by <a href="https://twitter.com/Mannilie">Emmanuel Vaccaro</a></p>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,87 +3,87 @@
|
|||
<!-- __CINERA_INCLUDES__ -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="cinera">
|
||||
<div>
|
||||
<!-- __CINERA_MENUS__ -->
|
||||
<!-- __CINERA_PLAYER__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<h3 id="so-what-s-with-all-that-define-and-typedef-mumbo-jumbo-there-">So what's with all that "#define" and "typedef" mumbo-jumbo there?</h3>
|
||||
<p>You may have found yourself confused when looking at this code:</p>
|
||||
<pre><code>#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
|
||||
typedef X_INPUT_GET_STATE(x_input_get_state);
|
||||
X_INPUT_GET_STATE(XInputGetStateStub)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
global_variable x_input_get_state *XInputGetState_ = XInputGetStateStub;
|
||||
#define XInputGetState XInputGetState_
|
||||
</code></pre><p>What on God's green earth is happening here? Well, don't despair. It's really quite simple, we just need to unwrap a few things and break it down line by line.</p>
|
||||
<pre><code>#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
|
||||
</code></pre><p>In C (as well as C++ and Objective C), the first step of the compilation process is a code transformation pass by the preprocessor. As we have covered previously in the series, any command which begins with a "#" is what is known as a preprocessor directive. This is just a command which will tell the preprocessor to do something, such as #include a file. In this case, "#define" is a directive which creates a macro. A macro can be thought of as a code substitution, anytime the processor finds the macro used in your code, it will swap it out for whatever you defined.</p>
|
||||
<h2 id="the-two-types-of-macros">The two types of macros</h2>
|
||||
<p>There are two types of macros, the first type is very simple and defines a constant value:</p>
|
||||
<pre><code>#define PI 3.14159265359
|
||||
</code></pre><p>This macro will cause the preprocessor to convert any instances of "PI" in the code directly into "3.14159265359". If we break it down a bit, on the left hand side we have what is called the identifier ("PI"), and on the right hand side we have what is known as the token string ("3.14159265359"). The preprocessor searches the code for the identifier, and replaces it with the token string. This means that the literal text "PI" will no longer appear in our code, having been replaced with "3.14159265359". This can be useful for constant values that we may want to use repeatedly, but not type in full all the time. Note that the preprocessor will not convert PI if it appears in a comment or as part of a longer identifier such as "PieceOfPI".</p>
|
||||
<p>So let's go back to the macro we were looking at earlier. </p>
|
||||
<pre><code>#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
|
||||
</code></pre><p>It is somewhat more complex and appears to have a function definition on the right for a function called "name". This is the second type of macro, which uses a token string as part of it's definition.
|
||||
As a simpler, but somewhat different example:</p>
|
||||
<pre><code>#define ADD(argument1, argument2) argument1 + argument2
|
||||
</code></pre><p>Looking at this, we can see that it can be broken down into two parts just the same as the previous macro. First, on the left side we have the identifier "ADD(argument1, argument2)". This works similarly to the first kind of macro, but because we have written it like a function, the preprocessor will save whatever it finds in place of "argument1" and "argument2", and then plug those in for where they were found in the token string.</p>
|
||||
<p>For example, if later on in the code we were to type:</p>
|
||||
<pre><code>int c = ADD(a,b);
|
||||
</code></pre><p>Our macro would expand this out to:</p>
|
||||
<pre><code>int c = a + b;
|
||||
</code></pre><p>So, again we will go back to our original macro, but this time we are going to pair it with its first use:</p>
|
||||
<pre><code>#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
|
||||
typedef X_INPUT_GET_STATE(x_input_get_state);
|
||||
</code></pre><p>Based on what we learned earlier we can see that the second line will expand out into the following:</p>
|
||||
<pre><code>typedef DWORD WINAPI x_input_get_state(DWORD dwUserIndex, XINPUT_STATE *pState);
|
||||
</code></pre><h2 id="what-s-a-typedef-">What's a typedef?</h2>
|
||||
<p>So that's easy enough to see, but what does 'typedef' mean?</p>
|
||||
<p>In C, typedef declares a different name for a type. This can almost be thought of as similar to #define, but it is limited to type names only and is performed by the compiler instead of the preprocessor.</p>
|
||||
<p>As a basic example, if we were to look at the following code:</p>
|
||||
<pre><code>typedef char CHARACTER;
|
||||
CHARACTER x;
|
||||
</code></pre><p>The compiler will treat the type of x as though it were 'char.' Why would we want to do this? Well, because declaring our own types allows us to make use of the compilers strict type checking. This means that if we were to declare a function which took a CHARACTER as the type of one of it's arguments then we would not be able to pass it a char without the compiler warning us about it, even though a CHARACTER was defined as a char and they are both signed one byte values.</p>
|
||||
<pre><code>typedef DWORD WINAPI x_input_get_state(DWORD dwUserIndex, XINPUT_STATE *pState);
|
||||
</code></pre><p>But in the Handmade Hero code, we are using typedef with a function declaration. This declares a function signature as a type. It is important to understand that although we can use that type to declare a function like this:</p>
|
||||
<pre><code>x_input_get_state _XInputGetState()
|
||||
</code></pre><p>Due to the rules of C, we are not allowed to then go on to define the function as follows:</p>
|
||||
<pre><code>//INVALID C CODE FOLLOWS
|
||||
x_input_get_state _XInputGetState()
|
||||
{
|
||||
//Do some things.
|
||||
}
|
||||
</code></pre><p>We can define a function using our typedef and also declare the same function by using the original function type information, however, this makes the typedef itself unnecessary.</p>
|
||||
<h2 id="using-typedef-for-function-pointers">Using typedef for function pointers</h2>
|
||||
<p>So why are we using a typedef with a function declaration here? Because we are planning on using it to create a function pointer. A function pointer is just what it sounds like, a pointer to a function. The main benefit of function pointers is that they can be treated like variables, that is, they can be reassigned or passed as arguments.</p>
|
||||
<p>So this is how we would use our "x_input_get_state" typedef to declare a function pointer:</p>
|
||||
<pre><code>x_input_get_state *PointerToXInputGetStateFunction;
|
||||
</code></pre><p>We can then assign that pointer to any function which matches the signature of x_input_get_state:</p>
|
||||
<pre><code>typedef DWORD WINAPI x_input_get_state(DWORD dwUserIndex, XINPUT_STATE *pState);
|
||||
DWORD XInputGetStateStub(DWORD dwUserIndex, XINPUT_STATE *pState);
|
||||
XInputGetStateStub
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
global_variable x_input_get_state *XInputGetState_ = XInputGetStateStub;
|
||||
</code></pre><p>Which if we were to unwrap our macros from the original code, is almost exactly what we would have:</p>
|
||||
<pre><code>#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
|
||||
typedef X_INPUT_GET_STATE(x_input_get_state);
|
||||
X_INPUT_GET_STATE(XInputGetStateStub)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
global_variable x_input_get_state *XInputGetState_ = XInputGetStateStub;
|
||||
</code></pre><p>Now all we have left is the last line:</p>
|
||||
<pre><code>#define XInputGetState XInputGetState_
|
||||
</code></pre><p>This is another simple macro which will substitute "XInputGetState_" for "XInputGetState" any time we use it in the code. We do this because XInputGetState is a function that has already been declared in xinput.h, and C does not allow us to declare functions multiple times. However, by using the preprocessor, we can type out our code as though we were just using XInputGetState we are actually using a function pointer which will either point to 'XInputGetStateStub' in the case that we are unable to dynamically load in the XInput library, or will point to the correct 'XInputGetState' function as defined by Microsoft in the case that we can load it.</p>
|
||||
<p>Hope that's helped clear some things up for you. Happy Heroing!</p>
|
||||
</article>
|
||||
</div>
|
||||
<!-- __CINERA_SCRIPT__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<h3 id="so-what-s-with-all-that-define-and-typedef-mumbo-jumbo-there-">So what's with all that "#define" and "typedef" mumbo-jumbo there?</h3>
|
||||
<p>You may have found yourself confused when looking at this code:</p>
|
||||
<pre><code>#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
|
||||
typedef X_INPUT_GET_STATE(x_input_get_state);
|
||||
X_INPUT_GET_STATE(XInputGetStateStub)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
global_variable x_input_get_state *XInputGetState_ = XInputGetStateStub;
|
||||
#define XInputGetState XInputGetState_
|
||||
</code></pre><p>What on God's green earth is happening here? Well, don't despair. It's really quite simple, we just need to unwrap a few things and break it down line by line.</p>
|
||||
<pre><code>#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
|
||||
</code></pre><p>In C (as well as C++ and Objective C), the first step of the compilation process is a code transformation pass by the preprocessor. As we have covered previously in the series, any command which begins with a "#" is what is known as a preprocessor directive. This is just a command which will tell the preprocessor to do something, such as #include a file. In this case, "#define" is a directive which creates a macro. A macro can be thought of as a code substitution, anytime the processor finds the macro used in your code, it will swap it out for whatever you defined.</p>
|
||||
<h2 id="the-two-types-of-macros">The two types of macros</h2>
|
||||
<p>There are two types of macros, the first type is very simple and defines a constant value:</p>
|
||||
<pre><code>#define PI 3.14159265359
|
||||
</code></pre><p>This macro will cause the preprocessor to convert any instances of "PI" in the code directly into "3.14159265359". If we break it down a bit, on the left hand side we have what is called the identifier ("PI"), and on the right hand side we have what is known as the token string ("3.14159265359"). The preprocessor searches the code for the identifier, and replaces it with the token string. This means that the literal text "PI" will no longer appear in our code, having been replaced with "3.14159265359". This can be useful for constant values that we may want to use repeatedly, but not type in full all the time. Note that the preprocessor will not convert PI if it appears in a comment or as part of a longer identifier such as "PieceOfPI".</p>
|
||||
<p>So let's go back to the macro we were looking at earlier. </p>
|
||||
<pre><code>#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
|
||||
</code></pre><p>It is somewhat more complex and appears to have a function definition on the right for a function called "name". This is the second type of macro, which uses a token string as part of it's definition.
|
||||
As a simpler, but somewhat different example:</p>
|
||||
<pre><code>#define ADD(argument1, argument2) argument1 + argument2
|
||||
</code></pre><p>Looking at this, we can see that it can be broken down into two parts just the same as the previous macro. First, on the left side we have the identifier "ADD(argument1, argument2)". This works similarly to the first kind of macro, but because we have written it like a function, the preprocessor will save whatever it finds in place of "argument1" and "argument2", and then plug those in for where they were found in the token string.</p>
|
||||
<p>For example, if later on in the code we were to type:</p>
|
||||
<pre><code>int c = ADD(a,b);
|
||||
</code></pre><p>Our macro would expand this out to:</p>
|
||||
<pre><code>int c = a + b;
|
||||
</code></pre><p>So, again we will go back to our original macro, but this time we are going to pair it with its first use:</p>
|
||||
<pre><code>#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
|
||||
typedef X_INPUT_GET_STATE(x_input_get_state);
|
||||
</code></pre><p>Based on what we learned earlier we can see that the second line will expand out into the following:</p>
|
||||
<pre><code>typedef DWORD WINAPI x_input_get_state(DWORD dwUserIndex, XINPUT_STATE *pState);
|
||||
</code></pre><h2 id="what-s-a-typedef-">What's a typedef?</h2>
|
||||
<p>So that's easy enough to see, but what does 'typedef' mean?</p>
|
||||
<p>In C, typedef declares a different name for a type. This can almost be thought of as similar to #define, but it is limited to type names only and is performed by the compiler instead of the preprocessor.</p>
|
||||
<p>As a basic example, if we were to look at the following code:</p>
|
||||
<pre><code>typedef char CHARACTER;
|
||||
CHARACTER x;
|
||||
</code></pre><p>The compiler will treat the type of x as though it were 'char.' Why would we want to do this? Well, because declaring our own types allows us to make use of the compilers strict type checking. This means that if we were to declare a function which took a CHARACTER as the type of one of it's arguments then we would not be able to pass it a char without the compiler warning us about it, even though a CHARACTER was defined as a char and they are both signed one byte values.</p>
|
||||
<pre><code>typedef DWORD WINAPI x_input_get_state(DWORD dwUserIndex, XINPUT_STATE *pState);
|
||||
</code></pre><p>But in the Handmade Hero code, we are using typedef with a function declaration. This declares a function signature as a type. It is important to understand that although we can use that type to declare a function like this:</p>
|
||||
<pre><code>x_input_get_state _XInputGetState()
|
||||
</code></pre><p>Due to the rules of C, we are not allowed to then go on to define the function as follows:</p>
|
||||
<pre><code>//INVALID C CODE FOLLOWS
|
||||
x_input_get_state _XInputGetState()
|
||||
{
|
||||
//Do some things.
|
||||
}
|
||||
</code></pre><p>We can define a function using our typedef and also declare the same function by using the original function type information, however, this makes the typedef itself unnecessary.</p>
|
||||
<h2 id="using-typedef-for-function-pointers">Using typedef for function pointers</h2>
|
||||
<p>So why are we using a typedef with a function declaration here? Because we are planning on using it to create a function pointer. A function pointer is just what it sounds like, a pointer to a function. The main benefit of function pointers is that they can be treated like variables, that is, they can be reassigned or passed as arguments.</p>
|
||||
<p>So this is how we would use our "x_input_get_state" typedef to declare a function pointer:</p>
|
||||
<pre><code>x_input_get_state *PointerToXInputGetStateFunction;
|
||||
</code></pre><p>We can then assign that pointer to any function which matches the signature of x_input_get_state:</p>
|
||||
<pre><code>typedef DWORD WINAPI x_input_get_state(DWORD dwUserIndex, XINPUT_STATE *pState);
|
||||
DWORD XInputGetStateStub(DWORD dwUserIndex, XINPUT_STATE *pState);
|
||||
XInputGetStateStub
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
global_variable x_input_get_state *XInputGetState_ = XInputGetStateStub;
|
||||
</code></pre><p>Which if we were to unwrap our macros from the original code, is almost exactly what we would have:</p>
|
||||
<pre><code>#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
|
||||
typedef X_INPUT_GET_STATE(x_input_get_state);
|
||||
X_INPUT_GET_STATE(XInputGetStateStub)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
global_variable x_input_get_state *XInputGetState_ = XInputGetStateStub;
|
||||
</code></pre><p>Now all we have left is the last line:</p>
|
||||
<pre><code>#define XInputGetState XInputGetState_
|
||||
</code></pre><p>This is another simple macro which will substitute "XInputGetState_" for "XInputGetState" any time we use it in the code. We do this because XInputGetState is a function that has already been declared in xinput.h, and C does not allow us to declare functions multiple times. However, by using the preprocessor, we can type out our code as though we were just using XInputGetState we are actually using a function pointer which will either point to 'XInputGetStateStub' in the case that we are unable to dynamically load in the XInput library, or will point to the correct 'XInputGetState' function as defined by Microsoft in the case that we can load it.</p>
|
||||
<p>Hope that's helped clear some things up for you. Happy Heroing!</p>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,41 +3,41 @@
|
|||
<!-- __CINERA_INCLUDES__ -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="cinera">
|
||||
<div>
|
||||
<!-- __CINERA_MENUS__ -->
|
||||
<!-- __CINERA_PLAYER__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<h3 id="input-cleanup-and-fixes">Input Cleanup and Fixes</h3>
|
||||
<p>This episode starts with some cleanup and fixes to the input handling code from yesterday.</p>
|
||||
<ul>
|
||||
<li>Windows API often uses a <code>0</code> return value to indicate success. Our stubs probably shouldn't return <code>0</code>.</li>
|
||||
<li>Windows 8 ships with <code>xinput1_4.dll</code> only. So we need to try each version in turn</li>
|
||||
<li>When you handle WM_SYSKEY* messages, you lose built in Alt-F4 functionality, and have to reimplement it.</li>
|
||||
</ul>
|
||||
<h3 id="sound-programming-for-games">Sound Programming for Games</h3>
|
||||
<p>Casey starts with a high level overview of sound programing for games. The key ideas here are that we are allocating
|
||||
a <a href="http://en.wikipedia.org/wiki/Circular_buffer">circular buffer</a> for sound, and the system will play it continually
|
||||
on a loop. If you haven't worked with circular buffers (or ring buffers) before, much of this code will be confusing.
|
||||
It's worth taking some time to familiarize yourself with them.</p>
|
||||
<p>Resources:</p>
|
||||
<ul>
|
||||
<li><a href="http://c.learncodethehardway.org/book/ex44.html">Ring Buffers</a> on "Learn C The Hard Way"</li>
|
||||
</ul>
|
||||
<h3 id="working-with-directsound">Working with DirectSound</h3>
|
||||
<p>The basic process for initializing DirectSound is as follows:</p>
|
||||
<ol>
|
||||
<li>Load the Library - <a href="http://msdn.microsoft.com/en-us/library/ms684175.aspx"><code>LoadLibrary</code></a><code>("dsound.dll")</code></li>
|
||||
<li>Create a DirectSound object - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.reference.directsoundcreate8.aspx"><code>DirectSoundCreate()</code></a></li>
|
||||
<li>Set the Cooperative Level - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsound8.idirectsound8.setcooperativelevel.aspx"><code>IDirectSound8::SetCooperativeLevel()</code></a></li>
|
||||
<li>"Create" a primary buffer - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsound8.idirectsound8.createsoundbuffer.aspx"><code>IDirectSound8::CreateSoundBuffer()</code></a></li>
|
||||
<li>Create a secondary buffer</li>
|
||||
<li>Tell DirectSound to start playing the secondary buffer - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsoundbuffer8.idirectsoundbuffer8.play.aspx"><code>IDirectSoundBuffer8::Play()</code></a></li>
|
||||
</ol>
|
||||
<p>In the next episode we will look closely at how to fill this buffer and implement it in the game loop.</p>
|
||||
</article>
|
||||
</div>
|
||||
<!-- __CINERA_SCRIPT__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<h3 id="input-cleanup-and-fixes">Input Cleanup and Fixes</h3>
|
||||
<p>This episode starts with some cleanup and fixes to the input handling code from yesterday.</p>
|
||||
<ul>
|
||||
<li>Windows API often uses a <code>0</code> return value to indicate success. Our stubs probably shouldn't return <code>0</code>.</li>
|
||||
<li>Windows 8 ships with <code>xinput1_4.dll</code> only. So we need to try each version in turn</li>
|
||||
<li>When you handle WM_SYSKEY* messages, you lose built in Alt-F4 functionality, and have to reimplement it.</li>
|
||||
</ul>
|
||||
<h3 id="sound-programming-for-games">Sound Programming for Games</h3>
|
||||
<p>Casey starts with a high level overview of sound programing for games. The key ideas here are that we are allocating
|
||||
a <a href="http://en.wikipedia.org/wiki/Circular_buffer">circular buffer</a> for sound, and the system will play it continually
|
||||
on a loop. If you haven't worked with circular buffers (or ring buffers) before, much of this code will be confusing.
|
||||
It's worth taking some time to familiarize yourself with them.</p>
|
||||
<p>Resources:</p>
|
||||
<ul>
|
||||
<li><a href="http://c.learncodethehardway.org/book/ex44.html">Ring Buffers</a> on "Learn C The Hard Way"</li>
|
||||
</ul>
|
||||
<h3 id="working-with-directsound">Working with DirectSound</h3>
|
||||
<p>The basic process for initializing DirectSound is as follows:</p>
|
||||
<ol>
|
||||
<li>Load the Library - <a href="http://msdn.microsoft.com/en-us/library/ms684175.aspx"><code>LoadLibrary</code></a><code>("dsound.dll")</code></li>
|
||||
<li>Create a DirectSound object - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.reference.directsoundcreate8.aspx"><code>DirectSoundCreate()</code></a></li>
|
||||
<li>Set the Cooperative Level - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsound8.idirectsound8.setcooperativelevel.aspx"><code>IDirectSound8::SetCooperativeLevel()</code></a></li>
|
||||
<li>"Create" a primary buffer - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsound8.idirectsound8.createsoundbuffer.aspx"><code>IDirectSound8::CreateSoundBuffer()</code></a></li>
|
||||
<li>Create a secondary buffer</li>
|
||||
<li>Tell DirectSound to start playing the secondary buffer - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsoundbuffer8.idirectsoundbuffer8.play.aspx"><code>IDirectSoundBuffer8::Play()</code></a></li>
|
||||
</ol>
|
||||
<p>In the next episode we will look closely at how to fill this buffer and implement it in the game loop.</p>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,57 +3,57 @@
|
|||
<!-- __CINERA_INCLUDES__ -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="cinera">
|
||||
<div>
|
||||
<!-- __CINERA_MENUS__ -->
|
||||
<!-- __CINERA_PLAYER__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<h3 id="directsound-is-object-oriented">DirectSound is Object Oriented</h3>
|
||||
<p>DirectSound in an object-oriented API. What does that mean? Casey starts this episode with a discussion of "method"
|
||||
dispatch through vtables, and why c++ virtual calls are costly.</p>
|
||||
<h3 id="reviewing-directsound-inititalization">Reviewing DirectSound Inititalization</h3>
|
||||
<p>The basic process for initializing DirectSound is as follows:</p>
|
||||
<ol>
|
||||
<li>Load the Library - <a href="http://msdn.microsoft.com/en-us/library/ms684175.aspx"><code>LoadLibrary</code></a><code>("dsound.dll")</code></li>
|
||||
<li>Create a DirectSound object - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.reference.directsoundcreate8.aspx"><code>DirectSoundCreate()</code></a></li>
|
||||
<li>Set the Cooperative Level - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsound8.idirectsound8.setcooperativelevel.aspx"><code>IDirectSound8::SetCooperativeLevel()</code></a></li>
|
||||
<li>"Create" a primary buffer - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsound8.idirectsound8.createsoundbuffer.aspx"><code>IDirectSound8::CreateSoundBuffer()</code></a></li>
|
||||
<li>Create a secondary buffer</li>
|
||||
<li>Tell DirectSound to start playing the secondary buffer - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsoundbuffer8.idirectsoundbuffer8.play.aspx"><code>IDirectSoundBuffer8::Play()</code></a></li>
|
||||
</ol>
|
||||
<h3 id="playing-sounds">Playing sounds</h3>
|
||||
<p>Audio is a complicated topic, and we start with a discussion of audio "waveforms" and how PCM audio data is encoded in
|
||||
memory.</p>
|
||||
<ul>
|
||||
<li>A square wave oscillates between "full-positive" to "full-negative" every half period</li>
|
||||
<li>A Stereo (2-channel) 16-bit PCM audio buffer is arranged as an array of signed int16 values in (left channel value,
|
||||
right channel value) pairs</li>
|
||||
<li>A "sample" sometimes refers to the values for all channels in a sampling period, and sometimes a value for a single
|
||||
channel. Be careful.</li>
|
||||
</ul>
|
||||
<p>The procedure for writing sound data into a buffer is as follows</p>
|
||||
<ol>
|
||||
<li>Figure out where in the buffer you want to start writing, and how much data you want to write
|
||||
<ul>
|
||||
<li>Its useful to look at the play cursor - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsoundbuffer8.idirectsoundbuffer8.getcurrentposition.aspx"><code>IDirectSoundBuffer8::GetCurrentPosition()</code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Acquire a lock on the buffer - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsoundbuffer8.idirectsoundbuffer8.unlock.aspx"><code>IDirectSoundBuffer8::Lock()</code></a>
|
||||
<ul>
|
||||
<li>Because we are working with a circular buffer, this call will return 1 or 2 writable regions</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Write the samples to the buffer</li>
|
||||
<li>Unlock the regions - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsoundbuffer8.idirectsoundbuffer8.unlock.aspx"><code>IDirectSoundBuffer8::Unlock()</code></a></li>
|
||||
</ol>
|
||||
<h3 id="a-note-on-audio-latency">A note on audio latency</h3>
|
||||
<p>Audio latency is determined not by the size of the buffer, but by how far ahead of the PlayCursor you write. The optimal
|
||||
amount of latency is the amount that will cause this frame's audio to coincide with the display of this frame's image.
|
||||
On most platforms, it is very difficult to ascertain the proper amount of latency. It's an unsolved problem, and games
|
||||
with need precise AV sync (like Guitar Hero) go to some lengths to achieve it.</p>
|
||||
</article>
|
||||
</div>
|
||||
<!-- __CINERA_SCRIPT__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<h3 id="directsound-is-object-oriented">DirectSound is Object Oriented</h3>
|
||||
<p>DirectSound in an object-oriented API. What does that mean? Casey starts this episode with a discussion of "method"
|
||||
dispatch through vtables, and why c++ virtual calls are costly.</p>
|
||||
<h3 id="reviewing-directsound-inititalization">Reviewing DirectSound Inititalization</h3>
|
||||
<p>The basic process for initializing DirectSound is as follows:</p>
|
||||
<ol>
|
||||
<li>Load the Library - <a href="http://msdn.microsoft.com/en-us/library/ms684175.aspx"><code>LoadLibrary</code></a><code>("dsound.dll")</code></li>
|
||||
<li>Create a DirectSound object - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.reference.directsoundcreate8.aspx"><code>DirectSoundCreate()</code></a></li>
|
||||
<li>Set the Cooperative Level - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsound8.idirectsound8.setcooperativelevel.aspx"><code>IDirectSound8::SetCooperativeLevel()</code></a></li>
|
||||
<li>"Create" a primary buffer - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsound8.idirectsound8.createsoundbuffer.aspx"><code>IDirectSound8::CreateSoundBuffer()</code></a></li>
|
||||
<li>Create a secondary buffer</li>
|
||||
<li>Tell DirectSound to start playing the secondary buffer - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsoundbuffer8.idirectsoundbuffer8.play.aspx"><code>IDirectSoundBuffer8::Play()</code></a></li>
|
||||
</ol>
|
||||
<h3 id="playing-sounds">Playing sounds</h3>
|
||||
<p>Audio is a complicated topic, and we start with a discussion of audio "waveforms" and how PCM audio data is encoded in
|
||||
memory.</p>
|
||||
<ul>
|
||||
<li>A square wave oscillates between "full-positive" to "full-negative" every half period</li>
|
||||
<li>A Stereo (2-channel) 16-bit PCM audio buffer is arranged as an array of signed int16 values in (left channel value,
|
||||
right channel value) pairs</li>
|
||||
<li>A "sample" sometimes refers to the values for all channels in a sampling period, and sometimes a value for a single
|
||||
channel. Be careful.</li>
|
||||
</ul>
|
||||
<p>The procedure for writing sound data into a buffer is as follows</p>
|
||||
<ol>
|
||||
<li>Figure out where in the buffer you want to start writing, and how much data you want to write
|
||||
<ul>
|
||||
<li>Its useful to look at the play cursor - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsoundbuffer8.idirectsoundbuffer8.getcurrentposition.aspx"><code>IDirectSoundBuffer8::GetCurrentPosition()</code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Acquire a lock on the buffer - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsoundbuffer8.idirectsoundbuffer8.unlock.aspx"><code>IDirectSoundBuffer8::Lock()</code></a>
|
||||
<ul>
|
||||
<li>Because we are working with a circular buffer, this call will return 1 or 2 writable regions</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Write the samples to the buffer</li>
|
||||
<li>Unlock the regions - <a href="http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.idirectsoundbuffer8.idirectsoundbuffer8.unlock.aspx"><code>IDirectSoundBuffer8::Unlock()</code></a></li>
|
||||
</ol>
|
||||
<h3 id="a-note-on-audio-latency">A note on audio latency</h3>
|
||||
<p>Audio latency is determined not by the size of the buffer, but by how far ahead of the PlayCursor you write. The optimal
|
||||
amount of latency is the amount that will cause this frame's audio to coincide with the display of this frame's image.
|
||||
On most platforms, it is very difficult to ascertain the proper amount of latency. It's an unsolved problem, and games
|
||||
with need precise AV sync (like Guitar Hero) go to some lengths to achieve it.</p>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,67 +3,67 @@
|
|||
<!-- __CINERA_INCLUDES__ -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="cinera">
|
||||
<div>
|
||||
<!-- __CINERA_MENUS__ -->
|
||||
<!-- __CINERA_PLAYER__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<h3 id="the-parable-of-the-game-jam">The parable of the Game Jam</h3>
|
||||
<p>Don't let this happen to you, kids. You need good audio hardware to debug audio code.</p>
|
||||
<h2 id="debugging-the-audio-code">Debugging the audio code</h2>
|
||||
<h3 id="square-vs-sine-waves">Square vs Sine Waves</h3>
|
||||
<p>Because square waves are already pretty harsh, they prevent our ability to diagnose some audio bugs. A Sine wave is a
|
||||
"purer" tone, and will enhance our ear's ability to pick up on weirdness. The <code>sin</code> function, however, is defined to
|
||||
return a value between -1 and 1, so we need to talk how to represent fractional numbers on a computer.</p>
|
||||
<h4 id="fixed-point-arithmetic">Fixed-point arithmetic</h4>
|
||||
<p>Fixed point is just integer math. We define some number of bits at the low end of our integer to represent the
|
||||
fractional part of the number, and the remaining bits represent the whole part. Normal addition, subtraction,
|
||||
multiplication, and division work fine, although we need to be aware of the rounding characteristics of fixed-point
|
||||
when doing any numeric computation.</p>
|
||||
<p>Fixed-point math was used more widely before computers commonly had floating point hardware. Today every computer, GPU,
|
||||
and phone has very strong floating point capabilities, and so it is the defacto way to do numerics on a modern computer.</p>
|
||||
<h4 id="floating-point-representation">Floating-point representation</h4>
|
||||
<p>Floating-point is a more complicated (although very rigorously defined) way to represent fractional values. It
|
||||
approaches the problem by dividing the available bits into:</p>
|
||||
<ul>
|
||||
<li>Sign bit</li>
|
||||
<li>Exponent</li>
|
||||
<li>Mantissa</li>
|
||||
</ul>
|
||||
<p>Such that the value represented is given by (sign)(mantissa * 2^exponent). This allows us to preserve a consistent
|
||||
number of bits of precision (like "significant figures" from your physics class), given by the size of the mantissa,
|
||||
regardless of the scale of our numbers, given by the exponent. This means that values representable by floating point
|
||||
will be more densely packed near zero, and more sparse near the limits.</p>
|
||||
<p>Floating Point values come in a few different precisions: <code>float</code> (single-precision, 32-bit), <code>double</code>
|
||||
(double-precision, 64-bit), and <code>long double</code> (128-bit). We will rely on single-precision <code>float</code>s almost exclusively,
|
||||
because they are good enough, and often we can operate on them twice as quickly as <code>double</code>s.</p>
|
||||
<h3 id="generating-a-sine-wave-test-tone">Generating a sine-wave test tone</h3>
|
||||
<p>For the test code, we use the c standard <code>sinf</code> function. It's defined in <code>math.h</code>. Its defined to accept a float
|
||||
"angle" and return a float in the range [-1.0f, 1.0f]. The angle is a function of:</p>
|
||||
<ul>
|
||||
<li>How many samples we have written in total. Call it <code>RunningSampleIndex</code>.</li>
|
||||
<li>The sampling frequency</li>
|
||||
<li>The frequency of the tone we want to play (200-500 Hz is a good range for testing).</li>
|
||||
</ul>
|
||||
<p>When we set the tone frequency, we calculate its period in samples, and call it <code>WavePeriod</code>.</p>
|
||||
<p>The "angle" is then given by <code>2.0f*PI*((float)RunningSampleIndex / (float)WavePeriod)</code>.</p>
|
||||
<p>The <code>SampleValue</code> is given by the <code>sinf(angle) * Volume</code>.</p>
|
||||
<h3 id="smoothing-the-waveform-on-frequency-change">Smoothing the waveform on frequency change</h3>
|
||||
<p>When you change the frequency with the current code, you'll end up with an artifact. To combat this, you need to track
|
||||
an additional value in your synth, basically your progress through the period of the wave, here called <code>tSine</code>.
|
||||
Incrementally accumulate it per sample written:</p>
|
||||
<pre><code>tSine += 2.0f*Pi32*1.0f/(float)WavePeriod // tSine = 2*Pi*how many "WavePeriods" we've played since we started
|
||||
</code></pre><p>Then just use it as the angle for the <code>SampleValue</code> calculation.</p>
|
||||
<pre><code>SampleValue = sinf(tSine) * ToneVolume;
|
||||
</code></pre><h2 id="additional-fixes">Additional Fixes</h2>
|
||||
<ul>
|
||||
<li>There is yet another XInput version that may be the only one available on some Windows 8 installs. <code>xinput9_1_0.dll</code>.
|
||||
Add it to the chain when loading libs.</li>
|
||||
<li>Bitshifting to divide will give you unexpected results for negative numbers. Use actual divide instead.</li>
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
<!-- __CINERA_SCRIPT__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<h3 id="the-parable-of-the-game-jam">The parable of the Game Jam</h3>
|
||||
<p>Don't let this happen to you, kids. You need good audio hardware to debug audio code.</p>
|
||||
<h2 id="debugging-the-audio-code">Debugging the audio code</h2>
|
||||
<h3 id="square-vs-sine-waves">Square vs Sine Waves</h3>
|
||||
<p>Because square waves are already pretty harsh, they prevent our ability to diagnose some audio bugs. A Sine wave is a
|
||||
"purer" tone, and will enhance our ear's ability to pick up on weirdness. The <code>sin</code> function, however, is defined to
|
||||
return a value between -1 and 1, so we need to talk how to represent fractional numbers on a computer.</p>
|
||||
<h4 id="fixed-point-arithmetic">Fixed-point arithmetic</h4>
|
||||
<p>Fixed point is just integer math. We define some number of bits at the low end of our integer to represent the
|
||||
fractional part of the number, and the remaining bits represent the whole part. Normal addition, subtraction,
|
||||
multiplication, and division work fine, although we need to be aware of the rounding characteristics of fixed-point
|
||||
when doing any numeric computation.</p>
|
||||
<p>Fixed-point math was used more widely before computers commonly had floating point hardware. Today every computer, GPU,
|
||||
and phone has very strong floating point capabilities, and so it is the defacto way to do numerics on a modern computer.</p>
|
||||
<h4 id="floating-point-representation">Floating-point representation</h4>
|
||||
<p>Floating-point is a more complicated (although very rigorously defined) way to represent fractional values. It
|
||||
approaches the problem by dividing the available bits into:</p>
|
||||
<ul>
|
||||
<li>Sign bit</li>
|
||||
<li>Exponent</li>
|
||||
<li>Mantissa</li>
|
||||
</ul>
|
||||
<p>Such that the value represented is given by (sign)(mantissa * 2^exponent). This allows us to preserve a consistent
|
||||
number of bits of precision (like "significant figures" from your physics class), given by the size of the mantissa,
|
||||
regardless of the scale of our numbers, given by the exponent. This means that values representable by floating point
|
||||
will be more densely packed near zero, and more sparse near the limits.</p>
|
||||
<p>Floating Point values come in a few different precisions: <code>float</code> (single-precision, 32-bit), <code>double</code>
|
||||
(double-precision, 64-bit), and <code>long double</code> (128-bit). We will rely on single-precision <code>float</code>s almost exclusively,
|
||||
because they are good enough, and often we can operate on them twice as quickly as <code>double</code>s.</p>
|
||||
<h3 id="generating-a-sine-wave-test-tone">Generating a sine-wave test tone</h3>
|
||||
<p>For the test code, we use the c standard <code>sinf</code> function. It's defined in <code>math.h</code>. Its defined to accept a float
|
||||
"angle" and return a float in the range [-1.0f, 1.0f]. The angle is a function of:</p>
|
||||
<ul>
|
||||
<li>How many samples we have written in total. Call it <code>RunningSampleIndex</code>.</li>
|
||||
<li>The sampling frequency</li>
|
||||
<li>The frequency of the tone we want to play (200-500 Hz is a good range for testing).</li>
|
||||
</ul>
|
||||
<p>When we set the tone frequency, we calculate its period in samples, and call it <code>WavePeriod</code>.</p>
|
||||
<p>The "angle" is then given by <code>2.0f*PI*((float)RunningSampleIndex / (float)WavePeriod)</code>.</p>
|
||||
<p>The <code>SampleValue</code> is given by the <code>sinf(angle) * Volume</code>.</p>
|
||||
<h3 id="smoothing-the-waveform-on-frequency-change">Smoothing the waveform on frequency change</h3>
|
||||
<p>When you change the frequency with the current code, you'll end up with an artifact. To combat this, you need to track
|
||||
an additional value in your synth, basically your progress through the period of the wave, here called <code>tSine</code>.
|
||||
Incrementally accumulate it per sample written:</p>
|
||||
<pre><code>tSine += 2.0f*Pi32*1.0f/(float)WavePeriod // tSine = 2*Pi*how many "WavePeriods" we've played since we started
|
||||
</code></pre><p>Then just use it as the angle for the <code>SampleValue</code> calculation.</p>
|
||||
<pre><code>SampleValue = sinf(tSine) * ToneVolume;
|
||||
</code></pre><h2 id="additional-fixes">Additional Fixes</h2>
|
||||
<ul>
|
||||
<li>There is yet another XInput version that may be the only one available on some Windows 8 installs. <code>xinput9_1_0.dll</code>.
|
||||
Add it to the chain when loading libs.</li>
|
||||
<li>Bitshifting to divide will give you unexpected results for negative numbers. Use actual divide instead.</li>
|
||||
</ul>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,65 +3,65 @@
|
|||
<!-- __CINERA_INCLUDES__ -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="cinera">
|
||||
<div>
|
||||
<!-- __CINERA_MENUS__ -->
|
||||
<!-- __CINERA_PLAYER__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p>Today we look at some techniques to get basic timing information from your running game. Timing, like everything, is
|
||||
more complicated than it first appears.</p>
|
||||
<p>A Couple of ideas of time:</p>
|
||||
<ul>
|
||||
<li>Wall clock time - time as it passes in the real world. Measured in seconds.</li>
|
||||
<li>Processor time - how many cycles? this is related to wall clock time by processor frequency, but for a long time now
|
||||
frequency varies a lot and quickly.</li>
|
||||
</ul>
|
||||
<h2 id="wall-clock-time">Wall Clock Time</h2>
|
||||
<p>The Windows platform attempts to provide us with some tools for high precision timing, but as it is a complicated topic,
|
||||
there are some gotchas.</p>
|
||||
<p><a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms644905.aspx"><code>QueryPerformanceFrequency()</code></a> returns a
|
||||
LARGE_INTEGER number of counts/sec. It's guaranteed to be stable, so you can get away with just calling it once at
|
||||
startup. <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms644904.aspx"><code>QueryPerformanceCounter()</code></a> returns
|
||||
a LARGE_INTEGER number of counts.</p>
|
||||
<p>So, dividing counter/freq will give you a number of seconds since some unknown time in the past. More useful would be
|
||||
(counter - last_counter)/freq. This will allow us to get an elapsed time since some known point in the past. However,
|
||||
almost anything we want to time should be less than a second, and since this is an integer divide, anything between 1
|
||||
and 0 seconds will return 0. Not super useful. So, we instead multiply the elapsed counts by 1000 to get our formula
|
||||
to get to elapsed milliseconds.</p>
|
||||
<pre><code>elapsedMs = (1000*(counter - last_counter)) / freq
|
||||
</code></pre><p>To get instantaneous frames per second, we can just divide without changing to milliseconds:</p>
|
||||
<pre><code>fps = freq / (counter - last_counter)
|
||||
</code></pre><p>Important ideas:</p>
|
||||
<ul>
|
||||
<li>To time a frame, only query the timer once per frame, otherwise your timer will leave out time between last frame's
|
||||
end and this frame's start.</li>
|
||||
</ul>
|
||||
<h2 id="processor-time">Processor Time</h2>
|
||||
<p>Every x86 family proccessor has a <a href="http://en.wikipedia.org/wiki/Time_Stamp_Counter">Timestamp Counter (TSC)</a>, which
|
||||
increments with every clock cycle since it was reset. RDTSC is a processor intruction that reads the TSC into general
|
||||
purpose registers.</p>
|
||||
<p>For processors before Sandy Bridge but after dynamic clocking, RDTSC gave us actual clocks, but it was difficult to
|
||||
correlate to wall time because of the variable frequency. Since Sandy Bridge, they give us "nominal" clocks, which
|
||||
is to say the number of clocks elapsed at the chip's nominal frequency. These should correlate closely to wall clock
|
||||
time, but make tracking the "number of cycles" notion of processor time more difficult.</p>
|
||||
<p>RDTSC is usually exposed in a compiler intrinsic. Check the docs for your compiler.</p>
|
||||
<p>Resources:</p>
|
||||
<ul>
|
||||
<li><a href="http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html">Intel Architecture Manuals</a></li>
|
||||
</ul>
|
||||
<h2 id="other-topics">Other topics</h2>
|
||||
<p>Casey had to cover a couple of new corners of C in order to work with the techniques above.</p>
|
||||
<h3 id="union-types">Union types</h3>
|
||||
<p><a href="http://en.wikipedia.org/wiki/Union_type">Union types</a> are a C feature that let you superimpose a number of different
|
||||
layouts over the same chunk of memory. For example LARGE_INTEGER, the return type of the QueryPerf calls. I can treat
|
||||
it as an int64 by accessing its QuadPart, or as two int32s via HighPart and LowPart.</p>
|
||||
<h3 id="compiler-intrinsics">Compiler Intrinsics</h3>
|
||||
<p>An <a href="http://en.wikipedia.org/wiki/Intrinsic_function">intrinsic</a> is a compiler-specific extension that allows direct
|
||||
invocation of some processor instruction. They generally need to be extensions to the compiler so they can avoid all
|
||||
the expensive niceties compilers have to afford functions.</p>
|
||||
</article>
|
||||
</div>
|
||||
<!-- __CINERA_SCRIPT__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p>Today we look at some techniques to get basic timing information from your running game. Timing, like everything, is
|
||||
more complicated than it first appears.</p>
|
||||
<p>A Couple of ideas of time:</p>
|
||||
<ul>
|
||||
<li>Wall clock time - time as it passes in the real world. Measured in seconds.</li>
|
||||
<li>Processor time - how many cycles? this is related to wall clock time by processor frequency, but for a long time now
|
||||
frequency varies a lot and quickly.</li>
|
||||
</ul>
|
||||
<h2 id="wall-clock-time">Wall Clock Time</h2>
|
||||
<p>The Windows platform attempts to provide us with some tools for high precision timing, but as it is a complicated topic,
|
||||
there are some gotchas.</p>
|
||||
<p><a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms644905.aspx"><code>QueryPerformanceFrequency()</code></a> returns a
|
||||
LARGE_INTEGER number of counts/sec. It's guaranteed to be stable, so you can get away with just calling it once at
|
||||
startup. <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms644904.aspx"><code>QueryPerformanceCounter()</code></a> returns
|
||||
a LARGE_INTEGER number of counts.</p>
|
||||
<p>So, dividing counter/freq will give you a number of seconds since some unknown time in the past. More useful would be
|
||||
(counter - last_counter)/freq. This will allow us to get an elapsed time since some known point in the past. However,
|
||||
almost anything we want to time should be less than a second, and since this is an integer divide, anything between 1
|
||||
and 0 seconds will return 0. Not super useful. So, we instead multiply the elapsed counts by 1000 to get our formula
|
||||
to get to elapsed milliseconds.</p>
|
||||
<pre><code>elapsedMs = (1000*(counter - last_counter)) / freq
|
||||
</code></pre><p>To get instantaneous frames per second, we can just divide without changing to milliseconds:</p>
|
||||
<pre><code>fps = freq / (counter - last_counter)
|
||||
</code></pre><p>Important ideas:</p>
|
||||
<ul>
|
||||
<li>To time a frame, only query the timer once per frame, otherwise your timer will leave out time between last frame's
|
||||
end and this frame's start.</li>
|
||||
</ul>
|
||||
<h2 id="processor-time">Processor Time</h2>
|
||||
<p>Every x86 family proccessor has a <a href="http://en.wikipedia.org/wiki/Time_Stamp_Counter">Timestamp Counter (TSC)</a>, which
|
||||
increments with every clock cycle since it was reset. RDTSC is a processor intruction that reads the TSC into general
|
||||
purpose registers.</p>
|
||||
<p>For processors before Sandy Bridge but after dynamic clocking, RDTSC gave us actual clocks, but it was difficult to
|
||||
correlate to wall time because of the variable frequency. Since Sandy Bridge, they give us "nominal" clocks, which
|
||||
is to say the number of clocks elapsed at the chip's nominal frequency. These should correlate closely to wall clock
|
||||
time, but make tracking the "number of cycles" notion of processor time more difficult.</p>
|
||||
<p>RDTSC is usually exposed in a compiler intrinsic. Check the docs for your compiler.</p>
|
||||
<p>Resources:</p>
|
||||
<ul>
|
||||
<li><a href="http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html">Intel Architecture Manuals</a></li>
|
||||
</ul>
|
||||
<h2 id="other-topics">Other topics</h2>
|
||||
<p>Casey had to cover a couple of new corners of C in order to work with the techniques above.</p>
|
||||
<h3 id="union-types">Union types</h3>
|
||||
<p><a href="http://en.wikipedia.org/wiki/Union_type">Union types</a> are a C feature that let you superimpose a number of different
|
||||
layouts over the same chunk of memory. For example LARGE_INTEGER, the return type of the QueryPerf calls. I can treat
|
||||
it as an int64 by accessing its QuadPart, or as two int32s via HighPart and LowPart.</p>
|
||||
<h3 id="compiler-intrinsics">Compiler Intrinsics</h3>
|
||||
<p>An <a href="http://en.wikipedia.org/wiki/Intrinsic_function">intrinsic</a> is a compiler-specific extension that allows direct
|
||||
invocation of some processor instruction. They generally need to be extensions to the compiler so they can avoid all
|
||||
the expensive niceties compilers have to afford functions.</p>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,26 +3,26 @@
|
|||
<!-- __CINERA_INCLUDES__ -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="cinera">
|
||||
<div>
|
||||
<!-- __CINERA_MENUS__ -->
|
||||
<!-- __CINERA_PLAYER__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p>Key ideas:</p>
|
||||
<ul>
|
||||
<li>Sound is necessarily temporal. You can drop a frame of video and the user probably won't notice, but if your audio
|
||||
drops out, they probably will notice.</li>
|
||||
<li>Sound bufers are small, and not all platforms will require us to deal with circular buffers. So one option is to do
|
||||
a buffer copy per frame and present the game with a contiguous block of memory. This should be cheap enough.</li>
|
||||
<li>Much like the bitmap, allocate plenty of space for a sound buffer at startup, and reuse it each frame.</li>
|
||||
</ul>
|
||||
<h2 id="c-features">C features</h2>
|
||||
<h3 id="alloca">alloca</h3>
|
||||
<p><code>alloca</code> is a compiler feature that allows for dynamic allocation <em>on the stack</em>. Much was learned and discussed, but
|
||||
it should be noted that the function is deprecated and probably shouldn't be used in shipping code.</p>
|
||||
</article>
|
||||
</div>
|
||||
<!-- __CINERA_SCRIPT__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p>Key ideas:</p>
|
||||
<ul>
|
||||
<li>Sound is necessarily temporal. You can drop a frame of video and the user probably won't notice, but if your audio
|
||||
drops out, they probably will notice.</li>
|
||||
<li>Sound bufers are small, and not all platforms will require us to deal with circular buffers. So one option is to do
|
||||
a buffer copy per frame and present the game with a contiguous block of memory. This should be cheap enough.</li>
|
||||
<li>Much like the bitmap, allocate plenty of space for a sound buffer at startup, and reuse it each frame.</li>
|
||||
</ul>
|
||||
<h2 id="c-features">C features</h2>
|
||||
<h3 id="alloca">alloca</h3>
|
||||
<p><code>alloca</code> is a compiler feature that allows for dynamic allocation <em>on the stack</em>. Much was learned and discussed, but
|
||||
it should be noted that the function is deprecated and probably shouldn't be used in shipping code.</p>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
<!-- __CINERA_INCLUDES__ -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="cinera">
|
||||
<div>
|
||||
<!-- __CINERA_MENUS__ -->
|
||||
<!-- __CINERA_PLAYER__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<h2 id="notes-">Notes:</h2>
|
||||
<p>Continuation on how dll uses memory: <a href="https://hero.handmadedev.org/forum/code-discussion/99-day-21-s-statement-about-msvcrt-is-correct">https://hero.handmadedev.org/forum/code-discussion/99-day-21-s-statement-about-msvcrt-is-correct</a></p>
|
||||
</article>
|
||||
</div>
|
||||
<!-- __CINERA_SCRIPT__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<h2 id="notes-">Notes:</h2>
|
||||
<p>Continuation on how dll uses memory: <a href="https://hero.handmadedev.org/forum/code-discussion/99-day-21-s-statement-about-msvcrt-is-correct">https://hero.handmadedev.org/forum/code-discussion/99-day-21-s-statement-about-msvcrt-is-correct</a></p>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,34 +3,34 @@
|
|||
<!-- __CINERA_INCLUDES__ -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="cinera">
|
||||
<div>
|
||||
<!-- __CINERA_MENUS__ -->
|
||||
<!-- __CINERA_PLAYER__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p>Masking the write:</p>
|
||||
<p>In SIMD, doing operations "4-wide" means that one wide (packed) operation operates on four pixels. So there's no
|
||||
difference between doing an operation on one pixel or two or three or four, except when it comes to reading and
|
||||
writing.</p>
|
||||
<p>The way we can make sure we only write the pixels we're actually operating on meaningfully is by masking out the ones we
|
||||
aren't. Instead of doing a conditional check every loop, we want to build a mask that's filled with 1s in the places
|
||||
where we'll keep the pixels, and 0s in the places where we'll throw out the pixels.
|
||||
If we're operating on four pixels at once and we're hanging 2 off the edge, the mask might look like:</p>
|
||||
<p>[0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF]</p>
|
||||
<p>By doing a bitwise AND with the pixel data we generate, we can mask out the values that are invalid, since the zeroes in
|
||||
the mask will knock out any bits set in our data. Likewise, the 1s will ensure any values we want to keep will remain in
|
||||
place.</p>
|
||||
<p>We still need to preserve the destination how it was, and the easiest way to do that is to remember what the destination
|
||||
looked like before, and use those values wherever we knocked out values in our data. So we generate an inverted mask
|
||||
that might look something like:</p>
|
||||
<p>[0xFFFFFFFF,0xFFFFFFFF,0x00000000,0x00000000]</p>
|
||||
<p>Using the same AND technique, we can grab out the destination values that should remain unchanged. Then, we can combine
|
||||
that with the set of valid pixel values we generated using the other mask using a bitwise OR. Since the places where the
|
||||
two sets of values overlap are set to 0s in one of them, the data will effectively just be copied from one onto the
|
||||
other with no interference.</p>
|
||||
</article>
|
||||
</div>
|
||||
<!-- __CINERA_SCRIPT__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p>Masking the write:</p>
|
||||
<p>In SIMD, doing operations "4-wide" means that one wide (packed) operation operates on four pixels. So there's no
|
||||
difference between doing an operation on one pixel or two or three or four, except when it comes to reading and
|
||||
writing.</p>
|
||||
<p>The way we can make sure we only write the pixels we're actually operating on meaningfully is by masking out the ones we
|
||||
aren't. Instead of doing a conditional check every loop, we want to build a mask that's filled with 1s in the places
|
||||
where we'll keep the pixels, and 0s in the places where we'll throw out the pixels.
|
||||
If we're operating on four pixels at once and we're hanging 2 off the edge, the mask might look like:</p>
|
||||
<p>[0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF]</p>
|
||||
<p>By doing a bitwise AND with the pixel data we generate, we can mask out the values that are invalid, since the zeroes in
|
||||
the mask will knock out any bits set in our data. Likewise, the 1s will ensure any values we want to keep will remain in
|
||||
place.</p>
|
||||
<p>We still need to preserve the destination how it was, and the easiest way to do that is to remember what the destination
|
||||
looked like before, and use those values wherever we knocked out values in our data. So we generate an inverted mask
|
||||
that might look something like:</p>
|
||||
<p>[0xFFFFFFFF,0xFFFFFFFF,0x00000000,0x00000000]</p>
|
||||
<p>Using the same AND technique, we can grab out the destination values that should remain unchanged. Then, we can combine
|
||||
that with the set of valid pixel values we generated using the other mask using a bitwise OR. Since the places where the
|
||||
two sets of values overlap are set to 0s in one of them, the data will effectively just be copied from one onto the
|
||||
other with no interference.</p>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,26 +3,26 @@
|
|||
<!-- __CINERA_INCLUDES__ -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="cinera">
|
||||
<div>
|
||||
<!-- __CINERA_MENUS__ -->
|
||||
<!-- __CINERA_PLAYER__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p>Semaphores:</p>
|
||||
<p>A semaphore is essentially a number that the operating system keeps track of, that can be incremented and decremented.
|
||||
When you wait for a semaphore, you're essentially telling the OS to let you know when the semaphore number becomes
|
||||
greater than zero. Once it does, then the Wait() call will return and the thread can do something. Calling
|
||||
ReleaseSemaphore(), maybe a little counterintuitively, increments the semaphore, allowing any threads waiting on it to
|
||||
continue working. (Thus, it <em>releases</em> those threads to do work). It doesn't actually change the state of the semaphore
|
||||
other than making the number go up. The semaphore number goes down when a thread has successfully Wait()ed for the
|
||||
semaphore. In cases like the one demonstrated on stream, this usually means the semaphore number will go up/down really
|
||||
fast and stick close to 0 as most of the time the threads are waiting for the semaphore to increment.</p>
|
||||
<p>What this allows you to do is tell several threads at once that some work is ready without having to signal to each one
|
||||
individually. As long as each one is waiting on the same semaphore object, they'll all know when there's more work to be
|
||||
done.</p>
|
||||
</article>
|
||||
</div>
|
||||
<!-- __CINERA_SCRIPT__ -->
|
||||
|
||||
<article id="video-notes">
|
||||
<h1><!-- __CINERA_TITLE__ --></h1>
|
||||
<p>Semaphores:</p>
|
||||
<p>A semaphore is essentially a number that the operating system keeps track of, that can be incremented and decremented.
|
||||
When you wait for a semaphore, you're essentially telling the OS to let you know when the semaphore number becomes
|
||||
greater than zero. Once it does, then the Wait() call will return and the thread can do something. Calling
|
||||
ReleaseSemaphore(), maybe a little counterintuitively, increments the semaphore, allowing any threads waiting on it to
|
||||
continue working. (Thus, it <em>releases</em> those threads to do work). It doesn't actually change the state of the semaphore
|
||||
other than making the number go up. The semaphore number goes down when a thread has successfully Wait()ed for the
|
||||
semaphore. In cases like the one demonstrated on stream, this usually means the semaphore number will go up/down really
|
||||
fast and stick close to 0 as most of the time the threads are waiting for the semaphore to increment.</p>
|
||||
<p>What this allows you to do is tell several threads at once that some work is ready without having to signal to each one
|
||||
individually. As long as each one is waiting on the same semaphore object, they'll all know when there's more work to be
|
||||
done.</p>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
#cineraIndex.riscy dt,
|
||||
|
||||
.cineraMenus.riscy,
|
||||
.cineraMenus.riscy > .menu .refs,
|
||||
.cineraMenus.riscy > .menu .filter_container,
|
||||
.cineraMenus.riscy > .menu > .refs,
|
||||
.cineraMenus.riscy > .menu > .filter_container,
|
||||
.cineraMenus.riscy > .menu > .refs .ref,
|
||||
.cineraMenus.riscy > .menu > .filter_container .filter_mode,
|
||||
.cineraMenus.riscy > .menu > .credits_container,
|
||||
.cineraMenus.riscy > .menu > .credits_container .credit,
|
||||
.cineraMenus.riscy > .menu > .view,
|
||||
.cineraMenus.riscy > .menu > .views_container .view,
|
||||
.cineraPlayerContainer .markers_container.riscy,
|
||||
.cineraPlayerContainer .markers_container.riscy > .marker {
|
||||
background-color: #EEE;
|
||||
|
@ -27,12 +29,13 @@
|
|||
#cineraResults .markerList.riscy > .marker,
|
||||
|
||||
.cineraMenus.riscy,
|
||||
.cineraMenus.riscy > .menu .refs,
|
||||
.cineraMenus.riscy > .menu .filter_container,
|
||||
.cineraMenus.riscy > .menu > .refs,
|
||||
.cineraMenus.riscy > .menu > .filter_container,
|
||||
.cineraMenus.riscy > .menu > .refs .ref,
|
||||
.cineraMenus.riscy > .menu > .filter_container .filter_mode,
|
||||
.cineraMenus.riscy > .menu > .credits_container,
|
||||
.cineraMenus.riscy > .menu > .credits_container .credit,
|
||||
.cineraMenus.riscy > .menu > .views_container,
|
||||
.cineraPlayerContainer .markers_container.riscy,
|
||||
.cineraPlayerContainer .markers_container.riscy > .marker {
|
||||
border-color: rgba(246, 178, 26, 0.8);
|
||||
|
@ -85,6 +88,8 @@
|
|||
.cineraMenus.riscy > .menu > .filter_container .filter_content.focused,
|
||||
/*.cineraMenus.riscy > .menu > .credits_container .credit *:hover,*/
|
||||
.cineraMenus.riscy > .menu > .credits_container .credit *.focused,
|
||||
.cineraMenus.riscy > .menu > .view:hover,
|
||||
.cineraMenus.riscy > .menu > .views_container .view:hover,
|
||||
.cineraPlayerContainer .markers_container.riscy > .marker:hover > .cineraContent {
|
||||
background-color: #FFF8E7;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue