[video member=cmuratori stream_platform=twitch stream_username=handmade_hero project=ray title="Optimizing with SSE2 and AVX2" vod_platform=youtube id=dpvrPYdTkPw annotator=Miblo]
[0:02][Recap and set the stage for the day][:speech]
[1:26][:Run the program to show the current picture]
[4:17][Begin to implement the LANE_WIDTH == 4 versions for our various functions / operators[ref
    site=Intel
    page="Intel Intrinsics Guide"
    url=https://software.intel.com/sites/landingpage/IntrinsicsGuide/]][:isa :lighting :optimisation :rendering]
[13:57][Describe the _mm_xor_si128 instruction[ref
    site=Intel
    page="Intel Intrinsics Guide"
    url=https://software.intel.com/sites/landingpage/IntrinsicsGuide/]][:isa :research]
[16:52][Implement a full set of lane width-agnostic operators[ref
    site=Intel
    page="Intel Intrinsics Guide"
    url=https://software.intel.com/sites/landingpage/IntrinsicsGuide/]][:isa :math :optimisation]
[42:33][Fix up CastSampleRays() to convert everything to the correct lane width][:optimisation][quote 608]
[45:35][Introduce LaneV3FromV3() and continue fixing up CastSampleRays()][:optimisation]
[48:45][Implement the various lane_v3 functions / operators[ref
    site=Intel
    page="Intel Intrinsics Guide"
    url=https://software.intel.com/sites/landingpage/IntrinsicsGuide/]][:isa :math :optimisation]
[1:03:14][Implement scalar comparison operators[ref
    site=Intel
    page="Intel Intrinsics Guide"
    url=https://software.intel.com/sites/landingpage/IntrinsicsGuide/]][:isa :math :optimisation]
[1:12:16][Introduce AndNot() using _mm_andnot_si128 for ConditionalAssign() to use[ref
    site=Intel
    page="Intel Intrinsics Guide"
    url=https://software.intel.com/sites/landingpage/IntrinsicsGuide/]][:isa :optimisation]
[1:23:00][Continue to implement our scalar functions][:optimisation]
[1:32:28][Double-check C's specification for comparison operators[ref
    site=cppreference.com
    page="Comparison operators"
    url=http://en.cppreference.com/w/c/language/operator_comparison]][:research]
[1:34:00][Continue to fix up compile errors]
[1:34:52][Implement scalar loading of materials using _mm_setr_ps[ref
    site=Intel
    page="Intel Intrinsics Guide"
    url=https://software.intel.com/sites/landingpage/IntrinsicsGuide/]][:isa :optimisation]
[1:50:15][Continue to fix up compile errors[ref
    site=Intel
    page="Intel Intrinsics Guide"
    url=https://software.intel.com/sites/landingpage/IntrinsicsGuide/]][:isa :optimisation]
[1:59:59][Implement multiple permutations of MaskIsZeroed() and HorizontalAdd()[ref
    site=Intel
    page="Intel Intrinsics Guide"
    url=https://software.intel.com/sites/landingpage/IntrinsicsGuide/]][:isa :optimisation]
[2:06:20][Make RenderTile() pack the sRGB colour inline and initialise everything in scalar][:optimisation]
[2:11:39][Introduce Extract0() for RenderTile() to call][:optimisation]
[2:14:17][Change Materials, Planes and Spheres to be initialiser lists]
[2:18:47][Make the Entropy stuff work properly[ref
    site=MSDN
    page="x64 (amd64) Intrinsics List"
    url=https://msdn.microsoft.com/en-us/library/hh977022.aspx]][:optimisation]
[2:21:42][:Run the program to see totally bogus results]
[2:22:18][Print out the lane width and flip LANE_WIDTH back to 1 so we can get that working again][:optimisation :profiling]
[2:32:07][:Run the program in 1-wide lanes to see that this no longer works]
[2:34:23][Step through CastSampleRays() and inspect its values]
[2:43:07][Make the operator& for lane_v3 zero out the mask if needed]
[2:44:29][:Run the program...]
[2:45:19][Bump the CPUCount back up]
[2:45:27][:Run the program to see what's going on]
[2:46:58][Increase the LANE_WIDTH to 4]
[2:47:19][:Run the program to see a bizarre picture]
[2:48:26][Switch back to the slow mode and step through CastSampleRays() to inspect its values]
[2:54:12][Fix ConditionalAssign() to cast rather than convert]
[2:54:54][Step back through CastSampleRays() to see more expected values]
[2:56:20][:Run our program to see a better image]
[2:58:02][Compare our GatherF32_() functions]
[2:59:52][Step into CastSampleRays() and inspect the material values]
[3:03:16][Scrutinise our operator!= for lane_u32]
[3:04:39][Fix our operator!= for lane_u32 to use _mm_set1_epi32(0xFFFFFFFF) rather than _mm_setzero_si128()][:isa]
[3:05:55][Step in to CastSampleRays() to see that our lane mask is set properly]
[3:06:57][:Run our program to see that we're now only a little bit wrong]
[3:08:08][Read through our scalar code for any obvious mistakes]
[3:18:07][:Run our program on 1 lane, to compare our image with the 4 lane version]
[3:20:51][Rename Scatter to Specular and try to force all Specular values to 1]
[3:23:52][:Run our program to see what that looks like]
[3:25:53][Revert those specular values and investigate whether the PureBounce, RandomBounce and RayDirection are being computed correctly]
[3:28:49][Step in to the lane_v3 Lerp() to see what it produces][:math]
[3:32:42][Check the normalisation of RayDirection]
[3:33:37][Step through RandomBilateral()]
[3:35:01][Step into LaneF32FromU32() and double-check what it is computing]
[3:36:48][Make LaneU32FromU32 cast its incoming u32 to an int when passing it to _mm_set1_epi32][quote 601]
[3:38:43][Step back in to RandomUnilateral() to see possibly more expected results]
[3:40:06][Assert in RandomUnilateral() that Result < 0.6f]
[3:41:06][:Run the game and don't hit that assert, to determine that RandomUnilateral() is not producing the full range of values from 0 to 1]
[3:43:00][Make RandomUnilateral() shift down its terms by 1][quote 602]
[3:44:29][:Run and hit our assertion in RandomUnilateral()]
[3:44:34][Remove that assert and :run the game to see a reasonable result]
[3:46:17][:Run the program at full quality and compare our images][quote 603]
[3:49:06][Step in to CastSampleRays() to see that we do break out properly]
[3:49:20][Cast significantly fewer rays per pixel to determine that we are not over-casting]
[3:52:21][Step in to CastSampleRays() and inspect the :asm]
[3:55:07][Make CastSampleRays() count up the LoopsComputed for us to print out][:profiling]
[4:00:35][:Run our program and inspect its statistics to see a mere 10.61% wasted bounces]
[4:01:40][Q&A][:speech]
[4:02:40][@thecodedragon][You didn't replace &= and |= with the correct operator inside the function]
[4:03:03][@popcorn0x90][Q: Is your beard fake? It grew pretty fast]
[4:03:11][@Kelimion][@cmuratori: Not just day 3, but also 2017-11-19 (for the image filename)]
[4:03:38][@pragmascrypt][Q: 64 samples looked very smooth. Could you compare 64 samples with 4 wide to 64 samples 1 wide?]
[4:03:59][@chrysos42][Q: Due to floating point precision, is there a significant difference between generating a random float by dividing 32 random bits by the max 32 bit integer vs dividing 24 random bits by the max 24 bit integer?][:prng]
[4:05:34][@the_lyribolical_coach_b][Q: You said in a much earlier stream that using operator overloads for SIMD could confuse the compiler, preferring to use macros. Why the change?]
[4:06:53][@pragmascrypt][Q: I was thinking maybe it does more samples than it should with 4 wide, so by comparing 64 samples 1 wide with 64 samples 4 wide maybe it would look different]
[4:07:12][:Run the program on 1 lane and 64 RaysPerPixel and compare the images]
[4:09:23][@groggeh][Q: Nine women can't grow a baby any faster; would smaller packing potentially be better? Is the packing taking too much time? Just spit-balling]
[4:10:24][Enable CastSampleRays() to early-out as often as possible][:optimisation]
[4:14:40][:Run the program to see that it is now twice as fast]
[4:16:00][Consider avoiding gathering for rays that haven't hit][:optimisation]
[4:16:59][Explicitly establish that the LaneMask is not zeroed before setting the Attenuation, Bounces and RayDirection][:optimisation]
[4:17:56][:Run the program to see another speedup]
[4:19:02][Pull out the lane width-specific code to their own .h files, introducing 8-wide versions for everything[ref
    site=Intel
    page="Intel Intrinsics Guide"
    url=https://software.intel.com/sites/landingpage/IntrinsicsGuide/]][:isa :optimisation]
[4:22:17][Check out the _CMP* defines in immintrin.h[ref
    site="Intel Developer Zone"
    page="_mm_cmp_ps, _mm256_cmp_ps"
    url=https://software.intel.com/en-us/node/524077]][quote 604]
[4:27:08][Learn what "ordered" means in the context of these _CMP* defines[ref
    site="Intel Developer Zone"
    page="Compare Intrinsics for Floating Point Vectors"
    url=https://software.intel.com/en-us/node/694431]][:research]
[4:28:22][Continue to implement the 8-wide versions of our functions / operators[ref
    site=Intel
    page="Intel Intrinsics Guide"
    url=https://software.intel.com/sites/landingpage/IntrinsicsGuide/]][:isa :optimisation]
[4:36:07][:Run the program in 8-wide lanes and crash immediately]
[4:38:21][Inspect the :asm for RenderTile() to see that we are failing on the vunpcklps call, and investigate if it is an alignment issue]
[4:42:03][Search the Intel 64 and IA-32 Architectures Software Developer Manuals for vunpcklps[ref
    site="Intel"
    page="Intel 64 and IA-32 Architectures Software Developer Manuals"
    url="https://www-ssl.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html"]][:research]
[4:44:33][Pass -arch:AVX2 on the build line to prevent the vunpcklps call from using bcst[ref
    site=MSDN
    page="x64 (amd64) Intrinsics List"
    url=https://msdn.microsoft.com/en-us/library/jj620901.aspx]]
[4:46:26][:Run our program in 8-wide lanes to see that we are slower, more wasteful and darker]
[4:47:18][Fix our 8-wide HorizontalAdd()][:optimisation]
[4:48:10][:Run our program to see that we are much better, and save off our images and statistics]
[4:52:28][That's about it for today][:speech]
[4:53:20][@butwhynot1][Q: Do AVX512 now]
[4:54:04][That's it][:speech]
[/video]
