[Bf-blender-cvs] [eafec6d4f72] blender2.8: GPUShader: Manually validate sampler count

Clément Foucault noreply at git.blender.org
Tue Sep 18 14:17:27 CEST 2018


Commit: eafec6d4f72b05c389576358b9e1d0ebd87cf174
Author: Clément Foucault
Date:   Tue Sep 18 14:22:42 2018 +0200
Branches: blender2.8
https://developer.blender.org/rBeafec6d4f72b05c389576358b9e1d0ebd87cf174

GPUShader: Manually validate sampler count

This happens on NVidia GPUs, using more textures than the maximum allowed
by the gl will NOT trigger a linking issue (maybe because of bindless
texture implementation?).

So in this case we manually count the number of samplers per shader stage
and compare it against the GL limit. We discard the shader if the sampler
count is too high. This shows the user something is wrong with the shader.

===================================================================

M	source/blender/gpu/GPU_extensions.h
M	source/blender/gpu/intern/gpu_codegen.c
M	source/blender/gpu/intern/gpu_extensions.c

===================================================================

diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h
index 23ac95775d7..1059ba06953 100644
--- a/source/blender/gpu/GPU_extensions.h
+++ b/source/blender/gpu/GPU_extensions.h
@@ -41,6 +41,9 @@ extern "C" {
 int GPU_max_texture_size(void);
 int GPU_max_texture_layers(void);
 int GPU_max_textures(void);
+int GPU_max_textures_vert(void);
+int GPU_max_textures_geom(void);
+int GPU_max_textures_frag(void);
 float GPU_max_texture_anisotropy(void);
 int GPU_max_color_texture_samples(void);
 int GPU_max_cube_map_size(void);
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index 7c312f5f36b..db893251149 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -185,7 +185,7 @@ static char *gpu_str_skip_token(char *str, char *token, int max)
 
 	/* skip a variable/function name */
 	while (*str) {
-		if (ELEM(*str, ' ', '(', ')', ',', '\t', '\n', '\r'))
+		if (ELEM(*str, ' ', '(', ')', ',', ';', '\t', '\n', '\r'))
 			break;
 		else {
 			if (token && len < max - 1) {
@@ -203,7 +203,7 @@ static char *gpu_str_skip_token(char *str, char *token, int max)
 	/* skip the next special characters:
 	 * note the missing ')' */
 	while (*str) {
-		if (ELEM(*str, ' ', '(', ',', '\t', '\n', '\r'))
+		if (ELEM(*str, ' ', '(', ',', ';', '\t', '\n', '\r'))
 			str++;
 		else
 			break;
@@ -1764,6 +1764,87 @@ GPUPass *GPU_generate_pass_new(
 	return pass;
 }
 
+static int count_active_texture_sampler(GPUShader *shader, char *source)
+{
+	char *code = source;
+	int samplers_id[64]; /* Remember this is per stage. */
+	int sampler_len = 0;
+
+	while((code = strstr(code, "uniform "))) {
+		/* Move past "uniform". */
+		code += 7;
+		/* Skip following spaces. */
+		while (*code == ' ') { code++; }
+		/* Skip "i" from potential isamplers. */
+		if (*code == 'i') { code++; }
+		/* Skip following spaces. */
+		if (gpu_str_prefix(code, "sampler")) {
+			/* Move past "uniform". */
+			code += 7;
+			/* Skip sampler type suffix. */
+			while (*code != ' ' && *code != '\0') { code++; }
+			/* Skip following spaces. */
+			while (*code == ' ') { code++; }
+
+			if (*code != '\0') {
+				char sampler_name[64];
+				code = gpu_str_skip_token(code, sampler_name, sizeof(sampler_name));
+				int id = GPU_shader_get_uniform(shader, sampler_name);
+
+				if (id == -1) {
+					continue;
+				}
+				/* Catch duplicates. */
+				bool is_duplicate = false;
+				for (int i = 0; i < sampler_len; ++i) {
+					if (samplers_id[i] == id) {
+						is_duplicate = true;
+					}
+				}
+
+				if (!is_duplicate) {
+					samplers_id[sampler_len] = id;
+					sampler_len++;
+				}
+			}
+		}
+	}
+
+	return sampler_len;
+}
+
+static bool gpu_pass_shader_validate(GPUPass *pass)
+{
+	if (pass->shader == NULL) {
+		return false;
+	}
+
+	/* NOTE: The only drawback of this method is that it will count a sampler
+	 * used in the fragment shader and only declared (but not used) in the vertex
+	 * shader as used by both. But this corner case is not happening for now. */
+	int vert_samplers_len = count_active_texture_sampler(pass->shader, pass->vertexcode);
+	int frag_samplers_len = count_active_texture_sampler(pass->shader, pass->fragmentcode);
+
+	int total_samplers_len = vert_samplers_len + frag_samplers_len;
+
+	/* Validate against opengl limit. */
+	if ((frag_samplers_len > GPU_max_textures_frag()) ||
+		(frag_samplers_len > GPU_max_textures_vert()))
+	{
+		return false;
+	}
+
+	if (pass->geometrycode) {
+		int geom_samplers_len = count_active_texture_sampler(pass->shader, pass->geometrycode);
+		total_samplers_len += geom_samplers_len;
+		if (geom_samplers_len > GPU_max_textures_geom()) {
+			return false;
+		}
+	}
+
+	return (total_samplers_len <= GPU_max_textures());
+}
+
 void GPU_pass_compile(GPUPass *pass, const char *shname)
 {
 	if (!pass->compiled) {
@@ -1774,6 +1855,16 @@ void GPU_pass_compile(GPUPass *pass, const char *shname)
 		        NULL,
 		        pass->defines,
 		        shname);
+
+		/* NOTE: Some drivers / gpu allows more active samplers than the opengl limit.
+		 * We need to make sure to count active samplers to avoid undefined behaviour. */
+		if (!gpu_pass_shader_validate(pass)) {
+			if (pass->shader != NULL) {
+				fprintf(stderr, "GPUShader: error: too many samplers in shader.\n");
+				GPU_shader_free(pass->shader);
+			}
+			pass->shader = NULL;
+		}
 		pass->compiled = true;
 	}
 }
diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c
index 112618de92d..198f986d06e 100644
--- a/source/blender/gpu/intern/gpu_extensions.c
+++ b/source/blender/gpu/intern/gpu_extensions.c
@@ -68,6 +68,9 @@ static struct GPUGlobal {
 	GLint maxtexlayers;
 	GLint maxcubemapsize;
 	GLint maxtextures;
+	GLint maxtexturesfrag;
+	GLint maxtexturesgeom;
+	GLint maxtexturesvert;
 	GLint maxubosize;
 	GLint maxubobinds;
 	int colordepth;
@@ -106,6 +109,21 @@ int GPU_max_textures(void)
 	return GG.maxtextures;
 }
 
+int GPU_max_textures_frag(void)
+{
+	return GG.maxtexturesfrag;
+}
+
+int GPU_max_textures_geom(void)
+{
+	return GG.maxtexturesgeom;
+}
+
+int GPU_max_textures_vert(void)
+{
+	return GG.maxtexturesvert;
+}
+
 float GPU_max_texture_anisotropy(void)
 {
 	return GG.max_anisotropy;
@@ -144,7 +162,10 @@ void gpu_extensions_init(void)
 	 */
 	BLI_assert(GLEW_VERSION_3_3);
 
-	glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &GG.maxtextures);
+	glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &GG.maxtexturesfrag);
+	glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &GG.maxtexturesvert);
+	glGetIntegerv(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, &GG.maxtexturesgeom);
+	glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &GG.maxtextures);
 
 	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &GG.maxtexsize);
 	glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &GG.maxtexlayers);
@@ -172,9 +193,7 @@ void gpu_extensions_init(void)
 	glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_FRONT_LEFT, GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, &b);
 	GG.colordepth = r + g + b; /* Assumes same depth for RGB. */
 
-	if (GLEW_VERSION_3_2 || GLEW_ARB_texture_multisample) {
-		glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &GG.samples_color_texture_max);
-	}
+	glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &GG.samples_color_texture_max);
 
 	const char *vendor = (const char *)glGetString(GL_VENDOR);
 	const char *renderer = (const char *)glGetString(GL_RENDERER);



More information about the Bf-blender-cvs mailing list