Merge branch 'master' into blender2.8
[blender.git] / source / blender / gpu / intern / gpu_material.c
index edb6c9a29f9c5a0cddc2e4aee8f0cc4f5fb698ea..2d7b941503094d2ca4caa8a33d22329da16458a9 100644 (file)
 #include "BLI_math.h"
 #include "BLI_blenlib.h"
 #include "BLI_utildefines.h"
+#include "BLI_rand.h"
 
 #include "BKE_anim.h"
 #include "BKE_colorband.h"
 #include "BKE_colortools.h"
 #include "BKE_global.h"
 #include "BKE_image.h"
+#include "BKE_layer.h"
 #include "BKE_main.h"
 #include "BKE_node.h"
 #include "BKE_scene.h"
 
 #include "GPU_extensions.h"
 #include "GPU_framebuffer.h"
+#include "GPU_lamp.h"
 #include "GPU_material.h"
 #include "GPU_shader.h"
 #include "GPU_texture.h"
+#include "GPU_uniformbuffer.h"
 
 #include "gpu_codegen.h"
+#include "gpu_lamp_private.h"
 
 #ifdef WITH_OPENSUBDIV
 #  include "BKE_DerivedMesh.h"
@@ -92,12 +97,15 @@ static struct GPUWorld {
 } GPUWorld;
 
 struct GPUMaterial {
-       Scene *scene;
+       Scene *scene; /* DEPRECATED was only usefull for lamps */
        Material *ma;
 
        /* material for mesh surface, worlds or something else.
         * some code generation is done differently depending on the use case */
-       int type;
+       int type; /* DEPRECATED */
+
+       const void *engine_type;   /* attached engine type */
+       int options;    /* to identify shader variations (shadow, probe, world background...) */
        
        /* for creating the material */
        ListBase nodes;
@@ -128,50 +136,26 @@ struct GPUMaterial {
        bool bound;
 
        bool is_opensubdiv;
-};
-
-struct GPULamp {
-       Scene *scene;
-       Object *ob;
-       Object *par;
-       Lamp *la;
-
-       int type, mode, lay, hide;
-
-       float dynenergy, dyncol[3];
-       float energy, col[3];
-
-       float co[3], vec[3];
-       float dynco[3], dynvec[3];
-       float obmat[4][4];
-       float imat[4][4];
-       float dynimat[4][4];
-
-       float spotsi, spotbl, k;
-       float spotvec[2];
-       float dyndist, dynatt1, dynatt2;
-       float dist, att1, att2;
-       float coeff_const, coeff_lin, coeff_quad;
-       float shadow_color[3];
-
-       float bias, d, clipend;
-       int size;
-
-       int falloff_type;
-       struct CurveMapping *curfalloff;
 
-       float winmat[4][4];
-       float viewmat[4][4];
-       float persmat[4][4];
-       float dynpersmat[4][4];
-
-       GPUFrameBuffer *fb;
-       GPUFrameBuffer *blurfb;
-       GPUTexture *tex;
-       GPUTexture *depthtex;
-       GPUTexture *blurtex;
+       /* XXX: Should be in Material. But it depends on the output node
+        * used and since the output selection is difference for GPUMaterial...
+        */
+       int domain;
+
+       GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */
+       GPUUniformBuffer *sss_profile; /* UBO containing SSS profile. */
+       GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */
+       float *sss_radii; /* UBO containing SSS profile. */
+       int sss_samples;
+       short int *sss_falloff;
+       float *sss_sharpness;
+       bool sss_dirty;
+};
 
-       ListBase materials;
+enum {
+       GPU_DOMAIN_SURFACE    = (1 << 0),
+       GPU_DOMAIN_VOLUME     = (1 << 1),
+       GPU_DOMAIN_SSS        = (1 << 2)
 };
 
 /* Forward declaration so shade_light_textures() can use this, while still keeping the code somewhat organized */
@@ -289,21 +273,18 @@ void GPU_material_free(ListBase *gpumaterial)
                if (material->pass)
                        GPU_pass_free(material->pass);
 
-               for (LinkData *nlink = material->lamps.first; nlink; nlink = nlink->next) {
-                       GPULamp *lamp = nlink->data;
+               if (material->ubo != NULL) {
+                       GPU_uniformbuffer_free(material->ubo);
+               }
 
-                       if (material->ma) {
-                               Material *ma = material->ma;
-                               
-                               LinkData *next = NULL;
-                               for (LinkData *mlink = lamp->materials.first; mlink; mlink = next) {
-                                       next = mlink->next;
-                                       if (mlink->data == ma)
-                                               BLI_freelinkN(&lamp->materials, mlink);
-                               }
-                       }
+               if (material->sss_tex_profile != NULL) {
+                       GPU_texture_free(material->sss_tex_profile);
                }
-               
+
+               if (material->sss_profile != NULL) {
+                       GPU_uniformbuffer_free(material->sss_profile);
+               }
+
                BLI_freelistN(&material->lamps);
 
                MEM_freeN(material);
@@ -312,28 +293,12 @@ void GPU_material_free(ListBase *gpumaterial)
        BLI_freelistN(gpumaterial);
 }
 
-bool GPU_lamp_visible(GPULamp *lamp, SceneRenderLayer *srl, Material *ma)
-{
-       if (lamp->hide)
-               return false;
-       else if (srl && srl->light_override)
-               return BKE_group_object_exists(srl->light_override, lamp->ob);
-       else if (ma && ma->group)
-               return BKE_group_object_exists(ma->group, lamp->ob);
-       else
-               return true;
-}
-
 void GPU_material_bind(
         GPUMaterial *material, int oblay, int viewlay, double time, int mipmap,
-        float viewmat[4][4], float viewinv[4][4], float camerafactors[4], bool scenelock)
+        float viewmat[4][4], float viewinv[4][4], float camerafactors[4])
 {
        if (material->pass) {
                GPUShader *shader = GPU_pass_shader(material->pass);
-               SceneRenderLayer *srl = scenelock ? BLI_findlink(&material->scene->r.layers, material->scene->r.actlay) : NULL;
-
-               if (srl)
-                       viewlay &= srl->lay;
 
                /* handle layer lamps */
                if (material->type == GPU_MATERIAL_TYPE_MESH) {
@@ -341,7 +306,7 @@ void GPU_material_bind(
                                GPULamp *lamp = nlink->data;
                                
                                if ((lamp->lay & viewlay) && (!(lamp->mode & LA_LAYER) || (lamp->lay & oblay)) &&
-                                   GPU_lamp_visible(lamp, srl, material->ma))
+                                   GPU_lamp_visible(lamp, material->ma))
                                {
                                        lamp->dynenergy = lamp->energy;
                                        copy_v3_v3(lamp->dyncol, lamp->col);
@@ -490,6 +455,350 @@ GPUMatType GPU_Material_get_type(GPUMaterial *material)
        return material->type;
 }
 
+GPUPass *GPU_material_get_pass(GPUMaterial *material)
+{
+       return material->pass;
+}
+
+GPUUniformBuffer *GPU_material_get_uniform_buffer(GPUMaterial *material)
+{
+       return material->ubo;
+}
+
+/**
+ * Create dynamic UBO from parameters
+ * \param ListBase of BLI_genericNodeN(GPUInput)
+ */
+void GPU_material_create_uniform_buffer(GPUMaterial *material, ListBase *inputs)
+{
+       material->ubo = GPU_uniformbuffer_dynamic_create(inputs, NULL);
+}
+
+void GPU_material_uniform_buffer_tag_dirty(ListBase *gpumaterials)
+{
+       for (LinkData *link = gpumaterials->first; link; link = link->next) {
+               GPUMaterial *material = link->data;
+               if (material->ubo != NULL) {
+                       GPU_uniformbuffer_tag_dirty(material->ubo);
+               }
+               if (material->sss_profile != NULL) {
+                       material->sss_dirty = true;
+               }
+       }
+}
+
+/* Eevee Subsurface scattering. */
+/* Based on Separable SSS. by Jorge Jimenez and Diego Gutierrez */
+
+#define SSS_SAMPLES 65
+#define SSS_EXPONENT 2.0f /* Importance sampling exponent */
+
+typedef struct GPUSssKernelData {
+       float kernel[SSS_SAMPLES][4];
+       float param[3], max_radius;
+       int samples;
+} GPUSssKernelData;
+
+static void sss_calculate_offsets(GPUSssKernelData *kd, int count, float exponent)
+{
+       float step = 2.0f / (float)(count - 1);
+       for (int i = 0; i < count; i++) {
+               float o = ((float)i) * step - 1.0f;
+               float sign = (o < 0.0f) ? -1.0f : 1.0f;
+               float ofs = sign * fabsf(powf(o, exponent));
+               kd->kernel[i][3] = ofs;
+       }
+}
+
+#define GAUSS_TRUNCATE 12.46f
+static float gaussian_profile(float r, float radius)
+{
+       const float v = radius * radius * (0.25f * 0.25f);
+       const float Rm = sqrtf(v * GAUSS_TRUNCATE);
+
+       if (r >= Rm) {
+               return 0.0f;
+       }
+       return expf(-r * r / (2.0f * v)) / (2.0f * M_PI * v);
+}
+
+#define BURLEY_TRUNCATE     16.0f
+#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE)
+static float burley_profile(float r, float d)
+{
+       float exp_r_3_d = expf(-r / (3.0f * d));
+       float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d;
+       return (exp_r_d + exp_r_3_d) / (4.0f * d);
+}
+
+static float cubic_profile(float r, float radius, float sharpness)
+{
+       float Rm = radius * (1.0f + sharpness);
+
+       if (r >= Rm) {
+               return 0.0f;
+       }
+       /* custom variation with extra sharpness, to match the previous code */
+       const float y = 1.0f / (1.0f + sharpness);
+       float Rmy, ry, ryinv;
+
+       Rmy = powf(Rm, y);
+       ry = powf(r, y);
+       ryinv = (r > 0.0f) ? powf(r, y - 1.0f) : 0.0f;
+
+       const float Rmy5 = (Rmy * Rmy) * (Rmy * Rmy) * Rmy;
+       const float f = Rmy - ry;
+       const float num = f * (f * f) * (y * ryinv);
+
+       return (10.0f * num) / (Rmy5 * M_PI);
+}
+
+static float eval_profile(float r, short falloff_type, float sharpness, float param)
+{
+       r = fabsf(r);
+
+       if (falloff_type == SHD_SUBSURFACE_BURLEY ||
+           falloff_type == SHD_SUBSURFACE_RANDOM_WALK)
+       {
+               return burley_profile(r, param) / BURLEY_TRUNCATE_CDF;
+       }
+       else if (falloff_type == SHD_SUBSURFACE_CUBIC) {
+               return cubic_profile(r, param, sharpness);
+       }
+       else {
+               return gaussian_profile(r, param);
+       }
+}
+
+/* Resolution for each sample of the precomputed kernel profile */
+#define INTEGRAL_RESOLUTION 32
+static float eval_integral(float x0, float x1, short falloff_type, float sharpness, float param)
+{
+       const float range = x1 - x0;
+       const float step = range / INTEGRAL_RESOLUTION;
+       float integral = 0.0f;
+
+       for (int i = 0; i < INTEGRAL_RESOLUTION; ++i) {
+               float x = x0 + range * ((float)i + 0.5f) / (float)INTEGRAL_RESOLUTION;
+               float y = eval_profile(x, falloff_type, sharpness, param);
+               integral += y * step;
+       }
+
+       return integral;
+}
+#undef INTEGRAL_RESOLUTION
+
+static void compute_sss_kernel(
+        GPUSssKernelData *kd, float *radii, int sample_ct, int falloff_type, float sharpness)
+{
+       float rad[3];
+       /* Minimum radius */
+       rad[0] = MAX2(radii[0], 1e-15f);
+       rad[1] = MAX2(radii[1], 1e-15f);
+       rad[2] = MAX2(radii[2], 1e-15f);
+
+       /* Christensen-Burley fitting */
+       float l[3], d[3];
+
+       if (falloff_type == SHD_SUBSURFACE_BURLEY ||
+           falloff_type == SHD_SUBSURFACE_RANDOM_WALK)
+       {
+               mul_v3_v3fl(l, rad, 0.25f * M_1_PI);
+               const float A = 1.0f;
+               const float s = 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f);
+               /* XXX 0.6f Out of nowhere to match cycles! Empirical! Can be tweak better. */
+               mul_v3_v3fl(d, l, 0.6f / s);
+               mul_v3_v3fl(rad, d, BURLEY_TRUNCATE);
+               kd->max_radius = MAX3(rad[0], rad[1], rad[2]);
+
+               copy_v3_v3(kd->param, d);
+       }
+       else if (falloff_type == SHD_SUBSURFACE_CUBIC) {
+               copy_v3_v3(kd->param, rad);
+               mul_v3_fl(rad, 1.0f + sharpness);
+               kd->max_radius = MAX3(rad[0], rad[1], rad[2]);
+       }
+       else {
+               kd->max_radius = MAX3(rad[0], rad[1], rad[2]);
+
+               copy_v3_v3(kd->param, rad);
+       }
+
+       /* Compute samples locations on the 1d kernel [-1..1] */
+       sss_calculate_offsets(kd, sample_ct, SSS_EXPONENT);
+
+       /* Weights sum for normalization */
+       float sum[3] = {0.0f, 0.0f, 0.0f};
+
+       /* Compute integral of each sample footprint */
+       for (int i = 0; i < sample_ct; i++) {
+               float x0, x1;
+
+               if (i == 0) {
+                       x0 = kd->kernel[0][3] - fabsf(kd->kernel[0][3] - kd->kernel[1][3]) / 2.0f;
+               }
+               else {
+                       x0 = (kd->kernel[i - 1][3] + kd->kernel[i][3]) / 2.0f;
+               }
+
+               if (i == sample_ct - 1) {
+                       x1 = kd->kernel[sample_ct - 1][3] + fabsf(kd->kernel[sample_ct - 2][3] - kd->kernel[sample_ct - 1][3]) / 2.0f;
+               }
+               else {
+                       x1 = (kd->kernel[i][3] + kd->kernel[i + 1][3]) / 2.0f;
+               }
+
+               x0 *= kd->max_radius;
+               x1 *= kd->max_radius;
+
+               kd->kernel[i][0] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[0]);
+               kd->kernel[i][1] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[1]);
+               kd->kernel[i][2] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[2]);
+
+               sum[0] += kd->kernel[i][0];
+               sum[1] += kd->kernel[i][1];
+               sum[2] += kd->kernel[i][2];
+       }
+
+       for (int i = 0; i < 3; ++i) {
+               if (sum[i] > 0.0f) {
+                       /* Normalize */
+                       for (int j = 0; j < sample_ct; j++) {
+                               kd->kernel[j][i] /= sum[i];
+                       }
+               }
+               else {
+                       /* Avoid 0 kernel sum. */
+                       kd->kernel[sample_ct / 2][i] = 1.0f;
+               }
+       }
+
+       /* Put center sample at the start of the array (to sample first) */
+       float tmpv[4];
+       copy_v4_v4(tmpv, kd->kernel[sample_ct / 2]);
+       for (int i = sample_ct / 2; i > 0; i--) {
+               copy_v4_v4(kd->kernel[i], kd->kernel[i - 1]);
+       }
+       copy_v4_v4(kd->kernel[0], tmpv);
+
+       kd->samples = sample_ct;
+}
+
+#define INTEGRAL_RESOLUTION 512
+static void compute_sss_translucence_kernel(
+        const GPUSssKernelData *kd, int resolution, short falloff_type, float sharpness, float **output)
+{
+       float (*texels)[4];
+       texels = MEM_callocN(sizeof(float) * 4 * resolution, "compute_sss_translucence_kernel");
+       *output = (float *)texels;
+
+       /* Last texel should be black, hence the - 1. */
+       for (int i = 0; i < resolution - 1; ++i) {
+               /* Distance from surface. */
+               float d = kd->max_radius * ((float)i + 0.00001f) / ((float)resolution);
+
+               /* For each distance d we compute the radiance incomming from an hypothetic parallel plane. */
+               /* Compute radius of the footprint on the hypothetic plane */
+               float r_fp = sqrtf(kd->max_radius * kd->max_radius - d * d);
+               float r_step = r_fp / INTEGRAL_RESOLUTION;
+               float area_accum = 0.0f;
+               for (float r = 0.0f; r < r_fp; r += r_step) {
+                       /* Compute distance to the "shading" point through the medium. */
+                       /* r_step * 0.5f to put sample between the area borders */
+                       float dist = hypotf(r + r_step * 0.5f, d);
+
+                       float profile[3];
+                       profile[0] = eval_profile(dist, falloff_type, sharpness, kd->param[0]);
+                       profile[1] = eval_profile(dist, falloff_type, sharpness, kd->param[1]);
+                       profile[2] = eval_profile(dist, falloff_type, sharpness, kd->param[2]);
+
+                       /* Since the profile and configuration are radially symetrical we
+                        * can just evaluate it once and weight it accordingly */
+                       float r_next = r + r_step;
+                       float disk_area = (M_PI * r_next * r_next) - (M_PI * r * r);
+
+                       mul_v3_fl(profile, disk_area);
+                       add_v3_v3(texels[i], profile);
+                       area_accum += disk_area;
+               }
+               /* Normalize over the disk. */
+               mul_v3_fl(texels[i], 1.0f / (area_accum));
+       }
+
+       /* Normalize */
+       for (int j = resolution - 2; j > 0; j--) {
+               texels[j][0] /= (texels[0][0] > 0.0f) ? texels[0][0] : 1.0f;
+               texels[j][1] /= (texels[0][1] > 0.0f) ? texels[0][1] : 1.0f;
+               texels[j][2] /= (texels[0][2] > 0.0f) ? texels[0][2] : 1.0f;
+       }
+
+       /* First texel should be white */
+       texels[0][0] = (texels[0][0] > 0.0f) ? 1.0f : 0.0f;
+       texels[0][1] = (texels[0][1] > 0.0f) ? 1.0f : 0.0f;
+       texels[0][2] = (texels[0][2] > 0.0f) ? 1.0f : 0.0f;
+
+       /* dim the last few texels for smoother transition */
+       mul_v3_fl(texels[resolution - 2], 0.25f);
+       mul_v3_fl(texels[resolution - 3], 0.5f);
+       mul_v3_fl(texels[resolution - 4], 0.75f);
+}
+#undef INTEGRAL_RESOLUTION
+
+void GPU_material_sss_profile_create(GPUMaterial *material, float *radii, short *falloff_type, float *sharpness)
+{
+       material->sss_radii = radii;
+       material->sss_falloff = falloff_type;
+       material->sss_sharpness = sharpness;
+       material->sss_dirty = true;
+
+       /* Update / Create UBO */
+       if (material->sss_profile == NULL) {
+               material->sss_profile = GPU_uniformbuffer_create(sizeof(GPUSssKernelData), NULL, NULL);
+       }
+}
+
+struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, int sample_ct, GPUTexture **tex_profile)
+{
+       if (material->sss_radii == NULL)
+               return NULL;
+
+       if (material->sss_dirty || (material->sss_samples != sample_ct)) {
+               GPUSssKernelData kd;
+
+               float sharpness = (material->sss_sharpness != NULL) ? *material->sss_sharpness : 0.0f;
+
+               /* XXX Black magic but it seems to fit. Maybe because we integrate -1..1 */
+               sharpness *= 0.5f;
+
+               compute_sss_kernel(&kd, material->sss_radii, sample_ct, *material->sss_falloff, sharpness);
+
+               /* Update / Create UBO */
+               GPU_uniformbuffer_update(material->sss_profile, &kd);
+
+               /* Update / Create Tex */
+               float *translucence_profile;
+               compute_sss_translucence_kernel(&kd, 64, *material->sss_falloff, sharpness, &translucence_profile);
+
+               if (material->sss_tex_profile != NULL) {
+                       GPU_texture_free(material->sss_tex_profile);
+               }
+
+               material->sss_tex_profile = GPU_texture_create_1D_custom(64, 4, GPU_RGBA16F, translucence_profile, NULL);
+
+               MEM_freeN(translucence_profile);
+
+               material->sss_samples = sample_ct;
+               material->sss_dirty = false;
+       }
+
+       if (tex_profile != NULL) {
+               *tex_profile = material->sss_tex_profile;
+       }
+       return material->sss_profile;
+}
+
+#undef SSS_EXPONENT
+#undef SSS_SAMPLES
 
 void GPU_material_vertex_attributes(GPUMaterial *material, GPUVertexAttribs *attribs)
 {
@@ -540,6 +849,16 @@ bool GPU_material_use_world_space_shading(GPUMaterial *mat)
        return BKE_scene_use_world_space_shading(mat->scene);
 }
 
