Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/draw/engines/eevee_next/eevee_shader.cc')
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.cc375
1 files changed, 375 insertions, 0 deletions
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc
new file mode 100644
index 00000000000..1ebf387f32b
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc
@@ -0,0 +1,375 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Shader module that manage shader libraries, deferred compilation,
+ * and static shader usage.
+ */
+
+#include "gpu_shader_create_info.hh"
+
+#include "eevee_shader.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Module
+ *
+ * \{ */
+
+ShaderModule *ShaderModule::g_shader_module = nullptr;
+
+ShaderModule *ShaderModule::module_get()
+{
+ if (g_shader_module == nullptr) {
+ /* TODO(fclem) threadsafety. */
+ g_shader_module = new ShaderModule();
+ }
+ return g_shader_module;
+}
+
+void ShaderModule::module_free()
+{
+ if (g_shader_module != nullptr) {
+ /* TODO(fclem) threadsafety. */
+ delete g_shader_module;
+ g_shader_module = nullptr;
+ }
+}
+
+ShaderModule::ShaderModule()
+{
+ for (GPUShader *&shader : shaders_) {
+ shader = nullptr;
+ }
+
+#ifdef DEBUG
+ /* Ensure all shader are described. */
+ for (auto i : IndexRange(MAX_SHADER_TYPE)) {
+ const char *name = static_shader_create_info_name_get(eShaderType(i));
+ if (name == nullptr) {
+ std::cerr << "EEVEE: Missing case for eShaderType(" << i
+ << ") in static_shader_create_info_name_get().";
+ BLI_assert(0);
+ }
+ const GPUShaderCreateInfo *create_info = GPU_shader_create_info_get(name);
+ BLI_assert_msg(create_info != nullptr, "EEVEE: Missing create info for static shader.");
+ }
+#endif
+}
+
+ShaderModule::~ShaderModule()
+{
+ for (GPUShader *&shader : shaders_) {
+ DRW_SHADER_FREE_SAFE(shader);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Static shaders
+ *
+ * \{ */
+
+const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_type)
+{
+ switch (shader_type) {
+ /* To avoid compiler warning about missing case. */
+ case MAX_SHADER_TYPE:
+ return "";
+ }
+ return "";
+}
+
+GPUShader *ShaderModule::static_shader_get(eShaderType shader_type)
+{
+ if (shaders_[shader_type] == nullptr) {
+ const char *shader_name = static_shader_create_info_name_get(shader_type);
+
+ shaders_[shader_type] = GPU_shader_create_from_info_name(shader_name);
+
+ if (shaders_[shader_type] == nullptr) {
+ fprintf(stderr, "EEVEE: error: Could not compile static shader \"%s\"\n", shader_name);
+ }
+ BLI_assert(shaders_[shader_type] != nullptr);
+ }
+ return shaders_[shader_type];
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPU Materials
+ *
+ * \{ */
+
+void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOutput *codegen_)
+{
+ using namespace blender::gpu::shader;
+
+ uint64_t shader_uuid = GPU_material_uuid_get(gpumat);
+
+ eMaterialPipeline pipeline_type;
+ eMaterialGeometry geometry_type;
+ material_type_from_shader_uuid(shader_uuid, pipeline_type, geometry_type);
+
+ GPUCodegenOutput &codegen = *codegen_;
+ ShaderCreateInfo &info = *reinterpret_cast<ShaderCreateInfo *>(codegen.create_info);
+
+ info.auto_resource_location(true);
+
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
+ info.define("MAT_TRANSPARENT");
+ }
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_BARYCENTRIC)) {
+ switch (geometry_type) {
+ case MAT_GEOM_MESH:
+ /* Support using gpu builtin barycentrics. */
+ info.define("USE_BARYCENTRICS");
+ info.builtins(BuiltinBits::BARYCENTRIC_COORD);
+ break;
+ case MAT_GEOM_CURVES:
+ /* Support using one vec2 attribute. See #hair_get_barycentric(). */
+ info.define("USE_BARYCENTRICS");
+ break;
+ default:
+ /* No support */
+ break;
+ }
+ }
+
+ std::stringstream global_vars;
+ switch (geometry_type) {
+ case MAT_GEOM_MESH:
+ /** Noop. */
+ break;
+ case MAT_GEOM_CURVES:
+ /** Hair attributes comme from sampler buffer. Transfer attributes to sampler. */
+ for (auto &input : info.vertex_inputs_) {
+ info.sampler(0, ImageType::FLOAT_BUFFER, input.name, Frequency::BATCH);
+ }
+ info.vertex_inputs_.clear();
+ break;
+ case MAT_GEOM_WORLD:
+ /**
+ * Only orco layer is supported by world and it is procedurally generated. These are here to
+ * make the attribs_load function calls valids.
+ */
+ ATTR_FALLTHROUGH;
+ case MAT_GEOM_GPENCIL:
+ /**
+ * Only one uv and one color attribute layer are supported by gpencil objects and they are
+ * already declared in another createInfo. These are here to make the attribs_load
+ * function calls valids.
+ */
+ for (auto &input : info.vertex_inputs_) {
+ global_vars << input.type << " " << input.name << ";\n";
+ }
+ info.vertex_inputs_.clear();
+ break;
+ case MAT_GEOM_VOLUME:
+ /** No attributes supported. */
+ info.vertex_inputs_.clear();
+ break;
+ }
+
+ const bool do_fragment_attrib_load = (geometry_type == MAT_GEOM_WORLD);
+
+ if (do_fragment_attrib_load && !info.vertex_out_interfaces_.is_empty()) {
+ /* Codegen outputs only one interface. */
+ const StageInterfaceInfo &iface = *info.vertex_out_interfaces_.first();
+ /* Globals the attrib_load() can write to when it is in the fragment shader. */
+ global_vars << "struct " << iface.name << " {\n";
+ for (auto &inout : iface.inouts) {
+ global_vars << " " << inout.type << " " << inout.name << ";\n";
+ }
+ global_vars << "};\n";
+ global_vars << iface.name << " " << iface.instance_name << ";\n";
+
+ info.vertex_out_interfaces_.clear();
+ }
+
+ std::stringstream attr_load;
+ attr_load << "void attrib_load()\n";
+ attr_load << "{\n";
+ attr_load << ((codegen.attr_load) ? codegen.attr_load : "");
+ attr_load << "}\n\n";
+
+ std::stringstream vert_gen, frag_gen;
+
+ if (do_fragment_attrib_load) {
+ frag_gen << global_vars.str() << attr_load.str();
+ }
+ else {
+ vert_gen << global_vars.str() << attr_load.str();
+ }
+
+ {
+ /* Only mesh and curves support displacement for now. */
+ if (ELEM(geometry_type, MAT_GEOM_MESH, MAT_GEOM_CURVES)) {
+ vert_gen << "vec3 nodetree_displacement()\n";
+ vert_gen << "{\n";
+ vert_gen << ((codegen.displacement) ? codegen.displacement : "return vec3(0);\n");
+ vert_gen << "}\n\n";
+ }
+
+ info.vertex_source_generated = vert_gen.str();
+ }
+
+ {
+ frag_gen << ((codegen.material_functions) ? codegen.material_functions : "\n");
+
+ if (codegen.displacement) {
+ /* Bump displacement. Needed to recompute normals after displacement. */
+ info.define("MAT_DISPLACEMENT_BUMP");
+
+ frag_gen << "vec3 nodetree_displacement()\n";
+ frag_gen << "{\n";
+ frag_gen << codegen.displacement;
+ frag_gen << "}\n\n";
+ }
+
+ frag_gen << "Closure nodetree_surface()\n";
+ frag_gen << "{\n";
+ frag_gen << " closure_weights_reset();\n";
+ frag_gen << ((codegen.surface) ? codegen.surface : "return Closure(0);\n");
+ frag_gen << "}\n\n";
+
+ frag_gen << "Closure nodetree_volume()\n";
+ frag_gen << "{\n";
+ frag_gen << " closure_weights_reset();\n";
+ frag_gen << ((codegen.volume) ? codegen.volume : "return Closure(0);\n");
+ frag_gen << "}\n\n";
+
+ frag_gen << "float nodetree_thickness()\n";
+ frag_gen << "{\n";
+ /* TODO(fclem): Better default. */
+ frag_gen << ((codegen.thickness) ? codegen.thickness : "return 0.1;\n");
+ frag_gen << "}\n\n";
+
+ info.fragment_source_generated = frag_gen.str();
+ }
+
+ /* Geometry Info. */
+ switch (geometry_type) {
+ case MAT_GEOM_WORLD:
+ info.additional_info("eevee_geom_world");
+ break;
+ case MAT_GEOM_VOLUME:
+ info.additional_info("eevee_geom_volume");
+ break;
+ case MAT_GEOM_GPENCIL:
+ info.additional_info("eevee_geom_gpencil");
+ break;
+ case MAT_GEOM_CURVES:
+ info.additional_info("eevee_geom_hair");
+ break;
+ case MAT_GEOM_MESH:
+ default:
+ info.additional_info("eevee_geom_mesh");
+ break;
+ }
+
+ /* Pipeline Info. */
+ switch (geometry_type) {
+ case MAT_GEOM_WORLD:
+ info.additional_info("eevee_surf_world");
+ break;
+ case MAT_GEOM_VOLUME:
+ break;
+ default:
+ switch (pipeline_type) {
+ case MAT_PIPE_FORWARD_PREPASS:
+ case MAT_PIPE_DEFERRED_PREPASS:
+ case MAT_PIPE_SHADOW:
+ info.additional_info("eevee_surf_depth");
+ break;
+ case MAT_PIPE_DEFERRED:
+ info.additional_info("eevee_surf_deferred");
+ break;
+ case MAT_PIPE_FORWARD:
+ info.additional_info("eevee_surf_forward");
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+ break;
+ }
+}
+
+/* WATCH: This can be called from another thread! Needs to not touch the shader module in any
+ * thread unsafe manner. */
+static void codegen_callback(void *thunk, GPUMaterial *mat, GPUCodegenOutput *codegen)
+{
+ reinterpret_cast<ShaderModule *>(thunk)->material_create_info_ammend(mat, codegen);
+}
+
+GPUMaterial *ShaderModule::material_shader_get(::Material *blender_mat,
+ struct bNodeTree *nodetree,
+ eMaterialPipeline pipeline_type,
+ eMaterialGeometry geometry_type,
+ bool deferred_compilation)
+{
+ uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
+
+ bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
+
+ return DRW_shader_from_material(
+ blender_mat, nodetree, shader_uuid, is_volume, deferred_compilation, codegen_callback, this);
+}
+
+GPUMaterial *ShaderModule::world_shader_get(::World *blender_world, struct bNodeTree *nodetree)
+{
+ eMaterialPipeline pipeline_type = MAT_PIPE_DEFERRED; /* Unused. */
+ eMaterialGeometry geometry_type = MAT_GEOM_WORLD;
+
+ uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
+
+ bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
+ bool deferred_compilation = false;
+
+ return DRW_shader_from_world(blender_world,
+ nodetree,
+ shader_uuid,
+ is_volume,
+ deferred_compilation,
+ codegen_callback,
+ this);
+}
+
+/* Variation to compile a material only with a nodetree. Caller needs to maintain the list of
+ * materials and call GPU_material_free on it to update the material. */
+GPUMaterial *ShaderModule::material_shader_get(const char *name,
+ ListBase &materials,
+ struct bNodeTree *nodetree,
+ eMaterialPipeline pipeline_type,
+ eMaterialGeometry geometry_type,
+ bool is_lookdev)
+{
+ uint64_t shader_uuid = shader_uuid_from_material_type(pipeline_type, geometry_type);
+
+ bool is_volume = (pipeline_type == MAT_PIPE_VOLUME);
+
+ GPUMaterial *gpumat = GPU_material_from_nodetree(nullptr,
+ nullptr,
+ nodetree,
+ &materials,
+ name,
+ shader_uuid,
+ is_volume,
+ is_lookdev,
+ codegen_callback,
+ this);
+ GPU_material_status_set(gpumat, GPU_MAT_QUEUED);
+ GPU_material_compile(gpumat);
+ return gpumat;
+}
+
+/** \} */
+
+} // namespace blender::eevee