OpenImageIO/oiio — pull request #990 by lgritz (2014-11-13 21:15:48+00:00)
Title: Another texture simd overhaul: texture improves 15%+

Continuation of the SIMD texture rewrite from a few weeks back, now that I know even more about SIMD-ifying code with SSE. Highlights:

* Replace the old "accum_sample_*" methods with "sample_*" which differ primarily by moving the loop over anisotropic samples into the function, which allows (a) some shared computation for all the samples along the aniso region to be shared, and (b) certain computations to be done on groups of 4 aniso samples at once (especially the "st_to_texel" computation).

* Pre-normalize the aniso weights, this leads to far fewer divide ops, and none for the common cases of only 1 or 2 aniso samples.

* Simplify, simd-ify, and mostly hoist out of loops a lot of the logic related to "fill" values (for channels requested but not present in the texture).

* For bicubic interpolation, SIMD-ify the computation of the four bicubic coefficients (4-wide, get it?). Also have functions that compute coefficients for both the bicubic value and the coefficients for the derivatives together instead of in separate functions -- this lets them share some intermediate values, cutting down on the math somewhat.

* A bunch of other miscellaneous ways in which slight rewriting of the SSE math I use leads to faster evaluation, especially in the successive lerping to do the bicubic math. Just as an example, if you have a sequence like this (capitalized variables are SIMD registers, lower case are floats):
A = ...computation...; b = A[1]; C *= b;
The extraction of one value (A[1]) into a float b, then re-SIMDizing it (by multiplying another float4 by b) is wasteful. It's better to do it like this:
float4 B = simd::shuffle<1>(A); C *= B;
Bottom line: Avoid mixing float registers with float4 registers if you can.

So where does this all get us? I have benchmarks like this:

channels baseline after
4-chan 15.4 13.4 (-13%)
3-chan 18.0 13.5 (-25%)
1-chan 17.9 13.4 (-25%)

These are all 4 channel lookups, and the "chans" is the number of channels in the map itself (with missing channels supplied by the "fill" logic). So I'm getting 13% speed gain when retrieving the right number of channels from the file, and 25% (!) gain when retrieving more channels than the file supplies (a common case of asking for RGBA from an RGB file, with "fill" supplying the missing 1.0 value).