+bool GPU_material_use_domain_surface(GPUMaterial *mat)
+{
+       return (mat->domain & GPU_DOMAIN_SURFACE);
+}
+
+bool GPU_material_use_domain_volume(GPUMaterial *mat)
+{
+       return (mat->domain & GPU_DOMAIN_VOLUME);
+}
+
 static GPUNodeLink *lamp_get_visibility(GPUMaterial *mat, GPULamp *lamp, GPUNodeLink **lv, GPUNodeLink **dist)
 {
        GPUNodeLink *visifac;
@@ -951,14 +1270,12 @@ static void shade_one_light(GPUShadeInput *shi, GPUShadeResult *shr, GPULamp *la
                                }
                                
                                add_user_list(&mat->lamps, lamp);
-                               add_user_list(&lamp->materials, shi->gpumat->ma);
                                return;
                        }
                }
        }
        else if ((mat->scene->gm.flag & GAME_GLSL_NO_SHADOWS) && (lamp->mode & LA_ONLYSHADOW)) {
                add_user_list(&mat->lamps, lamp);
-               add_user_list(&lamp->materials, shi->gpumat->ma);
                return;
        }
        else
@@ -1025,7 +1342,6 @@ static void shade_one_light(GPUShadeInput *shi, GPUShadeResult *shr, GPULamp *la
        }
 
        add_user_list(&mat->lamps, lamp);
