Swiftshader初探 (一)

2020-08-30  本文已影响0人  弥大生

Swiftshader是Google推出的OpenGL的软件实现.
它的机制和VM差不多, 可以动态地将shader翻译成cpu指令.

下图是它的架构, 主要有三层:

  1. Renderer: 负责将Shader中的操作转化为Reactor的调用.
  2. Reactor: 一种中间语言,主要就是包装了一下LLVM的调用.
  3. LLVM: Swiftshader底层通过LLVM来生成machine code.


    Architecture.png

本文分为三个部分:

Reactor Layer Introduction

Reactor是一种中间语言, 可以嵌入在C++中使用.
它的语法参见Reactor.md

Reactor里定义了很多数据类型:

// src/Reactor/Reactor.hpp
class Bool;
...
class Short8;
class UShort8;
...
class Int4;
class UInt4;
...
class Float4;

可以使用RR_WATCH(variable_name)的形式来print Reactor变量.
前提是在CMakeList.txt中定义ENABLE_RR_PRINT

// src/Reactor/Print.hpp

// RR_WATCH() is a helper that prints the name and value of all the supplied
// arguments.
// For example, if you had the Int and bool variables 'foo' and 'bar' that
// you want to print, you can simply write:
//    RR_WATCH(foo, bar)
// When this JIT compiled code is executed, it will print the string
// "foo: 1, bar: true" to stdout.
//
// RR_WATCH() is intended to be used for debugging JIT compiled code, and
// is not intended for production use.
#   define RR_WATCH(...) RR_LOG(RR_WATCH_FMT(__VA_ARGS__), __VA_ARGS__)

与LLVM的接口主要在src/Reactor/LLVMReactor.cpp

需要注意的是: Renderer Layer里对它的调用都会生成machine code, 但这些machine code并不会立刻执行, 而是作为data存在的. 只有当swiftshader将程序入口指到这些code的时候,它们才会执行.

Renderer Layer Introduciton

它处理图像的时候大致会经历三个步骤:

  1. VertexRoutine
  2. SetupRoutine
  3. PixelRoutine
// src/Render/Render.cpp
    void Renderer::draw(DrawType drawType, unsigned int indexOffset, unsigned int count, bool update)

VertexRoutine: 用于处理顶点变换.

SetupRoutine: 根据顶点建立平面方程. 比如说根据三角形的三点来构建一个三角形的平面方程. Pixel Routine会根据平面方程来分别处理不同的pixel.

PixelRoutine: 计算pixel color, 并写入frame buffer.
PixelRoutine可以细分为三个主要步骤:
(a) interpolate: 将pixel shader的输入参数根据平面方程做插值
(b) apply shader: 执行pixel shader中的算子
(c) write color: 将数据写入frame buffer

PixelRoutine的处理逻辑可以参见:

//  src/Shader/PixelRoutine.cpp
    void PixelRoutine::quad(Pointer<Byte> cBuffer[RENDERTARGETS], Pointer<Byte> &zBuffer, Pointer<Byte> &sBuffer, Int cMask[4], Int &x)
    {

...
        // 这里将1个pixel的x扩展成4个pixel的x
        Float4 xxxx = Float4(Float(x)) + *Pointer<Float4>(primitive + OFFSET(Primitive,xQuad), 16);

...
        If(depthPass || Bool(!earlyDepthTest))
        {
...
            // 这里将1个pixel的y扩展成4个pixel的y
            Float4 yyyy = Float4(Float(y)) + *Pointer<Float4>(primitive + OFFSET(Primitive,yQuad), 16);
...
            for(int interpolant = 0; interpolant < MAX_FRAGMENT_INPUTS; interpolant++)
            {
                for(int component = 0; component < 4; component++)
                {
                    if(state.interpolant[interpolant].component & (1 << component))
                    {
                        if(!state.interpolant[interpolant].centroid)
                        {
                            v[interpolant][component] = interpolate(xxxx, Dv[interpolant][component], rhw, primitive + OFFSET(Primitive, V[interpolant][component]), (state.interpolant[interpolant].flat & (1 << component)) != 0, state.perspective, false);
                        }
                        else
                        {
                            v[interpolant][component] = interpolateCentroid(XXXX, YYYY, rhwCentroid, primitive + OFFSET(Primitive, V[interpolant][component]), (state.interpolant[interpolant].flat & (1 << component)) != 0, state.perspective);
                        }
                    }
                }
...

            if(colorUsed())
            {
...
                applyShader(cMask);
...
            }

            If(alphaPass)
            {
...
                    if(colorUsed())
                    {
...
                        rasterOperation(f, cBuffer, x, sMask, zMask, cMask);
                    }
                }
...
            }
        }
...
    }

xQuad和yQuad会在SetupRoutine里进行初始化:

// src/Shader/SetupRoutine.cpp
            Float4 xQuad = Float4(0, 1, 0, 1) - Float4(dx);
            Float4 yQuad = Float4(0, 0, 1, 1) - Float4(dy);

Swiftshaer使用Vector4f存放4个pixel的rgba.
Float4里面存放了1个pixel的(r, r, r, r) or (g, g, g, g) or (b, b, b, b) or (a, a, a, a). 明白了这一点, code会容易理解很多.

Debug Tips:

  1. Profile switch of PixelRoutine
// src/Main/Config.hpp
#define PERF_PROFILE 0   // Profile various pipeline stages and display the timing in SwiftConfig
  1. Dump llvm's IR
// src/Reactor/LLVMReactor.cpp
        if(false)
        {
            std::error_code error;
            llvm::raw_fd_ostream file(std::string(name) + "-llvm-dump-unopt.txt", error);
            jit->module->print(file, 0);
        }
  1. Print shader
// src/OpenGL/libGLESv2/Shader.cpp
    if(false)
    {
        static int serial = 1;

        if(false)
        {
            char buffer[256];
            sprintf(buffer, "shader-input-%d-%d.txt", getName(), serial);
            FILE *file = fopen(buffer, "wt");
            fprintf(file, "%s", mSource);
            fclose(file);
        }

        getShader()->print("shader-output-%d-%d.txt", getName(), serial);

        serial++;
    }
  1. Settings of Swiftshader
    Swiftshader提供了很多配置, 包括图像质量/线程数量/优化等级等等, 这些都会极大地影响性能
// Swiftshader.ini
...

[Quality]
TextureSampleQuality=2
MipmapQuality=1
PerspectiveCorrection=1
TranscendentalPrecision=2
TransparencyAntialiasing=0

[Processor]
ThreadCount=0
EnableSSE3=1
EnableSSSE3=1
EnableSSE4_1=1

[Optimization]
OptimizationPass1=1
OptimizationPass2=0
OptimizationPass3=0
OptimizationPass4=0
OptimizationPass5=0
OptimizationPass6=0
OptimizationPass7=0
OptimizationPass8=0
OptimizationPass9=0
OptimizationPass10=0
...
上一篇下一篇

猜你喜欢

热点阅读