着色器参数-常量空间
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 ¶m = 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(¶ms[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 ¶m, 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
- obs_render_main_texture_internal
- obs_render_main_texture_src_color_only
- RenderMain
- render_display