-       add_user_list(&lamp->materials, shi->gpumat->ma);
 }
 
 static void material_lights(GPUShadeInput *shi, GPUShadeResult *shr)
@@ -2150,6 +2466,73 @@ GPUMaterial *GPU_material_world(struct Scene *scene, struct World *wo)
        return mat;
 }
 
+GPUMaterial *GPU_material_from_nodetree_find(
+        ListBase *gpumaterials, const void *engine_type, int options)
+{
+       for (LinkData *link = gpumaterials->first; link; link = link->next) {
+               GPUMaterial *current_material = (GPUMaterial *)link->data;
+               if (current_material->engine_type == engine_type &&
+                   current_material->options == options)
+               {
+                       return current_material;
+               }
+       }
+
+       return NULL;
+}
+
+/**
+ * TODO: This is supposed to replace GPU_material_from_blender/_world in the future
+ *
+ * \note Caller must use #GPU_material_from_nodetree_find to re-use existing materials,
+ * This is enforced since constructing other arguments to this function may be expensive
+ * so only do this when they are needed.
+ */
+GPUMaterial *GPU_material_from_nodetree(
+        Scene *scene, struct bNodeTree *ntree, ListBase *gpumaterials, const void *engine_type, int options,
+        const char *vert_code, const char *geom_code, const char *frag_lib, const char *defines)
+{
+       GPUMaterial *mat;
+       GPUNodeLink *outlink;
+       LinkData *link;
+       bool has_volume_output, has_surface_output;
+
+       /* Caller must re-use materials. */
+       BLI_assert(GPU_material_from_nodetree_find(gpumaterials, engine_type, options) == NULL);
+
+       /* allocate material */
+       mat = GPU_material_construct_begin(NULL); /* TODO remove GPU_material_construct_begin */
+       mat->scene = scene;
+       mat->engine_type = engine_type;
+       mat->options = options;
+
+       ntreeGPUMaterialNodes(ntree, mat, NODE_NEW_SHADING | NODE_NEWER_SHADING);
+       ntreeGPUMaterialDomain(ntree, &has_surface_output, &has_volume_output);
+
+       if (has_surface_output) {
+               mat->domain |= GPU_DOMAIN_SURFACE;
+       }
+       if (has_volume_output) {
+               mat->domain |= GPU_DOMAIN_VOLUME;
+       }
+
+       /* Let Draw manager finish the construction. */
+       if (mat->outlink) {
+               outlink = mat->outlink;
+               mat->pass = GPU_generate_pass_new(
+                       mat, &mat->nodes, outlink, &mat->attribs, vert_code, geom_code, frag_lib, defines);
+       }
+
+       /* note that even if building the shader fails in some way, we still keep
+        * it to avoid trying to compile again and again, and simple do not use
+        * the actual shader on drawing */
+
+       link = MEM_callocN(sizeof(LinkData), "GPUMaterialLink");
+       link->data = mat;
+       BLI_addtail(gpumaterials, link);
+
+       return mat;
+}
 
 GPUMaterial *GPU_material_from_blender(Scene *scene, Material *ma, bool use_opensubdiv)
 {
@@ -2237,386 +2620,6 @@ void GPU_materials_free(void)
 
 /* Lamps and shadow buffers */
 
-static void gpu_lamp_calc_winmat(GPULamp *lamp)
-{
-       float temp, angle, pixsize, wsize;
-
-       if (lamp->type == LA_SUN) {
-               wsize = lamp->la->shadow_frustum_size;
-               orthographic_m4(lamp->winmat, -wsize, wsize, -wsize, wsize, lamp->d, lamp->clipend);
-       }
-       else if (lamp->type == LA_SPOT) {
-               angle = saacos(lamp->spotsi);
-               temp = 0.5f * lamp->size * cosf(angle) / sinf(angle);
-               pixsize = lamp->d / temp;
-               wsize = pixsize * 0.5f * lamp->size;
-               /* compute shadows according to X and Y scaling factors */
-               perspective_m4(
-                       lamp->winmat,
-                       -wsize * lamp->spotvec[0], wsize * lamp->spotvec[0],
-                       -wsize * lamp->spotvec[1], wsize * lamp->spotvec[1],
-                       lamp->d, lamp->clipend);
-       }
-}
-
-void GPU_lamp_update(GPULamp *lamp, int lay, int hide, float obmat[4][4])
-{
-       float mat[4][4];
-       float obmat_scale[3];
-
-       lamp->lay = lay;
-       lamp->hide = hide;
-
-       normalize_m4_m4_ex(mat, obmat, obmat_scale);
-
-       copy_v3_v3(lamp->vec, mat[2]);
-       copy_v3_v3(lamp->co, mat[3]);
-       copy_m4_m4(lamp->obmat, mat);
-       invert_m4_m4(lamp->imat, mat);
-
-       if (lamp->type == LA_SPOT) {
-               /* update spotlamp scale on X and Y axis */
-               lamp->spotvec[0] = obmat_scale[0] / obmat_scale[2];
-               lamp->spotvec[1] = obmat_scale[1] / obmat_scale[2];
-       }
-
-       if (GPU_lamp_has_shadow_buffer(lamp)) {
-               /* makeshadowbuf */
-               gpu_lamp_calc_winmat(lamp);
-       }
-}
-
-void GPU_lamp_update_colors(GPULamp *lamp, float r, float g, float b, float energy)
-{
-       lamp->energy = energy;
-       if (lamp->mode & LA_NEG) lamp->energy = -lamp->energy;
-
-       lamp->col[0] = r;
-       lamp->col[1] = g;
-       lamp->col[2] = b;
-}
-
-void GPU_lamp_update_distance(GPULamp *lamp, float distance, float att1, float att2,
-                              float coeff_const, float coeff_lin, float coeff_quad)
-{
-       lamp->dist = distance;
-       lamp->att1 = att1;
-       lamp->att2 = att2;
-       lamp->coeff_const = coeff_const;
-       lamp->coeff_lin = coeff_lin;
-       lamp->coeff_quad = coeff_quad;
-}
-
-void GPU_lamp_update_spot(GPULamp *lamp, float spotsize, float spotblend)
-{
-       lamp->spotsi = cosf(spotsize * 0.5f);
-       lamp->spotbl = (1.0f - lamp->spotsi) * spotblend;
-}
-
-static void gpu_lamp_from_blender(Scene *scene, Object *ob, Object *par, Lamp *la, GPULamp *lamp)
-{
-       lamp->scene = scene;
-       lamp->ob = ob;
-       lamp->par = par;
-       lamp->la = la;
-
-       /* add_render_lamp */
-       lamp->mode = la->mode;
-       lamp->type = la->type;
-
-       lamp->energy = la->energy;
-       if (lamp->mode & LA_NEG) lamp->energy = -lamp->energy;
-
-       lamp->col[0] = la->r;
-       lamp->col[1] = la->g;
-       lamp->col[2] = la->b;
-
-       GPU_lamp_update(lamp, ob->lay, (ob->restrictflag & OB_RESTRICT_RENDER), ob->obmat);
-
-       lamp->spotsi = la->spotsize;
-       if (lamp->mode & LA_HALO)
-               if (lamp->spotsi > DEG2RADF(170.0f))
-                       lamp->spotsi = DEG2RADF(170.0f);
-       lamp->spotsi = cosf(lamp->spotsi * 0.5f);
-       lamp->spotbl = (1.0f - lamp->spotsi) * la->spotblend;
-       lamp->k = la->k;
-
-       lamp->dist = la->dist;
-       lamp->falloff_type = la->falloff_type;
-       lamp->att1 = la->att1;
-       lamp->att2 = la->att2;
-       lamp->coeff_const = la->coeff_const;
-       lamp->coeff_lin = la->coeff_lin;
-       lamp->coeff_quad = la->coeff_quad;
-       lamp->curfalloff = la->curfalloff;
-
-       /* initshadowbuf */
-       lamp->bias = 0.02f * la->bias;
-       lamp->size = la->bufsize;
-       lamp->d = la->clipsta;
-       lamp->clipend = la->clipend;
-
-       /* arbitrary correction for the fact we do no soft transition */
-       lamp->bias *= 0.25f;
-}
-
-static void gpu_lamp_shadow_free(GPULamp *lamp)
-{
-       if (lamp->tex) {
-               GPU_texture_free(lamp->tex);
-               lamp->tex = NULL;
-       }
-       if (lamp->depthtex) {
-               GPU_texture_free(lamp->depthtex);
-               lamp->depthtex = NULL;
-       }
-       if (lamp->fb) {
-               GPU_framebuffer_free(lamp->fb);
-               lamp->fb = NULL;
-       }
-       if (lamp->blurtex) {
-               GPU_texture_free(lamp->blurtex);
-               lamp->blurtex = NULL;
-       }
-       if (lamp->blurfb) {
-               GPU_framebuffer_free(lamp->blurfb);
-               lamp->blurfb = NULL;
-       }
-}
-
-GPULamp *GPU_lamp_from_blender(Scene *scene, Object *ob, Object *par)
-{
-       Lamp *la;
-       GPULamp *lamp;
-       LinkData *link;
-
-       for (link = ob->gpulamp.first; link; link = link->next) {
-               lamp = (GPULamp *)link->data;
-
-               if (lamp->par == par && lamp->scene == scene)
-                       return link->data;
-       }
-
-       lamp = MEM_callocN(sizeof(GPULamp), "GPULamp");
-
-       link = MEM_callocN(sizeof(LinkData), "GPULampLink");
-       link->data = lamp;
-       BLI_addtail(&ob->gpulamp, link);
-
-       la = ob->data;
-       gpu_lamp_from_blender(scene, ob, par, la, lamp);
-
-       if ((la->type == LA_SPOT && (la->mode & (LA_SHAD_BUF | LA_SHAD_RAY))) ||
-           (la->type == LA_SUN && (la->mode & LA_SHAD_RAY)))
-       {
-               /* opengl */
-               lamp->fb = GPU_framebuffer_create();
-               if (!lamp->fb) {
-                       gpu_lamp_shadow_free(lamp);
-                       return lamp;
-               }
-
-               if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) {
-                       /* Shadow depth map */
-                       lamp->depthtex = GPU_texture_create_depth(lamp->size, lamp->size, NULL);
-                       if (!lamp->depthtex) {
-                               gpu_lamp_shadow_free(lamp);
-                               return lamp;
-                       }
-               
-                       if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->depthtex, 0, NULL)) {
-                               gpu_lamp_shadow_free(lamp);
-                               return lamp;
-                       }
-
-                       /* Shadow color map */
-                       lamp->tex = GPU_texture_create_vsm_shadow_map(lamp->size, NULL);
-                       if (!lamp->tex) {
-                               gpu_lamp_shadow_free(lamp);
-                               return lamp;
-                       }
-
-                       if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, NULL)) {
-                               gpu_lamp_shadow_free(lamp);
-                               return lamp;
-                       }
-
-                       if (!GPU_framebuffer_check_valid(lamp->fb, NULL)) {
-                               gpu_lamp_shadow_free(lamp);
-                               return lamp;                            
-                       }
-                       
-                       /* FBO and texture for blurring */
-                       lamp->blurfb = GPU_framebuffer_create();
-                       if (!lamp->blurfb) {
-                               gpu_lamp_shadow_free(lamp);
-                               return lamp;
-                       }
-
-                       lamp->blurtex = GPU_texture_create_vsm_shadow_map(lamp->size * 0.5, NULL);
-                       if (!lamp->blurtex) {
-                               gpu_lamp_shadow_free(lamp);
-                               return lamp;
-                       }
-               
-                       if (!GPU_framebuffer_texture_attach(lamp->blurfb, lamp->blurtex, 0, NULL)) {
-                               gpu_lamp_shadow_free(lamp);
-                               return lamp;
-                       }
-                       
-                       /* we need to properly bind to test for completeness */
-                       GPU_texture_bind_as_framebuffer(lamp->blurtex);
-                       
-                       if (!GPU_framebuffer_check_valid(lamp->blurfb, NULL)) {
-                               gpu_lamp_shadow_free(lamp);
-                               return lamp;
-                       }
-                       
-                       GPU_framebuffer_texture_unbind(lamp->blurfb, lamp->blurtex);
-               }
-               else {
-                       lamp->tex = GPU_texture_create_depth(lamp->size, lamp->size, NULL);
-                       if (!lamp->tex) {
-                               gpu_lamp_shadow_free(lamp);
-                               return lamp;
-                       }
-
-                       if (!GPU_framebuffer_texture_attach(lamp->fb, lamp->tex, 0, NULL)) {
-                               gpu_lamp_shadow_free(lamp);
-                               return lamp;
-                       }
-                       
-                       if (!GPU_framebuffer_check_valid(lamp->fb, NULL)) {
-                               gpu_lamp_shadow_free(lamp);
-                               return lamp;                            
-                       }                                               
-               }
-
-               GPU_framebuffer_restore();
-
-               lamp->shadow_color[0] = la->shdwr;
-               lamp->shadow_color[1] = la->shdwg;
-               lamp->shadow_color[2] = la->shdwb;
-       }
-       else {
-               lamp->shadow_color[0] = 1.0;
-               lamp->shadow_color[1] = 1.0;
-               lamp->shadow_color[2] = 1.0;
-       }
-
-       return lamp;
-}
-
-void GPU_lamp_free(Object *ob)
-{
-       GPULamp *lamp;
-       LinkData *link;
-       LinkData *nlink;
-       Material *ma;
-
-       for (link = ob->gpulamp.first; link; link = link->next) {
-               lamp = link->data;
-
-               while (lamp->materials.first) {
-                       nlink = lamp->materials.first;
-                       ma = nlink->data;
-                       BLI_freelinkN(&lamp->materials, nlink);
-
-                       if (ma->gpumaterial.first)
-                               GPU_material_free(&ma->gpumaterial);
-               }
-
-               gpu_lamp_shadow_free(lamp);
-
-               MEM_freeN(lamp);
-       }
-
-       BLI_freelistN(&ob->gpulamp);
-}
-
-bool GPU_lamp_has_shadow_buffer(GPULamp *lamp)
-{
-       return (!(lamp->scene->gm.flag & GAME_GLSL_NO_SHADOWS) &&
-               !(lamp->scene->gm.flag & GAME_GLSL_NO_LIGHTS) &&
-               lamp->tex && lamp->fb);
-}
-
-void GPU_lamp_update_buffer_mats(GPULamp *lamp)
-{
-       float rangemat[4][4], persmat[4][4];
-
-       /* initshadowbuf */
-       invert_m4_m4(lamp->viewmat, lamp->obmat);
-       normalize_v3(lamp->viewmat[0]);
-       normalize_v3(lamp->viewmat[1]);
-       normalize_v3(lamp->viewmat[2]);
-
-       /* makeshadowbuf */
-       mul_m4_m4m4(persmat, lamp->winmat, lamp->viewmat);
-
-       /* opengl depth buffer is range 0.0..1.0 instead of -1.0..1.0 in blender */
-       unit_m4(rangemat);
-       rangemat[0][0] = 0.5f;
-       rangemat[1][1] = 0.5f;
-       rangemat[2][2] = 0.5f;
-       rangemat[3][0] = 0.5f;
-       rangemat[3][1] = 0.5f;
-       rangemat[3][2] = 0.5f;
-
-       mul_m4_m4m4(lamp->persmat, rangemat, persmat);
-}
-
-void GPU_lamp_shadow_buffer_bind(GPULamp *lamp, float viewmat[4][4], int *winsize, float winmat[4][4])
-{
-       GPU_lamp_update_buffer_mats(lamp);
-
-       /* opengl */
-       glDisable(GL_SCISSOR_TEST);
-       GPU_texture_bind_as_framebuffer(lamp->tex);
-       if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE)
-               GPU_shader_bind(GPU_shader_get_builtin_shader(GPU_SHADER_VSM_STORE));
-
-       /* set matrices */
-       copy_m4_m4(viewmat, lamp->viewmat);
-       copy_m4_m4(winmat, lamp->winmat);
-       *winsize = lamp->size;
-}
-
-void GPU_lamp_shadow_buffer_unbind(GPULamp *lamp)
-{
-       if (lamp->la->shadowmap_type == LA_SHADMAP_VARIANCE) {
-               GPU_shader_unbind();
-               GPU_framebuffer_blur(lamp->fb, lamp->tex, lamp->blurfb, lamp->blurtex);
-       }
-
-       GPU_framebuffer_texture_unbind(lamp->fb, lamp->tex);
-       GPU_framebuffer_restore();
-       glEnable(GL_SCISSOR_TEST);
-}
-
-int GPU_lamp_shadow_buffer_type(GPULamp *lamp)
-{
-       return lamp->la->shadowmap_type;
-}
-
-int GPU_lamp_shadow_bind_code(GPULamp *lamp)
-{
-       return lamp->tex ? GPU_texture_opengl_bindcode(lamp->tex) : -1;
-}
-
-float *GPU_lamp_dynpersmat(GPULamp *lamp)
-{
-       return &lamp->dynpersmat[0][0];
-}
-
-int GPU_lamp_shadow_layer(GPULamp *lamp)
-{
-       if (lamp->fb && lamp->tex && (lamp->mode & (LA_LAYER | LA_LAYER_SHADOW)))
-               return lamp->lay;
-       else
-               return -1;
-}
-
 GPUNodeLink *GPU_lamp_get_data(
         GPUMaterial *mat, GPULamp *lamp,
         GPUNodeLink **r_col, GPUNodeLink **r_lv, GPUNodeLink **r_dist, GPUNodeLink **r_shadow, GPUNodeLink **r_energy)
