# SIMD Optimizations in OpenImageIO TextureSystem for Batch Texture Lookups

> Discover how OpenImageIO's TextureSystem uses SIMD to accelerate batch texture lookups. Boost performance with vectorized calculations for samples, positions, and filter kernels.

- Repository: [Academy Software Foundation/openimageio](https://github.com/academysoftwarefoundation/openimageio)
- Tags: performance
- Published: 2026-02-23

---

**OpenImageIO's TextureSystem leverages portable SIMD abstractions to process four, eight, or sixteen texture samples per instruction, accelerating batch lookups through vectorized position calculations, weighted accumulations, and filter kernel evaluations.**

The `academysoftwarefoundation/openimageio` repository implements these SIMD optimizations within the `TextureSystem` class to handle high-performance texture sampling for production rendering. By utilizing the generic **OIIO_SIMD** abstraction layer, the same source code automatically scales across SSE, AVX, and AVX-512 instruction sets without requiring separate implementations.

## How TextureSystem Implements SIMD Optimizations

### The OIIO_SIMD Portable Abstraction

All SIMD optimizations in OpenImageIO are built upon the portable vector types defined in [`src/include/OpenImageIO/simd.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/include/OpenImageIO/simd.h). This header defines architecture-agnostic types such as `vfloat4`, `vint4`, and `vbool4`, along with the `OIIO_SIMD` macro that determines the vector width at compile time.

The abstraction automatically selects the optimal instruction set:
- **SSE**: 4-wide vectors (`vfloat4`)
- **AVX**: 8-wide vectors (`vfloat8`)
- **AVX-512**: 16-wide vectors (`vfloat16`)

This design allows `TextureSystem` to process multiple texture samples simultaneously without hardware-specific code branches.

### Vectorized Sample Position Calculation

When performing batch lookups, `TextureSystem` calculates sample positions using SIMD vectors to process four offsets simultaneously. In [`src/libtexture/texturesys.cpp`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/libtexture/texturesys.cpp) (lines 2310-2317), the implementation loads successive *s* and *t* offsets into a `vfloat4` vector:

```cpp
vfloat4 pos(samplepos+sample);  // Load four sample positions
vfloat4 ss = s + pos * smajor;  // Compute s coordinates
vfloat4 tt = t + pos * tmajor;  // Compute t coordinates
ss.store(sval+sample);          // Store back to scalar buffers
tt.store(tval+sample);

```

This vectorized approach eliminates the overhead of sequential coordinate calculations, reducing the per-sample cost significantly.

### SIMD-Accelerated Weighted Accumulation

After sampling individual texels, `TextureSystem` accumulates results using SIMD multiplication and addition operations. The weighted accumulation logic in [`src/libtexture/texturesys.cpp`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/libtexture/texturesys.cpp) (lines 2332-2339 and 2393-2396) processes four samples simultaneously:

```cpp
vfloat4 r, drds, drdt;
// ... sampling logic fills r, drds, drdt ...
vfloat4 lw = levelweight[sample];  // Load weights
r_sum += r * lw;                   // Vectorized multiply-accumulate
drds_sum += drds * lw;
drdt_sum += drdt * lw;

```

The final SIMD results are written directly to the caller's buffer using a cast to `simd::vfloat4*`, ensuring efficient memory transfers without scalar extraction overhead.

### Filter Kernel Vectorization

The core sampling kernels—including bilinear, bicubic, and smart-bicubic filters—utilize SIMD intrinsics to process 4×4 texel blocks. In [`src/libtexture/texturesys.cpp`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/libtexture/texturesys.cpp) (lines 2912-2942), the B-spline weight evaluation uses `simd::shuffle` operations and vectorized arithmetic with constant tables defined by `OIIO_SIMD_FLOAT4_CONST4`:

```cpp
// SIMD shuffle and weight calculation for bicubic filtering
vfloat4 wx = simd::shuffle<0>(w);  // Extract x weights
vfloat4 wy = simd::shuffle<1>(w);  // Extract y weights
// Vectorized multiply with texel data

```

These vectorized kernels eliminate the need for per-texel loops, providing substantial speedups for high-quality filtering modes.

## Batch Processing Workflow in Practice

The SIMD optimizations integrate seamlessly into the texture lookup pipeline:

1. **Entry Point**: The caller requests multiple samples via `TextureSystem::texture()` or `TextureSystem::gettexture()`.

2. **Batch Loop**: `TextureSystem::texture_lookup()` processes samples in groups of four (`sample += 4`) when `OIIO_SIMD` is defined.

3. **Vectorized Sampling**: Each iteration calls SIMD-aware `sample_*` functions that load 4×4 texel patches using `vfloat4` loads and apply filters via vector arithmetic.

4. **Result Storage**: Accumulated SIMD vectors are stored directly to the output buffer, maintaining API compatibility with scalar code while delivering up to **4× speedup** on SSE hardware and higher gains on AVX/AVX-512 systems.

The library automatically falls back to scalar loops on machines without SIMD support, ensuring correctness across all platforms.

## Code Example: Performing Batch Texture Lookups

The following example demonstrates how client code interacts with the SIMD-optimized texture system:

```cpp
#include <OpenImageIO/texture.h>
using namespace OIIO;

int main() {
    // Create a shared texture system (default ImageCache)
    std::shared_ptr<TextureSystem> texsys = TextureSystem::create();

    // Request a 3-component color at (u,v) = (0.45,0.62)
    float result[3];
    TextureOpt opt;                     // default options (bilinear)
    bool ok = texsys->texture("myTexture.exr", 0.45f, 0.62f,
                             opt, 3, result);
    if (!ok) {
        std::cerr << texsys->geterror() << "\n";
    } else {
        std::cout << "color = " << result[0] << " "
                  << result[1] << " " << result[2] << "\n";
    }
    TextureSystem::destroy(texsys);
}

```

When processing large batches of texels (e.g., via `get_texels`), the internal loops in [`src/libtexture/texturesys.cpp`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/libtexture/texturesys.cpp) automatically vectorize the operations using the SIMD paths described above.

## Key Source Files for SIMD Texture Lookups

| File | Role | Path |
|------|------|------|
| **texturesys.cpp** | Core implementation of texture lookup with SIMD batch loops, position calculation, and accumulation | [`src/libtexture/texturesys.cpp`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/libtexture/texturesys.cpp) |
| **simd.h** | Portable SIMD abstraction defining `vfloat4`, `vint4`, and `OIIO_SIMD` macros | [`src/include/OpenImageIO/simd.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/include/OpenImageIO/simd.h) |
| **texture.h** | Public API header exposing `TextureSystem` and `TextureOpt` | [`src/libtexture/texture.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/libtexture/texture.h) |
| **environment.cpp** | Environment map lookups using the same `vfloat4` batch logic | [`src/libtexture/environment.cpp`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/libtexture/environment.cpp) |
| **imagecache.cpp** | SIMD-aligned memory allocation using `OIIO_SIMD_MAX_SIZE_BYTES` | [`src/libtexture/imagecache.cpp`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/libtexture/imagecache.cpp) |

## Summary

- **OpenImageIO's TextureSystem** utilizes portable SIMD abstractions to accelerate batch texture lookups across SSE, AVX, and AVX-512 instruction sets.
- The **OIIO_SIMD** layer in [`simd.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/simd.h) provides generic vector types (`vfloat4`, `vfloat8`, `vfloat16`) that automatically scale to the host CPU capabilities.
- **Vectorized position calculations** in [`texturesys.cpp`](https://github.com/academysoftwarefoundation/openimageio/blob/main/texturesys.cpp) process four sample coordinates simultaneously, reducing per-sample overhead.
- **SIMD accumulation** multiplies and sums per-sample results using vector operations, writing final results directly to output buffers.
- **Filter kernels** (bilinear, bicubic) leverage `simd::shuffle` and constant tables to process 4×4 texel blocks in parallel.
- The implementation maintains **scalar fallback** paths for non-SIMD hardware, ensuring correctness while delivering up to 4× speedups on typical SSE systems.

## Frequently Asked Questions

### What SIMD instruction sets does OpenImageIO support?

OpenImageIO supports **SSE4.2**, **AVX**, **AVX2**, and **AVX-512** through its generic SIMD abstraction layer. The build system detects the host CPU capabilities and defines the appropriate `OIIO_SIMD` width (4, 8, or 16), allowing the same `vfloat4`/`vfloat8`/`vfloat16` code to compile across all architectures.

### How does TextureSystem handle non-SIMD fallback?

All SIMD-optimized loops in [`src/libtexture/texturesys.cpp`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/libtexture/texturesys.cpp) are guarded by `#if OIIO_SIMD` preprocessor directives. When compiling for platforms without SIMD support or when explicitly disabled, the code falls back to scalar loops that process one sample at a time. This ensures identical numerical results and API compatibility across all hardware tiers.

### Which texture filtering methods benefit most from SIMD?

**Bicubic** and **smart-bicubic** filtering see the largest gains from SIMD optimizations because they require evaluating 4×4 texel neighborhoods and computing B-spline weights. The vectorized implementation uses `simd::shuffle` operations and constant weight tables (`OIIO_SIMD_FLOAT4_CONST4`) to process these heavy computations in parallel, whereas scalar code would loop through 16 individual texel accesses.

### Can SIMD optimizations be disabled at compile time?

Yes. Developers can disable SIMD optimizations by setting the CMake option `USE_SIMD=0` or by undefining `OIIO_SIMD` during compilation. This forces the texture system to use scalar code paths exclusively, which is useful for debugging, comparing numerical precision, or targeting minimal embedded platforms without vector units.