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
207 lines
6.6 KiB
C++
207 lines
6.6 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup gpu
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "BLI_span.hh"
|
|
#include "BLI_string_ref.hh"
|
|
|
|
#include "GPU_shader.h"
|
|
#include "gpu_shader_create_info.hh"
|
|
#include "gpu_shader_interface.hh"
|
|
#include "gpu_vertex_buffer_private.hh"
|
|
|
|
#include "BLI_map.hh"
|
|
|
|
#include <string>
|
|
|
|
namespace blender {
|
|
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, ...).
|
|
*/
|
|
class Shader {
|
|
public:
|
|
/** Uniform & attribute locations for shader. */
|
|
ShaderInterface *interface = nullptr;
|
|
|
|
/**
|
|
* Specialization constants as a Struct-of-Arrays. Allow simpler comparison and reset.
|
|
* The backend is free to implement their support as they see fit.
|
|
*/
|
|
struct Constants {
|
|
using Value = shader::ShaderCreateInfo::SpecializationConstant::Value;
|
|
Vector<gpu::shader::Type> types;
|
|
/* 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:
|
|
/** For debugging purpose. */
|
|
char name[64];
|
|
|
|
/* Parent shader can be used for shaders which are derived from the same source material.
|
|
* The child shader can pull information from its parent to prepare additional resources
|
|
* such as PSOs upfront. This enables asynchronous PSO compilation which mitigates stuttering
|
|
* when updating new materials. */
|
|
Shader *parent_shader_ = nullptr;
|
|
|
|
public:
|
|
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;
|
|
virtual void compute_shader_from_glsl(MutableSpan<const char *> sources) = 0;
|
|
virtual bool finalize(const shader::ShaderCreateInfo *info = nullptr) = 0;
|
|
/* Pre-warms PSOs using parent shader's cached PSO descriptors. Limit specifies maximum PSOs to
|
|
* warm. If -1, compiles all PSO permutations in parent shader.
|
|
*
|
|
* See `GPU_shader_warm_cache(..)` in `GPU_shader.h` for more information. */
|
|
virtual void warm_cache(int limit) = 0;
|
|
|
|
virtual void transform_feedback_names_set(Span<const char *> name_list,
|
|
eGPUShaderTFBType geom_type) = 0;
|
|
virtual bool transform_feedback_enable(GPUVertBuf *) = 0;
|
|
virtual void transform_feedback_disable() = 0;
|
|
|
|
virtual void bind() = 0;
|
|
virtual void unbind() = 0;
|
|
|
|
virtual void uniform_float(int location, int comp_len, int array_size, const float *data) = 0;
|
|
virtual void uniform_int(int location, int comp_len, int array_size, const int *data) = 0;
|
|
|
|
/* Add specialization constant declarations to shader instance. */
|
|
void specialization_constants_init(const shader::ShaderCreateInfo &info);
|
|
|
|
std::string defines_declare(const shader::ShaderCreateInfo &info) const;
|
|
virtual std::string resources_declare(const shader::ShaderCreateInfo &info) const = 0;
|
|
virtual std::string vertex_interface_declare(const shader::ShaderCreateInfo &info) const = 0;
|
|
virtual std::string fragment_interface_declare(const shader::ShaderCreateInfo &info) const = 0;
|
|
virtual std::string geometry_interface_declare(const shader::ShaderCreateInfo &info) const = 0;
|
|
virtual std::string geometry_layout_declare(const shader::ShaderCreateInfo &info) const = 0;
|
|
virtual std::string compute_layout_declare(const shader::ShaderCreateInfo &info) const = 0;
|
|
|
|
/* DEPRECATED: Kept only because of BGL API. */
|
|
virtual int program_handle_get() const = 0;
|
|
|
|
/* Only used by SSBO Vertex fetch. */
|
|
virtual bool get_uses_ssbo_vertex_fetch() const = 0;
|
|
virtual int get_ssbo_vertex_fetch_output_num_verts() const = 0;
|
|
|
|
inline const char *const name_get() const
|
|
{
|
|
return name;
|
|
}
|
|
|
|
inline void parent_set(Shader *parent)
|
|
{
|
|
parent_shader_ = parent;
|
|
}
|
|
|
|
inline Shader *parent_get() const
|
|
{
|
|
return parent_shader_;
|
|
}
|
|
|
|
static bool srgb_uniform_dirty_get();
|
|
static void set_srgb_uniform(GPUShader *shader);
|
|
static void set_framebuffer_srgb_target(int use_srgb_to_linear);
|
|
|
|
protected:
|
|
void print_log(Span<const char *> sources,
|
|
const char *log,
|
|
const char *stage,
|
|
bool error,
|
|
GPULogParser *parser);
|
|
};
|
|
|
|
/* Syntactic sugar. */
|
|
static inline GPUShader *wrap(Shader *vert)
|
|
{
|
|
return reinterpret_cast<GPUShader *>(vert);
|
|
}
|
|
static inline Shader *unwrap(GPUShader *vert)
|
|
{
|
|
return reinterpret_cast<Shader *>(vert);
|
|
}
|
|
static inline const Shader *unwrap(const GPUShader *vert)
|
|
{
|
|
return reinterpret_cast<const Shader *>(vert);
|
|
}
|
|
|
|
enum class Severity {
|
|
Unknown,
|
|
Warning,
|
|
Error,
|
|
Note,
|
|
};
|
|
|
|
struct LogCursor {
|
|
int source = -1;
|
|
int row = -1;
|
|
int column = -1;
|
|
StringRef file_name_and_error_line = {};
|
|
};
|
|
|
|
struct GPULogItem {
|
|
LogCursor cursor;
|
|
bool source_base_row = false;
|
|
Severity severity = Severity::Unknown;
|
|
};
|
|
|
|
class GPULogParser {
|
|
public:
|
|
virtual const char *parse_line(const char *source_combined,
|
|
const char *log_line,
|
|
GPULogItem &log_item) = 0;
|
|
|
|
protected:
|
|
const char *skip_severity(const char *log_line,
|
|
GPULogItem &log_item,
|
|
const char *error_msg,
|
|
const char *warning_msg,
|
|
const char *note_msg) const;
|
|
const char *skip_separators(const char *log_line, const StringRef separators) const;
|
|
const char *skip_until(const char *log_line, char stop_char) const;
|
|
bool at_number(const char *log_line) const;
|
|
bool at_any(const char *log_line, const StringRef chars) const;
|
|
int parse_number(const char *log_line, const char **r_new_position) const;
|
|
|
|
MEM_CXX_CLASS_ALLOC_FUNCS("GPULogParser");
|
|
};
|
|
|
|
} // namespace gpu
|
|
} // namespace blender
|
|
|
|
/* XXX do not use it. Special hack to use OCIO with batch API. */
|
|
GPUShader *immGetShader();
|