OpenGL: Specialization Constants
This PR adds support for specialization constants for the OpenGL backend. The minimum OpenGL version we are targetting doesn't have native support for specialization constants. We simulate this by keeping track of shader programs for each set of specialization constants that are being used. Specialization constants can be used to reduce shader complexity and improve performance as less registry and/or spilling is done. This requires the ability to recompile GLShaders. In order to do this we need to keep track of the sources that are used when the shader was compiled. For static sources we only store references (`GLSource::source_ref`), for dynamically generated sources we keep a copy of the source (`GLSource::source`). When recompiling the shader GLSL source-code is generated for the constants stored in `Shader::constants`. When compiling the previous GLSource that contains specialization constants is then replaced by the new version. Pull Request: https://projects.blender.org/blender/blender/pulls/116926
This commit is contained in:
@@ -65,8 +65,10 @@ Shader::~Shader()
|
||||
static void standard_defines(Vector<const char *> &sources)
|
||||
{
|
||||
BLI_assert(sources.is_empty());
|
||||
/* Version needs to be first. Exact values will be added by implementation. */
|
||||
/* Version and specialization constants needs to be first.
|
||||
* Exact values will be added by implementation. */
|
||||
sources.append("version");
|
||||
sources.append("/* specialization_constants */");
|
||||
/* Define to identify code usage in shading language. */
|
||||
sources.append("#define GPU_SHADER\n");
|
||||
/* some useful defines to detect GPU type */
|
||||
@@ -297,7 +299,7 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info)
|
||||
}
|
||||
|
||||
Shader *shader = GPUBackend::get()->shader_alloc(info.name_.c_str());
|
||||
|
||||
shader->init(info);
|
||||
shader->specialization_constants_init(info);
|
||||
|
||||
std::string defines = shader->defines_declare(info);
|
||||
@@ -462,8 +464,13 @@ void GPU_shader_bind(GPUShader *gpu_shader)
|
||||
shader->bind();
|
||||
GPU_matrix_bind(gpu_shader);
|
||||
Shader::set_srgb_uniform(gpu_shader);
|
||||
shader->constants.is_dirty = false;
|
||||
}
|
||||
else {
|
||||
if (shader->constants.is_dirty) {
|
||||
shader->bind();
|
||||
shader->constants.is_dirty = false;
|
||||
}
|
||||
if (Shader::srgb_uniform_dirty_get()) {
|
||||
Shader::set_srgb_uniform(gpu_shader);
|
||||
}
|
||||
@@ -557,27 +564,36 @@ void Shader::specialization_constants_init(const shader::ShaderCreateInfo &info)
|
||||
constants.types.append(sc.type);
|
||||
constants.values.append(sc.default_value);
|
||||
}
|
||||
constants.is_dirty = true;
|
||||
}
|
||||
|
||||
void GPU_shader_constant_int_ex(GPUShader *sh, int location, int value)
|
||||
{
|
||||
BLI_assert(unwrap(sh)->constants.types[location] == gpu::shader::Type::INT);
|
||||
unwrap(sh)->constants.values[location].i = value;
|
||||
Shader &shader = *unwrap(sh);
|
||||
BLI_assert(shader.constants.types[location] == gpu::shader::Type::INT);
|
||||
shader.constants.values[location].i = value;
|
||||
shader.constants.is_dirty = true;
|
||||
}
|
||||
void GPU_shader_constant_uint_ex(GPUShader *sh, int location, uint value)
|
||||
{
|
||||
BLI_assert(unwrap(sh)->constants.types[location] == gpu::shader::Type::UINT);
|
||||
unwrap(sh)->constants.values[location].u = value;
|
||||
Shader &shader = *unwrap(sh);
|
||||
BLI_assert(shader.constants.types[location] == gpu::shader::Type::UINT);
|
||||
shader.constants.values[location].u = value;
|
||||
shader.constants.is_dirty = true;
|
||||
}
|
||||
void GPU_shader_constant_float_ex(GPUShader *sh, int location, float value)
|
||||
{
|
||||
BLI_assert(unwrap(sh)->constants.types[location] == gpu::shader::Type::FLOAT);
|
||||
unwrap(sh)->constants.values[location].f = value;
|
||||
Shader &shader = *unwrap(sh);
|
||||
BLI_assert(shader.constants.types[location] == gpu::shader::Type::FLOAT);
|
||||
shader.constants.values[location].f = value;
|
||||
shader.constants.is_dirty = true;
|
||||
}
|
||||
void GPU_shader_constant_bool_ex(GPUShader *sh, int location, bool value)
|
||||
{
|
||||
BLI_assert(unwrap(sh)->constants.types[location] == gpu::shader::Type::BOOL);
|
||||
unwrap(sh)->constants.values[location].u = value;
|
||||
Shader &shader = *unwrap(sh);
|
||||
BLI_assert(shader.constants.types[location] == gpu::shader::Type::BOOL);
|
||||
shader.constants.values[location].u = value;
|
||||
shader.constants.is_dirty = true;
|
||||
}
|
||||
|
||||
void GPU_shader_constant_int(GPUShader *sh, const char *name, int value)
|
||||
|
||||
@@ -25,6 +25,14 @@ namespace gpu {
|
||||
|
||||
class GPULogParser;
|
||||
|
||||
/**
|
||||
* Compilation is done on a list of GLSL sources. This list contains placeholders that should be
|
||||
* provided by the backend shader. These defines contains the locations where the backend can patch
|
||||
* the sources.
|
||||
*/
|
||||
#define SOURCES_INDEX_VERSION 0
|
||||
#define SOURCES_INDEX_SPECIALIZATION_CONSTANTS 1
|
||||
|
||||
/**
|
||||
* Implementation of shader compilation and uniforms handling.
|
||||
* Base class which is then specialized for each implementation (GL, VK, ...).
|
||||
@@ -44,6 +52,13 @@ class Shader {
|
||||
/* Current values set by `GPU_shader_constant_*()` call. The backend can choose to interpret
|
||||
* that however it wants (i.e: bind another shader instead). */
|
||||
Vector<Value> values;
|
||||
|
||||
/**
|
||||
* OpenGL needs to know if a different program needs to be attached when constants are
|
||||
* changed. Vulkan and Metal uses pipelines and don't have this issue. Attribute can be
|
||||
* removed after the OpenGL backend has been phased out.
|
||||
*/
|
||||
bool is_dirty;
|
||||
} constants;
|
||||
|
||||
protected:
|
||||
@@ -60,6 +75,8 @@ class Shader {
|
||||
Shader(const char *name);
|
||||
virtual ~Shader();
|
||||
|
||||
virtual void init(const shader::ShaderCreateInfo &info) = 0;
|
||||
|
||||
virtual void vertex_shader_from_glsl(MutableSpan<const char *> sources) = 0;
|
||||
virtual void geometry_shader_from_glsl(MutableSpan<const char *> sources) = 0;
|
||||
virtual void fragment_shader_from_glsl(MutableSpan<const char *> sources) = 0;
|
||||
|
||||
@@ -274,6 +274,8 @@ class MTLShader : public Shader {
|
||||
NSString *fragment_function_name_);
|
||||
~MTLShader();
|
||||
|
||||
void init(const shader::ShaderCreateInfo & /*info*/) override {}
|
||||
|
||||
/* Assign GLSL source. */
|
||||
void vertex_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
void geometry_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
|
||||
@@ -181,7 +181,7 @@ void MTLShader::vertex_shader_from_glsl(MutableSpan<const char *> sources)
|
||||
shd_builder_->source_from_msl_ = false;
|
||||
|
||||
/* Remove #version tag entry. */
|
||||
sources[0] = "";
|
||||
sources[SOURCES_INDEX_VERSION] = "";
|
||||
|
||||
/* Consolidate GLSL vertex sources. */
|
||||
std::stringstream ss;
|
||||
@@ -203,7 +203,7 @@ void MTLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources)
|
||||
shd_builder_->source_from_msl_ = false;
|
||||
|
||||
/* Remove #version tag entry. */
|
||||
sources[0] = "";
|
||||
sources[SOURCES_INDEX_VERSION] = "";
|
||||
|
||||
/* Consolidate GLSL fragment sources. */
|
||||
std::stringstream ss;
|
||||
@@ -231,7 +231,7 @@ void MTLShader::compute_shader_from_glsl(MutableSpan<const char *> sources)
|
||||
shd_builder_->source_from_msl_ = false;
|
||||
|
||||
/* Remove #version tag entry. */
|
||||
sources[0] = "";
|
||||
sources[SOURCES_INDEX_VERSION] = "";
|
||||
|
||||
/* Consolidate GLSL compute sources. */
|
||||
std::stringstream ss;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "GPU_capabilities.h"
|
||||
#include "GPU_platform.h"
|
||||
#include "gpu_shader_dependency_private.h"
|
||||
|
||||
#include "gl_debug.hh"
|
||||
#include "gl_vertex_buffer.hh"
|
||||
@@ -40,9 +41,6 @@ GLShader::GLShader(const char *name) : Shader(name)
|
||||
* does not have a GPUContext. */
|
||||
BLI_assert(GLContext::get() != nullptr);
|
||||
#endif
|
||||
shader_program_ = glCreateProgram();
|
||||
|
||||
debug::object_label(GL_PROGRAM, shader_program_, name);
|
||||
}
|
||||
|
||||
GLShader::~GLShader()
|
||||
@@ -51,12 +49,14 @@ GLShader::~GLShader()
|
||||
* does not have a GPUContext. */
|
||||
BLI_assert(GLContext::get() != nullptr);
|
||||
#endif
|
||||
/* Invalid handles are silently ignored. */
|
||||
glDeleteShader(vert_shader_);
|
||||
glDeleteShader(geom_shader_);
|
||||
glDeleteShader(frag_shader_);
|
||||
glDeleteShader(compute_shader_);
|
||||
glDeleteProgram(shader_program_);
|
||||
}
|
||||
|
||||
void GLShader::init(const shader::ShaderCreateInfo &info)
|
||||
{
|
||||
/* Extract the constants names from info and store them locally. */
|
||||
for (const ShaderCreateInfo::SpecializationConstant &constant : info.specialization_constants_) {
|
||||
specialization_constant_names_.append(constant.name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -618,30 +618,6 @@ std::string GLShader::resources_declare(const ShaderCreateInfo &info) const
|
||||
/* NOTE: We define macros in GLSL to trigger compilation error if the resource names
|
||||
* are reused for local variables. This is to match other backend behavior which needs accessors
|
||||
* macros. */
|
||||
|
||||
ss << "\n/* Specialization Constants (pass-through). */\n";
|
||||
for (const ShaderCreateInfo::SpecializationConstant &sc : info.specialization_constants_) {
|
||||
switch (sc.type) {
|
||||
case Type::INT:
|
||||
ss << "const int " << sc.name << "=" << std::to_string(sc.default_value.i) << ";\n";
|
||||
break;
|
||||
case Type::UINT:
|
||||
ss << "const uint " << sc.name << "=" << std::to_string(sc.default_value.u) << "u;\n";
|
||||
break;
|
||||
case Type::BOOL:
|
||||
ss << "const bool " << sc.name << "=" << (sc.default_value.u ? "true" : "false") << ";\n";
|
||||
break;
|
||||
case Type::FLOAT:
|
||||
/* Use uint representation to allow exact same bit pattern even if NaN. */
|
||||
ss << "const float " << sc.name << "= uintBitsToFloat("
|
||||
<< std::to_string(sc.default_value.u) << "u);\n";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ss << "\n/* Pass Resources. */\n";
|
||||
for (const ShaderCreateInfo::Resource &res : info.pass_resources_) {
|
||||
print_resource(ss, res, info.auto_resource_location_);
|
||||
@@ -675,6 +651,39 @@ std::string GLShader::resources_declare(const ShaderCreateInfo &info) const
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string GLShader::constants_declare() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
ss << "/* Specialization Constants. */\n";
|
||||
for (int constant_index : IndexRange(constants.types.size())) {
|
||||
const StringRefNull name = specialization_constant_names_[constant_index];
|
||||
gpu::shader::Type constant_type = constants.types[constant_index];
|
||||
const shader::ShaderCreateInfo::SpecializationConstant::Value &value =
|
||||
constants.values[constant_index];
|
||||
|
||||
switch (constant_type) {
|
||||
case Type::INT:
|
||||
ss << "const int " << name << "=" << std::to_string(value.i) << ";\n";
|
||||
break;
|
||||
case Type::UINT:
|
||||
ss << "const uint " << name << "=" << std::to_string(value.u) << "u;\n";
|
||||
break;
|
||||
case Type::BOOL:
|
||||
ss << "const bool " << name << "=" << (value.u ? "true" : "false") << ";\n";
|
||||
break;
|
||||
case Type::FLOAT:
|
||||
/* Use uint representation to allow exact same bit pattern even if NaN. */
|
||||
ss << "const float " << name << "= uintBitsToFloat(" << std::to_string(value.u) << "u);\n";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static std::string main_function_wrapper(std::string &pre_main, std::string &post_main)
|
||||
{
|
||||
std::stringstream ss;
|
||||
@@ -1168,7 +1177,9 @@ const char *GLShader::glsl_patch_get(GLenum gl_stage)
|
||||
return glsl_patch_default_get();
|
||||
}
|
||||
|
||||
GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources)
|
||||
GLuint GLShader::create_shader_stage(GLenum gl_stage,
|
||||
MutableSpan<const char *> sources,
|
||||
const GLSources &gl_sources)
|
||||
{
|
||||
GLuint shader = glCreateShader(gl_stage);
|
||||
if (shader == 0) {
|
||||
@@ -1176,8 +1187,21 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *>
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Patch the shader sources to include specialization constants. */
|
||||
std::string constants_source;
|
||||
Vector<const char *> recreated_sources;
|
||||
const bool has_specialization_constants = !constants.types.is_empty();
|
||||
if (has_specialization_constants) {
|
||||
constants_source = constants_declare();
|
||||
if (sources.is_empty()) {
|
||||
recreated_sources = gl_sources.sources_get();
|
||||
sources = recreated_sources;
|
||||
}
|
||||
}
|
||||
|
||||
/* Patch the shader code using the first source slot. */
|
||||
sources[0] = glsl_patch_get(gl_stage);
|
||||
sources[SOURCES_INDEX_VERSION] = glsl_patch_get(gl_stage);
|
||||
sources[SOURCES_INDEX_SPECIALIZATION_CONSTANTS] = constants_source.c_str();
|
||||
|
||||
glShaderSource(shader, sources.size(), sources.data(), nullptr);
|
||||
glCompileShader(shader);
|
||||
@@ -1213,28 +1237,47 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *>
|
||||
|
||||
debug::object_label(gl_stage, shader, name);
|
||||
|
||||
glAttachShader(shader_program_, shader);
|
||||
glAttachShader(program_active_->program_id, shader);
|
||||
return shader;
|
||||
}
|
||||
|
||||
void GLShader::update_program_and_sources(GLSources &stage_sources,
|
||||
MutableSpan<const char *> sources)
|
||||
{
|
||||
const bool has_specialization_constants = !constants.types.is_empty();
|
||||
if (has_specialization_constants && stage_sources.is_empty()) {
|
||||
stage_sources = sources;
|
||||
}
|
||||
|
||||
init_program();
|
||||
}
|
||||
|
||||
void GLShader::vertex_shader_from_glsl(MutableSpan<const char *> sources)
|
||||
{
|
||||
vert_shader_ = this->create_shader_stage(GL_VERTEX_SHADER, sources);
|
||||
update_program_and_sources(vertex_sources_, sources);
|
||||
program_active_->vert_shader = this->create_shader_stage(
|
||||
GL_VERTEX_SHADER, sources, vertex_sources_);
|
||||
}
|
||||
|
||||
void GLShader::geometry_shader_from_glsl(MutableSpan<const char *> sources)
|
||||
{
|
||||
geom_shader_ = this->create_shader_stage(GL_GEOMETRY_SHADER, sources);
|
||||
update_program_and_sources(geometry_sources_, sources);
|
||||
program_active_->geom_shader = this->create_shader_stage(
|
||||
GL_GEOMETRY_SHADER, sources, geometry_sources_);
|
||||
}
|
||||
|
||||
void GLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources)
|
||||
{
|
||||
frag_shader_ = this->create_shader_stage(GL_FRAGMENT_SHADER, sources);
|
||||
update_program_and_sources(fragment_sources_, sources);
|
||||
program_active_->frag_shader = this->create_shader_stage(
|
||||
GL_FRAGMENT_SHADER, sources, fragment_sources_);
|
||||
}
|
||||
|
||||
void GLShader::compute_shader_from_glsl(MutableSpan<const char *> sources)
|
||||
{
|
||||
compute_shader_ = this->create_shader_stage(GL_COMPUTE_SHADER, sources);
|
||||
update_program_and_sources(compute_sources_, sources);
|
||||
program_active_->compute_shader = this->create_shader_stage(
|
||||
GL_COMPUTE_SHADER, sources, compute_sources_);
|
||||
}
|
||||
|
||||
bool GLShader::finalize(const shader::ShaderCreateInfo *info)
|
||||
@@ -1249,26 +1292,21 @@ bool GLShader::finalize(const shader::ShaderCreateInfo *info)
|
||||
sources.append("version");
|
||||
sources.append(source.c_str());
|
||||
geometry_shader_from_glsl(sources);
|
||||
if (!constants.types.is_empty()) {
|
||||
geometry_sources_ = sources;
|
||||
}
|
||||
}
|
||||
|
||||
glLinkProgram(shader_program_);
|
||||
|
||||
GLint status;
|
||||
glGetProgramiv(shader_program_, GL_LINK_STATUS, &status);
|
||||
if (!status) {
|
||||
char log[5000];
|
||||
glGetProgramInfoLog(shader_program_, sizeof(log), nullptr, log);
|
||||
Span<const char *> sources;
|
||||
GLLogParser parser;
|
||||
this->print_log(sources, log, "Linking", true, &parser);
|
||||
if (!program_link()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GLuint program_id = program_get();
|
||||
if (info != nullptr && info->legacy_resource_location_ == false) {
|
||||
interface = new GLShaderInterface(shader_program_, *info);
|
||||
interface = new GLShaderInterface(program_id, *info);
|
||||
}
|
||||
else {
|
||||
interface = new GLShaderInterface(shader_program_);
|
||||
interface = new GLShaderInterface(program_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -1282,8 +1320,8 @@ bool GLShader::finalize(const shader::ShaderCreateInfo *info)
|
||||
|
||||
void GLShader::bind()
|
||||
{
|
||||
BLI_assert(shader_program_ != 0);
|
||||
glUseProgram(shader_program_);
|
||||
GLuint program_id = program_get();
|
||||
glUseProgram(program_id);
|
||||
}
|
||||
|
||||
void GLShader::unbind()
|
||||
@@ -1305,7 +1343,7 @@ void GLShader::transform_feedback_names_set(Span<const char *> name_list,
|
||||
const eGPUShaderTFBType geom_type)
|
||||
{
|
||||
glTransformFeedbackVaryings(
|
||||
shader_program_, name_list.size(), name_list.data(), GL_INTERLEAVED_ATTRIBS);
|
||||
program_get(), name_list.size(), name_list.data(), GL_INTERLEAVED_ATTRIBS);
|
||||
transform_feedback_type_ = geom_type;
|
||||
}
|
||||
|
||||
@@ -1408,7 +1446,144 @@ void GLShader::uniform_int(int location, int comp_len, int array_size, const int
|
||||
|
||||
int GLShader::program_handle_get() const
|
||||
{
|
||||
return int(this->shader_program_);
|
||||
BLI_assert(program_active_);
|
||||
return program_active_->program_id;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Sources
|
||||
* \{ */
|
||||
GLSource::GLSource(const char *other)
|
||||
{
|
||||
if (!gpu_shader_dependency_get_filename_from_source_string(other).is_empty()) {
|
||||
source = "";
|
||||
source_ref = StringRefNull(other);
|
||||
}
|
||||
else {
|
||||
source = other;
|
||||
source_ref = StringRefNull(source);
|
||||
}
|
||||
}
|
||||
|
||||
GLSources &GLSources::operator=(Span<const char *> other)
|
||||
{
|
||||
clear();
|
||||
reserve(other.size());
|
||||
|
||||
for (const char *other_source : other) {
|
||||
/* Don't store empty string as compilers can optimize these away and result in pointing to a
|
||||
* string that isn't c-str compliant anymore. */
|
||||
if (other_source[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
append(GLSource(other_source));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Vector<const char *> GLSources::sources_get() const
|
||||
{
|
||||
Vector<const char *> result;
|
||||
result.reserve(size());
|
||||
|
||||
for (const GLSource &source : *this) {
|
||||
result.append(source.source_ref.c_str());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Specialization Constants
|
||||
* \{ */
|
||||
|
||||
GLShader::GLProgram::~GLProgram()
|
||||
{
|
||||
/* Invalid handles are silently ignored. */
|
||||
glDeleteShader(vert_shader);
|
||||
glDeleteShader(geom_shader);
|
||||
glDeleteShader(frag_shader);
|
||||
glDeleteShader(compute_shader);
|
||||
glDeleteProgram(program_id);
|
||||
}
|
||||
|
||||
bool GLShader::program_link()
|
||||
{
|
||||
GLuint program_id = program_active_->program_id;
|
||||
glLinkProgram(program_id);
|
||||
|
||||
GLint status;
|
||||
glGetProgramiv(program_id, GL_LINK_STATUS, &status);
|
||||
if (!status) {
|
||||
char log[5000];
|
||||
glGetProgramInfoLog(program_id, sizeof(log), nullptr, log);
|
||||
Span<const char *> sources;
|
||||
GLLogParser parser;
|
||||
print_log(sources, log, "Linking", true, &parser);
|
||||
}
|
||||
|
||||
return bool(status);
|
||||
}
|
||||
|
||||
void GLShader::init_program()
|
||||
{
|
||||
if (program_active_) {
|
||||
return;
|
||||
}
|
||||
|
||||
program_active_ = &program_cache_.lookup_or_add_default(constants.values);
|
||||
if (!program_active_->program_id) {
|
||||
program_active_->program_id = glCreateProgram();
|
||||
debug::object_label(GL_PROGRAM, program_active_->program_id, name);
|
||||
}
|
||||
}
|
||||
|
||||
GLuint GLShader::program_get()
|
||||
{
|
||||
if (constants.types.is_empty()) {
|
||||
/* Early exit for shaders that doesn't use specialization constants. The active shader should
|
||||
* already be setup. */
|
||||
BLI_assert(program_active_ && program_active_->program_id);
|
||||
return program_active_->program_id;
|
||||
}
|
||||
|
||||
if (!constants.is_dirty) {
|
||||
/* Early exit when constants didn't change since the last call. */
|
||||
BLI_assert(program_active_ && program_active_->program_id);
|
||||
return program_active_->program_id;
|
||||
}
|
||||
|
||||
program_active_ = &program_cache_.lookup_or_add_default(constants.values);
|
||||
if (!program_active_->program_id) {
|
||||
program_active_->program_id = glCreateProgram();
|
||||
debug::object_label(GL_PROGRAM, program_active_->program_id, name);
|
||||
MutableSpan<const char *> no_sources;
|
||||
if (!vertex_sources_.is_empty()) {
|
||||
program_active_->vert_shader = create_shader_stage(
|
||||
GL_VERTEX_SHADER, no_sources, vertex_sources_);
|
||||
}
|
||||
if (!geometry_sources_.is_empty()) {
|
||||
program_active_->geom_shader = create_shader_stage(
|
||||
GL_GEOMETRY_SHADER, no_sources, geometry_sources_);
|
||||
}
|
||||
if (!fragment_sources_.is_empty()) {
|
||||
program_active_->frag_shader = create_shader_stage(
|
||||
GL_FRAGMENT_SHADER, no_sources, fragment_sources_);
|
||||
}
|
||||
if (!compute_sources_.is_empty()) {
|
||||
program_active_->compute_shader = create_shader_stage(
|
||||
GL_COMPUTE_SHADER, no_sources, compute_sources_);
|
||||
}
|
||||
|
||||
program_link();
|
||||
}
|
||||
|
||||
constants.is_dirty = false;
|
||||
return program_active_->program_id;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -12,12 +12,50 @@
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
#include "BLI_map.hh"
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
#include "gpu_shader_private.hh"
|
||||
|
||||
namespace blender {
|
||||
template<>
|
||||
struct DefaultHash<Vector<gpu::shader::ShaderCreateInfo::SpecializationConstant::Value>> {
|
||||
uint64_t operator()(
|
||||
const Vector<gpu::shader::ShaderCreateInfo::SpecializationConstant::Value> &key) const
|
||||
{
|
||||
uint64_t hash = 0;
|
||||
for (const gpu::shader::ShaderCreateInfo::SpecializationConstant::Value &value : key) {
|
||||
hash = hash * 33 + value.u;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
namespace gpu {
|
||||
|
||||
/**
|
||||
* Shaders that uses specialization constants must keep track of the sources in order to rebuild
|
||||
* shader stages.
|
||||
*
|
||||
* Some sources are shared and won't be copied. For example for dependencies. In this case we
|
||||
* would only store the source_ref.
|
||||
*
|
||||
* Other sources would be stored in the #source attribute. #source_ref
|
||||
* would still be updated.
|
||||
*/
|
||||
struct GLSource {
|
||||
std::string source;
|
||||
StringRefNull source_ref;
|
||||
|
||||
GLSource() = default;
|
||||
GLSource(const char *other_source);
|
||||
};
|
||||
class GLSources : public Vector<GLSource> {
|
||||
public:
|
||||
GLSources &operator=(Span<const char *> other);
|
||||
Vector<const char *> sources_get() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of shader compilation and uniforms handling using OpenGL.
|
||||
*/
|
||||
@@ -26,13 +64,61 @@ class GLShader : public Shader {
|
||||
friend shader::StageInterfaceInfo;
|
||||
|
||||
private:
|
||||
/** Handle for full program (links shader stages below). */
|
||||
GLuint shader_program_ = 0;
|
||||
/** Individual shader stages. */
|
||||
GLuint vert_shader_ = 0;
|
||||
GLuint geom_shader_ = 0;
|
||||
GLuint frag_shader_ = 0;
|
||||
GLuint compute_shader_ = 0;
|
||||
struct GLProgram {
|
||||
/** Handle for program. */
|
||||
GLuint program_id = 0;
|
||||
/** Handle for individual shader stages. */
|
||||
GLuint vert_shader = 0;
|
||||
GLuint geom_shader = 0;
|
||||
GLuint frag_shader = 0;
|
||||
GLuint compute_shader = 0;
|
||||
~GLProgram();
|
||||
};
|
||||
|
||||
using GLProgramCacheKey = Vector<shader::ShaderCreateInfo::SpecializationConstant::Value>;
|
||||
Map<GLProgramCacheKey, GLProgram> program_cache_;
|
||||
|
||||
/**
|
||||
* Points to the active program. When binding a shader the active program is
|
||||
* setup.
|
||||
*/
|
||||
GLProgram *program_active_ = nullptr;
|
||||
|
||||
/**
|
||||
* When the shader uses Specialization Constants these attribute contains the sources to
|
||||
* rebuild shader stages. When Specialization Constants aren't used they are empty to
|
||||
* reduce memory needs.
|
||||
*/
|
||||
GLSources vertex_sources_;
|
||||
GLSources geometry_sources_;
|
||||
GLSources fragment_sources_;
|
||||
GLSources compute_sources_;
|
||||
|
||||
Vector<const char *> specialization_constant_names_;
|
||||
|
||||
/**
|
||||
* Initialize an this instance.
|
||||
*
|
||||
* - Ensures that program_cache at least has a default GLProgram.
|
||||
* - Ensures that active program is set.
|
||||
* - Active GLProgram has a shader_program (at least in creation state).
|
||||
* - Does nothing when instance was already initialized.
|
||||
*/
|
||||
void init_program();
|
||||
|
||||
void update_program_and_sources(GLSources &stage_sources, MutableSpan<const char *> sources);
|
||||
|
||||
/**
|
||||
* Link the active program.
|
||||
*/
|
||||
bool program_link();
|
||||
|
||||
/**
|
||||
* Return a GLProgram program id that reflects the current state of shader.constants.values.
|
||||
* The returned program_id is in linked state, or an error happened during linking.
|
||||
*/
|
||||
GLuint program_get();
|
||||
|
||||
/** True if any shader failed to compile. */
|
||||
bool compilation_failed_ = false;
|
||||
|
||||
@@ -42,6 +128,8 @@ class GLShader : public Shader {
|
||||
GLShader(const char *name);
|
||||
~GLShader();
|
||||
|
||||
void init(const shader::ShaderCreateInfo &info) override;
|
||||
|
||||
/** Return true on success. */
|
||||
void vertex_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
void geometry_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
@@ -51,6 +139,7 @@ class GLShader : public Shader {
|
||||
void warm_cache(int /*limit*/) override{};
|
||||
|
||||
std::string resources_declare(const shader::ShaderCreateInfo &info) const override;
|
||||
std::string constants_declare() const;
|
||||
std::string vertex_interface_declare(const shader::ShaderCreateInfo &info) const override;
|
||||
std::string fragment_interface_declare(const shader::ShaderCreateInfo &info) const override;
|
||||
std::string geometry_interface_declare(const shader::ShaderCreateInfo &info) const override;
|
||||
@@ -84,14 +173,22 @@ class GLShader : public Shader {
|
||||
|
||||
bool is_compute() const
|
||||
{
|
||||
return compute_shader_ != 0;
|
||||
if (!vertex_sources_.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
if (!compute_sources_.is_empty()) {
|
||||
return true;
|
||||
}
|
||||
return program_active_->compute_shader != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
const char *glsl_patch_get(GLenum gl_stage);
|
||||
|
||||
/** Create, compile and attach the shader stage to the shader program. */
|
||||
GLuint create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources);
|
||||
GLuint create_shader_stage(GLenum gl_stage,
|
||||
MutableSpan<const char *> sources,
|
||||
const GLSources &gl_sources);
|
||||
|
||||
/**
|
||||
* \brief features available on newer implementation such as native barycentric coordinates
|
||||
|
||||
@@ -610,7 +610,7 @@ void VKShader::build_shader_module(MutableSpan<const char *> sources,
|
||||
shaderc_compute_shader),
|
||||
"Only forced ShaderC shader kinds are supported.");
|
||||
const VKDevice &device = VKBackend::get().device_get();
|
||||
sources[0] = device.glsl_patch_get();
|
||||
sources[SOURCES_INDEX_VERSION] = device.glsl_patch_get();
|
||||
Vector<uint32_t> spirv_module = compile_glsl_to_spirv(sources, stage);
|
||||
build_shader_module(spirv_module, r_shader_module);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ class VKShader : public Shader {
|
||||
VKShader(const char *name);
|
||||
virtual ~VKShader();
|
||||
|
||||
void init(const shader::ShaderCreateInfo & /*info*/) override {}
|
||||
|
||||
void vertex_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
void geometry_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
void fragment_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
|
||||
Reference in New Issue
Block a user