OBS-D3D11渲染梳理
+ -

着色器参数-常量空间

2024-07-31 24 0

这里的常量空间指的是顶点着色器或者像素着色器的全局参数。它们在GPU端以常量空间形式存在。
GPU常量空间是一片内存,包括所有参数的空间。故常量空间一般只创建一个。而在常量空间内部,各个参数根据其偏移位置对号入座。

为了便于处理,OBS关于这个常量参数空间有三种类型的存在:

  • 第一种:就是解析参数即gs_effect parameter
  • 第二种:就是图形着色器参数gs_shader_param
  • 第三种:就是常量参数空间ID3D11Buffer
    它们三者的转换关系如下:
    (gs_effect_set_vec4)-> gs_effect parameter->(gs_shader_set_val) ->gs_shader_param ->(gs_shader::UploadParams)->ID3D11Buffer

根据gs_effect parameter创建ID3D11Buffer

void gs_shader::BuildConstantBuffer()
{
//计数常量空间的总大小
    for (size_t i = 0; i < params.size(); i++) {
        gs_shader_param &param = params[i];
        size_t size = 0;

        switch (param.type) {
        case GS_SHADER_PARAM_BOOL:
        case GS_SHADER_PARAM_INT:
        case GS_SHADER_PARAM_FLOAT:
            size = sizeof(float);
            break;
        case GS_SHADER_PARAM_INT2:
        case GS_SHADER_PARAM_VEC2:
            size = sizeof(vec2);
            break;
        case GS_SHADER_PARAM_INT3:
        case GS_SHADER_PARAM_VEC3:
            size = sizeof(float) * 3;
            break;
        case GS_SHADER_PARAM_INT4:
        case GS_SHADER_PARAM_VEC4:
            size = sizeof(vec4);
            break;
        case GS_SHADER_PARAM_MATRIX4X4:
            size = sizeof(float) * 4 * 4;
            break;
        case GS_SHADER_PARAM_TEXTURE:
        case GS_SHADER_PARAM_STRING:
        case GS_SHADER_PARAM_UNKNOWN:
            continue;
        }

        //数组个数
        if (param.arrayCount)
            size *= param.arrayCount;

        /* checks to see if this constant needs to start at a new register 
        //常量总大小没有16字节对齐时
        if (size && (constantSize & 15) != 0) 
        {
            size_t alignMax = (constantSize + 15) & ~15;  //常量总大小16字节对齐后

            //如果超过了,最最大值
            if ((size + constantSize) > alignMax)
                constantSize = alignMax;
        }

        param.pos = constantSize;//参数在常量空间的位置,都是16字节对齐的
        constantSize += size;    //总的常量空间大小
    }

    memset(&bd, 0, sizeof(bd));

    if (constantSize) 
    {
        HRESULT hr;
        bd.ByteWidth = (constantSize + 15) & 0xFFFFFFF0; /* align */
        bd.Usage = D3D11_USAGE_DYNAMIC;
        bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
        bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

        //创建常量空间
        hr = device->device->CreateBuffer(&bd, NULL, constants.Assign());
        if (FAILED(hr))
            throw HRError("Failed to create constant buffer", hr);
    }

    for (size_t i = 0; i < params.size(); i++)
        gs_shader_set_default(&params[i]); 
}

在解析后创建的着色器有两种结构,分别为解析后的空间和着色器空间。实些其实都在CPU内部,最终由于着色器常量空间是一片连续的内存,故使用gs_shader::UpdateParam构造一块相同大小的内存空间,使用着色器参数填充其空间内容,然后再整体拷贝到着色器内存空间。

void gs_shader::UploadParams()
{
    vector<uint8_t> constData;
    bool upload = false;

//设置最大内存capacity=constantSize size=0
    constData.reserve(constantSize);

    for (size_t i = 0; i < params.size(); i++)
        UpdateParam(constData, params[i], upload);

    if (constData.size() != constantSize)
        throw "Invalid constant data size given to shader";

    if (upload) {
        D3D11_MAPPED_SUBRESOURCE map;
        HRESULT hr;

        hr = device->context->Map(constants, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
        if (FAILED(hr))
            throw HRError("Could not lock constant buffer", hr);

        memcpy(map.pData, constData.data(), constData.size());
        device->context->Unmap(constants, 0);
    }
}

UpdateParam则是根据每个参数的位置和大小,设置各个参数的对应空间。
对于纹理类型有特殊的操作,其它的只是传递参数:

enum gs_shader_param_type {
    GS_SHADER_PARAM_UNKNOWN,
    GS_SHADER_PARAM_BOOL,
    GS_SHADER_PARAM_FLOAT,
    GS_SHADER_PARAM_INT,
    GS_SHADER_PARAM_STRING,
    GS_SHADER_PARAM_VEC2,
    GS_SHADER_PARAM_VEC3,
    GS_SHADER_PARAM_VEC4,
    GS_SHADER_PARAM_INT2,
    GS_SHADER_PARAM_INT3,
    GS_SHADER_PARAM_INT4,
    GS_SHADER_PARAM_MATRIX4X4,
    GS_SHADER_PARAM_TEXTURE,
};

内存复制如下:

inline void gs_shader::UpdateParam(vector<uint8_t> &constData, gs_shader_param &param, bool &upload)
{
//非纹理为型,即数据类型
    if (param.type != GS_SHADER_PARAM_TEXTURE)
    {
        if (param.pos > constData.size()) 
        {
            uint8_t zero = 0;
            constData.insert(constData.end(), param.pos - constData.size(), zero);
        }
        constData.insert(constData.end(), param.curValue.begin(),param.curValue.end());
        if (param.changed) 
        {
            upload = true;
            param.changed = false;
        }
    } 
    //纹理类型
    else if (param.curValue.size() == sizeof(struct gs_shader_texture)) 
    {
        struct gs_shader_texture shader_tex;
        memcpy(&shader_tex, param.curValue.data(), sizeof(shader_tex));
        if (shader_tex.srgb)
            device_load_texture_srgb(device, shader_tex.tex, param.textureID);
        else
            device_load_texture(device, shader_tex.tex, param.textureID);

        if (param.nextSampler)
        {
            ID3D11SamplerState *state = param.nextSampler->state;
            device->context->PSSetSamplers(param.textureID, 1, &state);
            param.nextSampler = nullptr;
        }
    }
}

纹理类型通过PSSetShaderResources加载,如default.draw.ps
纹理的渲染其实一般用于渲染后的上屏。如obs_render_main_texture_internal函数中。

    tex = video->render_texture;
    effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
    param = gs_effect_get_param_by_name(effect, "image");
    gs_effect_set_texture(param, tex);

取得output_frames渲染到的video->render_texture,这时作为参数在render_display渲染屏时又作为参数传递的。

  • render_displays
    • render_display
      • RenderMain
        • obs_render_main_texture_src_color_only
          • obs_render_main_texture_internal
            • gs_draw_sprite
            • gs_draw
            • d3d11的 device_draw
            • UploadParams
            • UpdateParam

0 篇笔记 写笔记

作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!