This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/gpu/opengl/gl_shader_interface.cc
Clément Foucault 2637f94358 GLShader: Fix buffer overflow caused by workaround uniform
In order to use a workaround builtin uniform, we need to count it
just like other uniforms and give it some space in the name buffer.

This also fixes extensions being added after the uniform declaration.
All `#extension` directives are now part of the gl backend.
2022-01-25 18:48:58 +01:00

531 lines
18 KiB
C++

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2016 by Mike Erwin.
* All rights reserved.
*/
/** \file
* \ingroup gpu
*
* GPU shader interface (C --> GLSL)
*/
#include "BLI_bitmap.h"
#include "gl_backend.hh"
#include "gl_batch.hh"
#include "gl_context.hh"
#include "gl_shader_interface.hh"
#include "GPU_capabilities.h"
namespace blender::gpu {
/* -------------------------------------------------------------------- */
/** \name Binding assignment
*
* To mimic vulkan, we assign binding at shader creation to avoid shader recompilation.
* In the future, we should set it in the shader using layout(binding = i) and query its value.
* \{ */
static inline int block_binding(int32_t program, uint32_t block_index)
{
/* For now just assign a consecutive index. In the future, we should set it in
* the shader using layout(binding = i) and query its value. */
glUniformBlockBinding(program, block_index, block_index);
return block_index;
}
static inline int sampler_binding(int32_t program,
uint32_t uniform_index,
int32_t uniform_location,
int *sampler_len)
{
/* Identify sampler uniforms and assign sampler units to them. */
GLint type;
glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type);
switch (type) {
case GL_SAMPLER_1D:
case GL_SAMPLER_2D:
case GL_SAMPLER_3D:
case GL_SAMPLER_CUBE:
case GL_SAMPLER_CUBE_MAP_ARRAY_ARB: /* OpenGL 4.0 */
case GL_SAMPLER_1D_SHADOW:
case GL_SAMPLER_2D_SHADOW:
case GL_SAMPLER_1D_ARRAY:
case GL_SAMPLER_2D_ARRAY:
case GL_SAMPLER_1D_ARRAY_SHADOW:
case GL_SAMPLER_2D_ARRAY_SHADOW:
case GL_SAMPLER_2D_MULTISAMPLE:
case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_SAMPLER_CUBE_SHADOW:
case GL_SAMPLER_BUFFER:
case GL_INT_SAMPLER_1D:
case GL_INT_SAMPLER_2D:
case GL_INT_SAMPLER_3D:
case GL_INT_SAMPLER_CUBE:
case GL_INT_SAMPLER_1D_ARRAY:
case GL_INT_SAMPLER_2D_ARRAY:
case GL_INT_SAMPLER_2D_MULTISAMPLE:
case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_INT_SAMPLER_BUFFER:
case GL_UNSIGNED_INT_SAMPLER_1D:
case GL_UNSIGNED_INT_SAMPLER_2D:
case GL_UNSIGNED_INT_SAMPLER_3D:
case GL_UNSIGNED_INT_SAMPLER_CUBE:
case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
case GL_UNSIGNED_INT_SAMPLER_BUFFER: {
/* For now just assign a consecutive index. In the future, we should set it in
* the shader using layout(binding = i) and query its value. */
int binding = *sampler_len;
glUniform1i(uniform_location, binding);
(*sampler_len)++;
return binding;
}
default:
return -1;
}
}
static inline int image_binding(int32_t program,
uint32_t uniform_index,
int32_t uniform_location,
int *image_len)
{
/* Identify image uniforms and assign image units to them. */
GLint type;
glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type);
switch (type) {
case GL_IMAGE_1D:
case GL_IMAGE_2D:
case GL_IMAGE_3D: {
/* For now just assign a consecutive index. In the future, we should set it in
* the shader using layout(binding = i) and query its value. */
int binding = *image_len;
glUniform1i(uniform_location, binding);
(*image_len)++;
return binding;
}
default:
return -1;
}
}
static inline int ssbo_binding(int32_t program, uint32_t ssbo_index)
{
GLint binding = -1;
GLenum property = GL_BUFFER_BINDING;
GLint values_written = 0;
glGetProgramResourceiv(
program, GL_SHADER_STORAGE_BLOCK, ssbo_index, 1, &property, 1, &values_written, &binding);
return binding;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Creation / Destruction
* \{ */
GLShaderInterface::GLShaderInterface(GLuint program)
{
/* Necessary to make #glUniform works. */
glUseProgram(program);
GLint max_attr_name_len = 0, attr_len = 0;
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attr_name_len);
glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attr_len);
GLint max_ubo_name_len = 0, ubo_len = 0;
glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_ubo_name_len);
glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &ubo_len);
GLint max_uniform_name_len = 0, active_uniform_len = 0, uniform_len = 0;
glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_len);
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len);
uniform_len = active_uniform_len;
GLint max_ssbo_name_len = 0, ssbo_len = 0;
if (GPU_shader_storage_buffer_objects_support()) {
glGetProgramInterfaceiv(program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &ssbo_len);
glGetProgramInterfaceiv(
program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &max_ssbo_name_len);
}
BLI_assert_msg(ubo_len <= 16, "enabled_ubo_mask_ is uint16_t");
/* Work around driver bug with Intel HD 4600 on Windows 7/8, where
* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */
if (attr_len > 0 && max_attr_name_len == 0) {
max_attr_name_len = 256;
}
if (ubo_len > 0 && max_ubo_name_len == 0) {
max_ubo_name_len = 256;
}
if (uniform_len > 0 && max_uniform_name_len == 0) {
max_uniform_name_len = 256;
}
if (ssbo_len > 0 && max_ssbo_name_len == 0) {
max_ssbo_name_len = 256;
}
/* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before
* allocating the uniform array. */
GLint max_ubo_uni_len = 0;
for (int i = 0; i < ubo_len; i++) {
GLint ubo_uni_len;
glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len);
max_ubo_uni_len = max_ii(max_ubo_uni_len, ubo_uni_len);
uniform_len -= ubo_uni_len;
}
/* Bit set to true if uniform comes from a uniform block. */
BLI_bitmap *uniforms_from_blocks = BLI_BITMAP_NEW(active_uniform_len, __func__);
/* Set uniforms from block for exclusion. */
GLint *ubo_uni_ids = (GLint *)MEM_mallocN(sizeof(GLint) * max_ubo_uni_len, __func__);
for (int i = 0; i < ubo_len; i++) {
GLint ubo_uni_len;
glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len);
glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, ubo_uni_ids);
for (int u = 0; u < ubo_uni_len; u++) {
BLI_BITMAP_ENABLE(uniforms_from_blocks, ubo_uni_ids[u]);
}
}
MEM_freeN(ubo_uni_ids);
int input_tot_len = attr_len + ubo_len + uniform_len + ssbo_len;
inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__);
const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len +
uniform_len * max_uniform_name_len +
ssbo_len * max_ssbo_name_len;
name_buffer_ = (char *)MEM_mallocN(name_buffer_len, "name_buffer");
uint32_t name_buffer_offset = 0;
/* Attributes */
enabled_attr_mask_ = 0;
for (int i = 0; i < attr_len; i++) {
char *name = name_buffer_ + name_buffer_offset;
GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
GLsizei name_len = 0;
GLenum type;
GLint size;
glGetActiveAttrib(program, i, remaining_buffer, &name_len, &size, &type, name);
GLint location = glGetAttribLocation(program, name);
/* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */
if (location == -1) {
continue;
}
ShaderInput *input = &inputs_[attr_len_++];
input->location = input->binding = location;
name_buffer_offset += set_input_name(input, name, name_len);
enabled_attr_mask_ |= (1 << input->location);
}
/* Uniform Blocks */
for (int i = 0; i < ubo_len; i++) {
char *name = name_buffer_ + name_buffer_offset;
GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
GLsizei name_len = 0;
glGetActiveUniformBlockName(program, i, remaining_buffer, &name_len, name);
ShaderInput *input = &inputs_[attr_len_ + ubo_len_++];
input->binding = input->location = block_binding(program, i);
name_buffer_offset += this->set_input_name(input, name, name_len);
enabled_ubo_mask_ |= (1 << input->binding);
}
/* Uniforms & samplers & images */
for (int i = 0, sampler = 0, image = 0; i < active_uniform_len; i++) {
if (BLI_BITMAP_TEST(uniforms_from_blocks, i)) {
continue;
}
char *name = name_buffer_ + name_buffer_offset;
GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
GLsizei name_len = 0;
glGetActiveUniformName(program, i, remaining_buffer, &name_len, name);
ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_++];
input->location = glGetUniformLocation(program, name);
input->binding = sampler_binding(program, i, input->location, &sampler);
name_buffer_offset += this->set_input_name(input, name, name_len);
enabled_tex_mask_ |= (input->binding != -1) ? (1lu << input->binding) : 0lu;
if (input->binding == -1) {
input->binding = image_binding(program, i, input->location, &image);
enabled_ima_mask_ |= (input->binding != -1) ? (1lu << input->binding) : 0lu;
}
}
/* SSBOs */
for (int i = 0; i < ssbo_len; i++) {
char *name = name_buffer_ + name_buffer_offset;
GLsizei remaining_buffer = name_buffer_len - name_buffer_offset;
GLsizei name_len = 0;
glGetProgramResourceName(
program, GL_SHADER_STORAGE_BLOCK, i, remaining_buffer, &name_len, name);
const GLint binding = ssbo_binding(program, i);
ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_++];
input->binding = input->location = binding;
name_buffer_offset += this->set_input_name(input, name, name_len);
}
/* Builtin Uniforms */
for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) {
GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int);
builtins_[u] = glGetUniformLocation(program, builtin_uniform_name(u));
}
/* Builtin Uniforms Blocks */
for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) {
GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int);
const ShaderInput *block = this->ubo_get(builtin_uniform_block_name(u));
builtin_blocks_[u] = (block != nullptr) ? block->binding : -1;
}
MEM_freeN(uniforms_from_blocks);
/* Resize name buffer to save some memory. */
if (name_buffer_offset < name_buffer_len) {
name_buffer_ = (char *)MEM_reallocN(name_buffer_, name_buffer_offset);
}
// this->debug_print();
this->sort_inputs();
}
GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateInfo &info)
{
using namespace blender::gpu::shader;
attr_len_ = info.vertex_inputs_.size();
uniform_len_ = info.push_constants_.size();
ubo_len_ = 0;
ssbo_len_ = 0;
Vector<ShaderCreateInfo::Resource> all_resources;
all_resources.extend(info.pass_resources_);
all_resources.extend(info.batch_resources_);
for (ShaderCreateInfo::Resource &res : all_resources) {
switch (res.bind_type) {
case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER:
ubo_len_++;
break;
case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
ssbo_len_++;
break;
case ShaderCreateInfo::Resource::BindType::SAMPLER:
uniform_len_++;
break;
case ShaderCreateInfo::Resource::BindType::IMAGE:
uniform_len_++;
break;
}
}
size_t workaround_names_size = 0;
Vector<StringRefNull> workaround_uniform_names;
auto check_enabled_uniform = [&](const char *uniform_name) {
if (glGetUniformLocation(program, uniform_name) != -1) {
workaround_uniform_names.append(uniform_name);
workaround_names_size += StringRefNull(uniform_name).size() + 1;
uniform_len_++;
}
};
if (!GLContext::shader_draw_parameters_support) {
check_enabled_uniform("gpu_BaseInstance");
}
BLI_assert_msg(ubo_len_ <= 16, "enabled_ubo_mask_ is uint16_t");
int input_tot_len = attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_;
inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__);
ShaderInput *input = inputs_;
name_buffer_ = (char *)MEM_mallocN(info.interface_names_size_ + workaround_names_size,
"name_buffer");
uint32_t name_buffer_offset = 0;
/* Necessary to make #glUniform works. TODO(fclem) Remove. */
glUseProgram(program);
/* Attributes */
for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) {
copy_input_name(input, attr.name, name_buffer_, name_buffer_offset);
if (true || !GLContext::explicit_location_support) {
input->location = input->binding = glGetAttribLocation(program, attr.name.c_str());
}
else {
input->location = input->binding = attr.index;
}
enabled_attr_mask_ |= (1 << input->location);
input++;
}
/* Uniform Blocks */
for (const ShaderCreateInfo::Resource &res : all_resources) {
if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) {
copy_input_name(input, res.uniformbuf.name, name_buffer_, name_buffer_offset);
if (true || !GLContext::explicit_location_support) {
input->location = glGetUniformBlockIndex(program, name_buffer_ + input->name_offset);
glUniformBlockBinding(program, input->location, res.slot);
}
input->binding = res.slot;
enabled_ubo_mask_ |= (1 << input->binding);
input++;
}
}
/* Uniforms & samplers & images */
for (const ShaderCreateInfo::Resource &res : all_resources) {
if (res.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) {
copy_input_name(input, res.sampler.name, name_buffer_, name_buffer_offset);
/* Until we make use of explicit uniform location or eliminate all
* sampler manually changing. */
if (true || !GLContext::explicit_location_support) {
input->location = glGetUniformLocation(program, res.sampler.name.c_str());
glUniform1i(input->location, res.slot);
}
input->binding = res.slot;
enabled_tex_mask_ |= (1ull << input->binding);
input++;
}
else if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) {
copy_input_name(input, res.image.name, name_buffer_, name_buffer_offset);
/* Until we make use of explicit uniform location. */
if (true || !GLContext::explicit_location_support) {
input->location = glGetUniformLocation(program, res.image.name.c_str());
glUniform1i(input->location, res.slot);
}
input->binding = res.slot;
enabled_ima_mask_ |= (1 << input->binding);
input++;
}
}
for (const ShaderCreateInfo::PushConst &uni : info.push_constants_) {
copy_input_name(input, uni.name, name_buffer_, name_buffer_offset);
/* Until we make use of explicit uniform location. */
if (true || !GLContext::explicit_location_support) {
input->location = glGetUniformLocation(program, name_buffer_ + input->name_offset);
}
input->binding = -1;
input++;
}
/* Compatibility uniforms. */
for (auto &name : workaround_uniform_names) {
copy_input_name(input, name, name_buffer_, name_buffer_offset);
input->location = glGetUniformLocation(program, name_buffer_ + input->name_offset);
input->binding = -1;
input++;
}
/* SSBOs */
for (const ShaderCreateInfo::Resource &res : all_resources) {
if (res.bind_type == ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER) {
copy_input_name(input, res.storagebuf.name, name_buffer_, name_buffer_offset);
input->location = input->binding = res.slot;
enabled_ubo_mask_ |= (1 << input->binding);
input++;
}
}
/* Builtin Uniforms */
for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) {
GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int);
const ShaderInput *uni = this->uniform_get(builtin_uniform_name(u));
builtins_[u] = (uni != nullptr) ? uni->location : -1;
}
/* Builtin Uniforms Blocks */
for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) {
GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int);
const ShaderInput *block = this->ubo_get(builtin_uniform_block_name(u));
builtin_blocks_[u] = (block != nullptr) ? block->binding : -1;
}
this->sort_inputs();
// this->debug_print();
}
GLShaderInterface::~GLShaderInterface()
{
for (auto *ref : refs_) {
if (ref != nullptr) {
ref->remove(this);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Batch Reference
* \{ */
void GLShaderInterface::ref_add(GLVaoCache *ref)
{
for (int i = 0; i < refs_.size(); i++) {
if (refs_[i] == NULL) {
refs_[i] = ref;
return;
}
}
refs_.append(ref);
}
void GLShaderInterface::ref_remove(GLVaoCache *ref)
{
for (int i = 0; i < refs_.size(); i++) {
if (refs_[i] == ref) {
refs_[i] = NULL;
break; /* cannot have duplicates */
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Validation
* TODO
* \{ */
/** \} */
} // namespace blender::gpu