@@ -2658,7 +2661,6 @@ GPUNodeLink *GPU_lamp_get_data(
 
        /* ensure shadow buffer and lamp textures will be updated */
        add_user_list(&mat->lamps, lamp);
-       add_user_list(&lamp->materials, mat->ma);
 
        return visifac;
 }
@@ -2728,6 +2730,7 @@ GPUShaderExport *GPU_shader_export(struct Scene *scene, struct Material *ma)
                                                break;
 
                                        case GPU_NONE:
+                                       case GPU_TEX3D:
                                        case GPU_TEXCUBE:
                                        case GPU_FLOAT:
                                        case GPU_VEC2:
@@ -2735,6 +2738,7 @@ GPUShaderExport *GPU_shader_export(struct Scene *scene, struct Material *ma)
                                        case GPU_VEC4:
                                        case GPU_MAT3:
                                        case GPU_MAT4:
+                                       case GPU_CLOSURE:
                                        case GPU_ATTRIB:
                                                break;
                                }
@@ -2763,7 +2767,9 @@ GPUShaderExport *GPU_shader_export(struct Scene *scene, struct Material *ma)
                                                break;
 
                                        case GPU_NONE:
+                                       case GPU_CLOSURE:
                                        case GPU_TEX2D:
+                                       case GPU_TEX3D:
                                        case GPU_TEXCUBE:
                                        case GPU_SHADOW2D:
                                        case GPU_ATTRIB: