Selaa lähdekoodia

Replace ARB_separate_shader_objects extension

This replaces the ARB_separate_shader_objects extension with traditional
linked shaders.  I was able to get the existing system to use linked
shaders without having to change any libobs graphics API.

This essentially creates a linked list of shader programs with
references to the shaders they link.  Before draw, it searches that
linked list for a particular pixel/vertex shader pair, and the linked
program associated with it.  If no matching program exists, it creates
the program.
jp9000 11 vuotta sitten
vanhempi
sitoutus
3967c6354a

+ 309 - 96
libobs-opengl/gl-shader.c

@@ -37,20 +37,25 @@ static inline void shader_param_free(struct gs_shader_param *param)
 	da_free(param->def_value);
 }
 
-static void gl_get_program_info(GLuint program, const char *file,
+static inline void shader_attrib_free(struct shader_attrib *attrib)
+{
+	bfree(attrib->name);
+}
+
+static void gl_get_shader_info(GLuint shader, const char *file,
 		char **error_string)
 {
 	char    *errors;
 	GLint   info_len = 0;
 	GLsizei chars_written = 0;
 
-	glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_len);
+	glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_len);
 	if (!gl_success("glGetProgramiv") || !info_len)
 		return;
 
 	errors = bzalloc(info_len+1);
-	glGetProgramInfoLog(program, info_len, &chars_written, errors);
-	gl_success("glGetProgramInfoLog");
+	glGetShaderInfoLog(shader, info_len, &chars_written, errors);
+	gl_success("glGetShaderInfoLog");
 
 	blog(LOG_DEBUG, "Compiler warnings/errors for %s:\n%s", file, errors);
 
@@ -80,23 +85,8 @@ static bool gl_add_param(struct gs_shader *shader, struct shader_var *var,
 	da_move(param.def_value, var->default_val);
 	da_copy(param.cur_value, param.def_value);
 
-	param.param = glGetUniformLocation(shader->program, param.name);
-	if (!gl_success("glGetUniformLocation"))
-		goto fail;
-
-	if (param.type == GS_SHADER_PARAM_TEXTURE) {
-		glProgramUniform1i(shader->program, param.param,
-				param.texture_id);
-		if (!gl_success("glProgramUniform1i"))
-			goto fail;
-	}
-
 	da_push_back(shader->params, &param);
 	return true;
-
-fail:
-	shader_param_free(&param);
-	return false;
 }
 
 static inline bool gl_add_params(struct gs_shader *shader,
@@ -164,7 +154,7 @@ static void get_attrib_type(const char *mapping, enum attrib_type *type,
 	*index = 0;
 }
 
-static inline bool gl_process_attrib(struct gs_shader *shader,
+static inline bool gl_process_attrib(struct gs_shader *program,
 		struct gl_parser_attrib *pa)
 {
 	struct shader_attrib attrib = {0};
@@ -174,18 +164,13 @@ static inline bool gl_process_attrib(struct gs_shader *shader,
 		return true;
 
 	get_attrib_type(pa->mapping, &attrib.type, &attrib.index);
+	attrib.name = pa->name.array;
 
-	attrib.attrib = glGetAttribLocation(shader->program, pa->name.array);
-	if (!gl_success("glGetAttribLocation"))
-		return false;
+	pa->name.array    = NULL;
+	pa->name.len      = 0;
+	pa->name.capacity = 0;
 
-	if (attrib.attrib == -1) {
-		blog(LOG_ERROR, "glGetAttribLocation: Could not find "
-		                "attribute '%s'", pa->name.array);
-		return false;
-	}
-
-	da_push_back(shader->attribs, &attrib);
+	da_push_back(program->attribs, &attrib);
 	return true;
 }
 
@@ -210,9 +195,17 @@ static bool gl_shader_init(struct gs_shader *shader,
 	int compiled = 0;
 	bool success = true;
 
-	shader->program = glCreateShaderProgramv(type, 1,
-			(const GLchar**)&glsp->gl_string.array);
-	if (!gl_success("glCreateShaderProgramv") || !shader->program)
+	shader->obj = glCreateShader(type);
+	if (!gl_success("glCreateShader") || !shader->obj)
+		return false;
+
+	glShaderSource(shader->obj, 1, (const GLchar**)&glsp->gl_string.array,
+			0);
+	if (!gl_success("glShaderSource"))
+		return false;
+
+	glCompileShader(shader->obj);
+	if (!gl_success("glCompileShader"))
 		return false;
 
 #if 0
@@ -223,14 +216,14 @@ static bool gl_shader_init(struct gs_shader *shader,
 	blog(LOG_DEBUG, "+++++++++++++++++++++++++++++++++++");
 #endif
 
-	glGetProgramiv(shader->program, GL_LINK_STATUS, &compiled);
-	if (!gl_success("glGetProgramiv"))
+	glGetShaderiv(shader->obj, GL_COMPILE_STATUS, &compiled);
+	if (!gl_success("glGetShaderiv"))
 		return false;
 
 	if (!compiled)
 		success = false;
 
-	gl_get_program_info(shader->program, file, error_string);
+	gl_get_shader_info(shader->obj, file, error_string);
 
 	if (success)
 		success = gl_add_params(shader, glsp);
@@ -293,6 +286,29 @@ gs_shader_t device_pixelshader_create(gs_device_t device,
 	return ptr;
 }
 
+static void remove_program_references(struct gs_shader *shader)
+{
+	struct gs_program *program = shader->device->first_program;
+
+	while (program) {
+		struct gs_program *next = program->next;
+		bool destroy = false;
+
+		if (shader->type == GS_SHADER_VERTEX &&
+				program->vertex_shader == shader)
+			destroy = true;
+
+		else if (shader->type == GS_SHADER_PIXEL &&
+				program->pixel_shader == shader)
+			destroy = true;
+
+		if (destroy)
+			gs_program_destroy(program);
+
+		program = next;
+	}
+}
+
 void gs_shader_destroy(gs_shader_t shader)
 {
 	size_t i;
@@ -300,15 +316,20 @@ void gs_shader_destroy(gs_shader_t shader)
 	if (!shader)
 		return;
 
+	remove_program_references(shader);
+
+	for (i = 0; i < shader->attribs.num; i++)
+		shader_attrib_free(shader->attribs.array+i);
+
 	for (i = 0; i < shader->samplers.num; i++)
 		gs_samplerstate_destroy(shader->samplers.array[i]);
 
 	for (i = 0; i < shader->params.num; i++)
 		shader_param_free(shader->params.array+i);
 
-	if (shader->program) {
-		glDeleteProgram(shader->program);
-		gl_success("glDeleteProgram");
+	if (shader->obj) {
+		glDeleteShader(shader->obj);
+		gl_success("glDeleteShader");
 	}
 
 	da_free(shader->samplers);
@@ -360,63 +381,46 @@ void gs_shader_get_param_info(gs_sparam_t param,
 
 void gs_shader_set_bool(gs_sparam_t param, bool val)
 {
-	struct gs_shader *shader = param->shader;
-	glProgramUniform1i(shader->program, param->param, (GLint)val);
-	gl_success("glProgramUniform1i");
+	int int_val = val;
+	da_copy_array(param->cur_value, &int_val, sizeof(int_val));
 }
 
 void gs_shader_set_float(gs_sparam_t param, float val)
 {
-	struct gs_shader *shader = param->shader;
-	glProgramUniform1f(shader->program, param->param, val);
-	gl_success("glProgramUniform1f");
+	da_copy_array(param->cur_value, &val, sizeof(val));
 }
 
 void gs_shader_set_int(gs_sparam_t param, int val)
 {
-	struct gs_shader *shader = param->shader;
-	glProgramUniform1i(shader->program, param->param, val);
-	gl_success("glProgramUniform1i");
+	da_copy_array(param->cur_value, &val, sizeof(val));
 }
 
 void gs_shader_setmatrix3(gs_sparam_t param, const struct matrix3 *val)
 {
-	struct gs_shader *shader = param->shader;
 	struct matrix4 mat;
 	matrix4_from_matrix3(&mat, val);
 
-	glProgramUniformMatrix4fv(shader->program, param->param, 1,
-			false, mat.x.ptr);
-	gl_success("glProgramUniformMatrix4fv");
+	da_copy_array(param->cur_value, &mat, sizeof(mat));
 }
 
 void gs_shader_set_matrix4(gs_sparam_t param, const struct matrix4 *val)
 {
-	struct gs_shader *shader = param->shader;
-	glProgramUniformMatrix4fv(shader->program, param->param, 1,
-			false, val->x.ptr);
-	gl_success("glProgramUniformMatrix4fv");
+	da_copy_array(param->cur_value, val, sizeof(*val));
 }
 
 void gs_shader_set_vec2(gs_sparam_t param, const struct vec2 *val)
 {
-	struct gs_shader *shader = param->shader;
-	glProgramUniform2fv(shader->program, param->param, 1, val->ptr);
-	gl_success("glProgramUniform2fv");
+	da_copy_array(param->cur_value, val->ptr, sizeof(*val));
 }
 
 void gs_shader_set_vec3(gs_sparam_t param, const struct vec3 *val)
 {
-	struct gs_shader *shader = param->shader;
-	glProgramUniform3fv(shader->program, param->param, 1, val->ptr);
-	gl_success("glProgramUniform3fv");
+	da_copy_array(param->cur_value, val->ptr, sizeof(*val));
 }
 
 void gs_shader_set_vec4(gs_sparam_t param, const struct vec4 *val)
 {
-	struct gs_shader *shader = param->shader;
-	glProgramUniform4fv(shader->program, param->param, 1, val->ptr);
-	gl_success("glProgramUniform4fv");
+	da_copy_array(param->cur_value, val->ptr, sizeof(*val));
 }
 
 void gs_shader_set_texture(gs_sparam_t param, gs_texture_t val)
@@ -424,48 +428,257 @@ void gs_shader_set_texture(gs_sparam_t param, gs_texture_t val)
 	param->texture = val;
 }
 
-static void shader_setval_data(gs_sparam_t param, const void *val, int count)
+static inline bool validate_param(struct program_param *pp,
+		size_t expected_size)
 {
-	struct gs_shader *shader = param->shader;
+	if (pp->param->cur_value.num != expected_size) {
+		blog(LOG_ERROR, "Parameter '%s' set to invalid size %u, "
+		                "expected %u",
+		                pp->param->name,
+		                (unsigned int)pp->param->cur_value.num,
+		                (unsigned int)expected_size);
+		return false;
+	}
 
-	if (param->type == GS_SHADER_PARAM_BOOL ||
-	    param->type == GS_SHADER_PARAM_INT) {
-		glProgramUniform1iv(shader->program, param->param, count, val);
-		gl_success("glProgramUniform1iv");
+	return true;
+}
 
-	} else if (param->type == GS_SHADER_PARAM_FLOAT) {
-		glProgramUniform1fv(shader->program, param->param, count, val);
-		gl_success("glProgramUniform1fv");
+static void program_set_param_data(struct gs_program *program,
+		struct program_param *pp)
+{
+	void *array = pp->param->cur_value.array;
+
+	if (pp->param->type == GS_SHADER_PARAM_BOOL ||
+	    pp->param->type == GS_SHADER_PARAM_INT) {
+		if (validate_param(pp, sizeof(int))) {
+			glUniform1iv(pp->obj, 1, (int*)array);
+			gl_success("glUniform1iv");
+		}
+
+	} else if (pp->param->type == GS_SHADER_PARAM_FLOAT) {
+		if (validate_param(pp, sizeof(float))) {
+			glUniform1fv(pp->obj, 1, (float*)array);
+			gl_success("glUniform1fv");
+		}
+
+	} else if (pp->param->type == GS_SHADER_PARAM_VEC2) {
+		if (validate_param(pp, sizeof(struct vec2))) {
+			glUniform2fv(pp->obj, 1, (float*)array);
+			gl_success("glUniform2fv");
+		}
+
+	} else if (pp->param->type == GS_SHADER_PARAM_VEC3) {
+		if (validate_param(pp, sizeof(float) * 3)) {
+			glUniform3fv(pp->obj, 1, (float*)array);
+			gl_success("glUniform3fv");
+		}
+
+	} else if (pp->param->type == GS_SHADER_PARAM_VEC4) {
+		if (validate_param(pp, sizeof(struct vec4))) {
+			glUniform4fv(pp->obj, 1, (float*)array);
+			gl_success("glUniform4fv");
+		}
+
+	} else if (pp->param->type == GS_SHADER_PARAM_MATRIX4X4) {
+		if (validate_param(pp, sizeof(struct matrix4))) {
+			glUniformMatrix4fv(pp->obj, 1, false,
+					(float*)array);
+			gl_success("glUniformMatrix4fv");
+		}
+
+	} else if (pp->param->type == GS_SHADER_PARAM_TEXTURE) {
+		device_load_texture(program->device, pp->param->texture,
+				pp->param->texture_id);
+	}
+}
 
-	} else if (param->type == GS_SHADER_PARAM_VEC2) {
-		glProgramUniform2fv(shader->program, param->param, count, val);
-		gl_success("glProgramUniform2fv");
+void program_update_params(struct gs_program *program)
+{
+	for (size_t i = 0; i < program->params.num; i++) {
+		struct program_param *pp = program->params.array + i;
+		program_set_param_data(program, pp);
+	}
+}
 
-	} else if (param->type == GS_SHADER_PARAM_VEC3) {
-		glProgramUniform3fv(shader->program, param->param, count, val);
-		gl_success("glProgramUniform3fv");
+static void print_link_errors(GLuint program)
+{
+	char    *errors = NULL;
+	GLint   info_len = 0;
+	GLsizei chars_written = 0;
 
-	} else if (param->type == GS_SHADER_PARAM_VEC4) {
-		glProgramUniform4fv(shader->program, param->param, count, val);
-		gl_success("glProgramUniform4fv");
+	glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_len);
+	if (!gl_success("glGetProgramiv") || !info_len)
+		return;
+
+	errors = calloc(1, info_len+1);
+	glGetProgramInfoLog(program, info_len, &chars_written, errors);
+	gl_success("glGetShaderInfoLog");
+
+	blog(LOG_DEBUG, "Linker warnings/errors:\n%s", errors);
 
-	} else if (param->type == GS_SHADER_PARAM_MATRIX4X4) {
-		glProgramUniformMatrix4fv(shader->program, param->param,
-				count, false, val);
-		gl_success("glProgramUniformMatrix4fv");
+	free(errors);
+}
+
+static bool assign_program_attrib(struct gs_program *program,
+		struct shader_attrib *attrib)
+{
+	GLint attrib_obj = glGetAttribLocation(program->obj, attrib->name);
+	if (!gl_success("glGetAttribLocation"))
+		return false;
+
+	if (attrib_obj == -1) {
+		blog(LOG_ERROR, "glGetAttribLocation: Could not find "
+		                "attribute '%s'", attrib->name);
+		return false;
 	}
+
+	da_push_back(program->attribs, &attrib_obj);
+	return true;
 }
 
-void shader_update_textures(struct gs_shader *shader)
+static inline bool assign_program_attribs(struct gs_program *program)
 {
-	size_t i;
-	for (i = 0; i < shader->params.num; i++) {
-		struct gs_shader_param *param = shader->params.array+i;
+	struct gs_shader *shader = program->vertex_shader;
+
+	for (size_t i = 0; i < shader->attribs.num; i++) {
+		struct shader_attrib *attrib = shader->attribs.array + i;
+		if (!assign_program_attrib(program, attrib))
+			return false;
+	}
+
+	return true;
+}
 
-		if (param->type == GS_SHADER_PARAM_TEXTURE)
-			device_load_texture(shader->device, param->texture,
-					param->texture_id);
+static bool assign_program_param(struct gs_program *program,
+		struct gs_shader_param *param)
+{
+	struct program_param info;
+
+	info.obj = glGetUniformLocation(program->obj, param->name);
+	if (!gl_success("glGetUniformLocation"))
+		return false;
+
+	if (info.obj == -1) {
+		blog(LOG_ERROR, "Program parameter '%s' not found",
+				param->name);
+		return false;
 	}
+
+	info.param = param;
+	da_push_back(program->params, &info);
+	return true;
+}
+
+static inline bool assign_program_shader_params(struct gs_program *program,
+		struct gs_shader *shader)
+{
+	for (size_t i = 0; i < shader->params.num; i++) {
+		struct gs_shader_param *param = shader->params.array + i;
+		if (!assign_program_param(program, param))
+			return false;
+	}
+
+	return true;
+}
+
+static inline bool assign_program_params(struct gs_program *program)
+{
+	if (!assign_program_shader_params(program, program->vertex_shader))
+		return false;
+	if (!assign_program_shader_params(program, program->pixel_shader))
+		return false;
+
+	return true;
+}
+
+struct gs_program *gs_program_create(struct gs_device *device)
+{
+	struct gs_program *program = bzalloc(sizeof(*program));
+	int linked = false;
+
+	program->device        = device;
+	program->vertex_shader = device->cur_vertex_shader;
+	program->pixel_shader  = device->cur_pixel_shader;
+
+	program->obj = glCreateProgram();
+	if (!gl_success("glCreateProgram"))
+		goto error_detach_neither;
+
+	glAttachShader(program->obj, program->vertex_shader->obj);
+	if (!gl_success("glAttachShader (vertex)"))
+		goto error_detach_neither;
+
+	glAttachShader(program->obj, program->pixel_shader->obj);
+	if (!gl_success("glAttachShader (pixel)"))
+		goto error_detach_vertex;
+
+	glLinkProgram(program->obj);
+	if (!gl_success("glLinkProgram"))
+		goto error;
+
+	glGetProgramiv(program->obj, GL_LINK_STATUS, &linked);
+	if (!gl_success("glGetProgramiv"))
+		goto error;
+
+	if (linked == GL_FALSE) {
+		print_link_errors(program->obj);
+		goto error;
+	}
+
+	if (!assign_program_attribs(program))
+		goto error;
+	if (!assign_program_params(program))
+		goto error;
+
+	glDetachShader(program->obj, program->vertex_shader->obj);
+	gl_success("glDetachShader (vertex)");
+
+	glDetachShader(program->obj, program->pixel_shader->obj);
+	gl_success("glDetachShader (pixel)");
+
+	program->next = device->first_program;
+	program->prev_next = &device->first_program;
+	device->first_program = program;
+	if (program->next)
+		program->next->prev_next = &program->next;
+
+	return program;
+
+error:
+	glDetachShader(program->obj, program->pixel_shader->obj);
+	gl_success("glDetachShader (pixel)");
+
+error_detach_vertex:
+	glDetachShader(program->obj, program->vertex_shader->obj);
+	gl_success("glDetachShader (vertex)");
+
+error_detach_neither:
+	gs_program_destroy(program);
+	return NULL;
+}
+
+void gs_program_destroy(struct gs_program *program)
+{
+	if (!program)
+		return;
+
+	if (program->device->cur_program == program) {
+		program->device->cur_program = 0;
+		glUseProgram(0);
+		gl_success("glUseProgram (zero)");
+	}
+
+	da_free(program->attribs);
+	da_free(program->params);
+
+	if (program->next)
+		program->next->prev_next = program->prev_next;
+	*program->prev_next = program->next;
+
+	glDeleteProgram(program->obj);
+	gl_success("glDeleteProgram");
+
+	bfree(program);
 }
 
 void gs_shader_set_val(gs_sparam_t param, const void *val, size_t size)
@@ -484,7 +697,7 @@ void gs_shader_set_val(gs_sparam_t param, const void *val, size_t size)
 	case GS_SHADER_PARAM_VEC4:      expected_size = sizeof(float)*4; break;
 	case GS_SHADER_PARAM_MATRIX4X4: expected_size = sizeof(float)*4*4;break;
 	case GS_SHADER_PARAM_TEXTURE:   expected_size = sizeof(void*); break;
-	default:                     expected_size = 0;
+	default:                        expected_size = 0;
 	}
 
 	expected_size *= count;
@@ -500,7 +713,7 @@ void gs_shader_set_val(gs_sparam_t param, const void *val, size_t size)
 	if (param->type == GS_SHADER_PARAM_TEXTURE)
 		gs_shader_set_texture(param, *(gs_texture_t*)val);
 	else
-		shader_setval_data(param, val, count);
+		da_copy_array(param->cur_value, val, size);
 }
 
 void gs_shader_set_default(gs_sparam_t param)

+ 1 - 1
libobs-opengl/gl-shaderparser.c

@@ -617,7 +617,7 @@ static void gl_rename_attributes(struct gl_shader_parser *glsp)
 			val    = output_idx++;
 		}
 
-		dstr_printf(&new_name, "%s%u", prefix, val);
+		dstr_printf(&new_name, "%s%u", prefix, (unsigned int)val);
 		dstr_replace(&glsp->gl_string, attrib->name.array,
 				new_name.array);
 		dstr_move(&attrib->name, &new_name);

+ 43 - 63
libobs-opengl/gl-subsystem.c

@@ -124,12 +124,6 @@ static bool gl_init_extensions(struct gs_device* device)
 		gl_enable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
 	}
 
-	if (!GLAD_GL_VERSION_4_1 && !GLAD_GL_ARB_separate_shader_objects) {
-		blog(LOG_ERROR, "OpenGL extension ARB_separate_shader_objects "
-		                "is required.");
-		return false;
-	}
-
 	if (GLAD_GL_VERSION_4_3 || GLAD_GL_ARB_copy_image)
 		device->copy_type = COPY_TYPE_ARB;
 	else if (GLAD_GL_NV_copy_image)
@@ -213,14 +207,6 @@ int device_create(gs_device_t *p_device, struct gs_init_data *info)
 	
 	gl_enable(GL_CULL_FACE);
 	
-	glGenProgramPipelines(1, &device->pipeline);
-	if (!gl_success("glGenProgramPipelines"))
-		goto fail;
-
-	glBindProgramPipeline(device->pipeline);
-	if (!gl_success("glBindProgramPipeline"))
-		goto fail;
-
 	device_leave_context(device);
 	device->cur_swap = gl_platform_getswap(device->plat);
 
@@ -239,11 +225,12 @@ void device_destroy(gs_device_t device)
 {
 	if (device) {
 		size_t i;
+
 		for (i = 0; i < device->fbos.num; i++)
 			fbo_info_destroy(device->fbos.array[i]);
 
-		if (device->pipeline)
-			glDeleteProgramPipelines(1, &device->pipeline);
+		while (device->first_program)
+			gs_program_destroy(device->first_program);
 
 		da_free(device->proj_stack);
 		da_free(device->fbos);
@@ -511,37 +498,17 @@ void device_load_samplerstate(gs_device_t device, gs_samplerstate_t ss,
 void device_load_vertexshader(gs_device_t device, gs_shader_t vertshader)
 {
 	GLuint program = 0;
-	gs_vertbuffer_t cur_vb = device->cur_vertex_buffer;
 
 	if (device->cur_vertex_shader == vertshader)
 		return;
 
 	if (vertshader && vertshader->type != GS_SHADER_VERTEX) {
 		blog(LOG_ERROR, "Specified shader is not a vertex shader");
-		goto fail;
+		blog(LOG_ERROR, "device_load_vertexshader (GL) failed");
+		return;
 	}
 
-	/* unload and reload the vertex buffer to sync the buffers up with
-	 * the specific shader */
-	if (cur_vb && !vertexbuffer_load(device, NULL))
-		goto fail;
-
 	device->cur_vertex_shader = vertshader;
-
-	if (vertshader)
-		program = vertshader->program;
-
-	glUseProgramStages(device->pipeline, GL_VERTEX_SHADER_BIT, program);
-	if (!gl_success("glUseProgramStages"))
-		goto fail;
-
-	if (cur_vb && !vertexbuffer_load(device, cur_vb))
-		goto fail;
-
-	return;
-
-fail:
-	blog(LOG_ERROR, "device_load_vertexshader (GL) failed");
 }
 
 static void load_default_pixelshader_samplers(struct gs_device *device,
@@ -562,7 +529,6 @@ static void load_default_pixelshader_samplers(struct gs_device *device,
 
 void device_load_pixelshader(gs_device_t device, gs_shader_t pixelshader)
 {
-	GLuint program = 0;
 	if (device->cur_pixel_shader == pixelshader)
 		return;
 
@@ -573,13 +539,6 @@ void device_load_pixelshader(gs_device_t device, gs_shader_t pixelshader)
 
 	device->cur_pixel_shader = pixelshader;
 
-	if (pixelshader)
-		program = pixelshader->program;
-
-	glUseProgramStages(device->pipeline, GL_FRAGMENT_SHADER_BIT, program);
-	if (!gl_success("glUseProgramStages"))
-		goto fail;
-
 	clear_textures(device);
 
 	if (pixelshader)
@@ -916,22 +875,29 @@ static void update_viewproj_matrix(struct gs_device *device)
 		gs_shader_set_matrix4(vs->viewproj, &device->cur_viewproj);
 }
 
-static inline bool check_shader_pipeline_validity(gs_device_t device)
+static inline struct gs_program *find_program(struct gs_device *device)
 {
-	int valid = false;
+	struct gs_program *program = device->first_program;
 
-	glValidateProgramPipeline(device->pipeline);
-	if (!gl_success("glValidateProgramPipeline"))
-		return false;
+	while (program) {
+		if (program->vertex_shader == device->cur_vertex_shader &&
+		    program->pixel_shader  == device->cur_pixel_shader)
+			return program;
 
-	glGetProgramPipelineiv(device->pipeline, GL_VALIDATE_STATUS, &valid);
-	if (!gl_success("glGetProgramPipelineiv"))
-		return false;
+		program = program->next;
+	}
 
-	if (!valid)
-		blog(LOG_ERROR, "Shader pipeline appears to be invalid");
+	return NULL;
+}
+
+static inline struct gs_program *get_shader_program(struct gs_device *device)
+{
+	struct gs_program *program = find_program(device);
+
+	if (!program)
+		program = gs_program_create(device);
 
-	return valid != 0;
+	return program;
 }
 
 void device_draw(gs_device_t device, enum gs_draw_mode draw_mode,
@@ -940,6 +906,7 @@ void device_draw(gs_device_t device, enum gs_draw_mode draw_mode,
 	struct gs_index_buffer *ib = device->cur_index_buffer;
 	GLenum  topology = convert_gs_topology(draw_mode);
 	gs_effect_t effect = gs_get_effect();
+	struct gs_program *program;
 
 	if (!can_render(device))
 		goto fail;
@@ -947,15 +914,28 @@ void device_draw(gs_device_t device, enum gs_draw_mode draw_mode,
 	if (effect)
 		gs_effect_update_params(effect);
 
-	shader_update_textures(device->cur_pixel_shader);
+	program = get_shader_program(device);
+	if (!program)
+		goto fail;
 
-	update_viewproj_matrix(device);
+	load_vb_buffers(program, device->cur_vertex_buffer);
 
+	if (program != device->cur_program && device->cur_program) {
+		glUseProgram(0);
+		gl_success("glUseProgram (zero)");
+	}
 
-#ifdef _DEBUG
-	if (!check_shader_pipeline_validity(device))
-		goto fail;
-#endif
+	if (program != device->cur_program) {
+		device->cur_program = program;
+
+		glUseProgram(program->obj);
+		if (!gl_success("glUseProgram"))
+			goto fail;
+	}
+
+	update_viewproj_matrix(device);
+
+	program_update_params(program);
 
 	if (ib) {
 		if (num_verts == 0)

+ 31 - 9
libobs-opengl/gl-subsystem.h

@@ -300,7 +300,6 @@ struct gs_shader_param {
 
 	char                 *name;
 	gs_shader_t          shader;
-	GLint                param;
 	GLint                texture_id;
 	size_t               sampler_id;
 	int                  array_count;
@@ -322,7 +321,7 @@ enum attrib_type {
 };
 
 struct shader_attrib {
-	GLint                attrib;
+	char                 *name;
 	size_t               index;
 	enum attrib_type     type;
 };
@@ -330,17 +329,37 @@ struct shader_attrib {
 struct gs_shader {
 	gs_device_t          device;
 	enum gs_shader_type  type;
-	GLuint               program;
+	GLuint               obj;
 
 	struct gs_shader_param  *viewproj;
 	struct gs_shader_param  *world;
 
-	DARRAY(struct shader_attrib)    attribs;
-	DARRAY(struct gs_shader_param)  params;
-	DARRAY(gs_samplerstate_t)       samplers;
+	DARRAY(struct shader_attrib)   attribs;
+	DARRAY(struct gs_shader_param) params;
+	DARRAY(gs_samplerstate_t)      samplers;
+};
+
+struct program_param {
+	GLint                  obj;
+	struct gs_shader_param *param;
+};
+
+struct gs_program {
+	gs_device_t                  device;
+	GLuint                       obj;
+	struct gs_shader             *vertex_shader;
+	struct gs_shader             *pixel_shader;
+
+	DARRAY(struct program_param) params;
+	DARRAY(GLint)                attribs;
+
+	struct gs_program            **prev_next;
+	struct gs_program            *next;
 };
 
-extern void shader_update_textures(struct gs_shader *shader);
+extern struct gs_program *gs_program_create(struct gs_device *device);
+extern void gs_program_destroy(struct gs_program *program);
+extern void program_update_params(struct gs_program *shader);
 
 struct gs_vertex_buffer {
 	GLuint               vao;
@@ -357,7 +376,8 @@ struct gs_vertex_buffer {
 	struct gs_vb_data    *data;
 };
 
-extern bool vertexbuffer_load(gs_device_t device, gs_vertbuffer_t vb);
+extern bool load_vb_buffers(struct gs_program *program,
+		struct gs_vertex_buffer *vb);
 
 struct gs_index_buffer {
 	GLuint               buffer;
@@ -455,7 +475,6 @@ static inline void fbo_info_destroy(struct fbo_info *fbo)
 
 struct gs_device {
 	struct gl_platform   *plat;
-	GLuint               pipeline;
 	enum copy_type       copy_type;
 
 	gs_texture_t         cur_render_target;
@@ -468,6 +487,9 @@ struct gs_device {
 	gs_shader_t          cur_vertex_shader;
 	gs_shader_t          cur_pixel_shader;
 	gs_swapchain_t       cur_swap;
+	struct gs_program    *cur_program;
+
+	struct gs_program    *first_program;
 
 	enum gs_cull_mode    cur_cull_mode;
 	struct gs_rect       cur_viewport;

+ 7 - 25
libobs-opengl/gl-vertexbuffer.c

@@ -203,7 +203,7 @@ static inline GLuint get_vb_buffer(struct gs_vertex_buffer *vb,
 }
 
 static bool load_vb_buffer(struct shader_attrib *attrib,
-		struct gs_vertex_buffer *vb)
+		struct gs_vertex_buffer *vb, GLint id)
 {
 	GLenum type;
 	GLint width;
@@ -220,11 +220,11 @@ static bool load_vb_buffer(struct shader_attrib *attrib,
 	if (!gl_bind_buffer(GL_ARRAY_BUFFER, buffer))
 		return false;
 
-	glVertexAttribPointer(attrib->attrib, width, type, GL_TRUE, 0, 0);
+	glVertexAttribPointer(id, width, type, GL_TRUE, 0, 0);
 	if (!gl_success("glVertexAttribPointer"))
 		success = false;
 
-	glEnableVertexAttribArray(attrib->attrib);
+	glEnableVertexAttribArray(id);
 	if (!gl_success("glEnableVertexAttribArray"))
 		success = false;
 
@@ -234,9 +234,9 @@ static bool load_vb_buffer(struct shader_attrib *attrib,
 	return success;
 }
 
-static inline bool load_vb_buffers(struct gs_shader *shader,
-		struct gs_vertex_buffer *vb)
+bool load_vb_buffers(struct gs_program *program, struct gs_vertex_buffer *vb)
 {
+	struct gs_shader *shader = program->vertex_shader;
 	size_t i;
 
 	if (!gl_bind_vertex_array(vb->vao))
@@ -244,32 +244,14 @@ static inline bool load_vb_buffers(struct gs_shader *shader,
 
 	for (i = 0; i < shader->attribs.num; i++) {
 		struct shader_attrib *attrib = shader->attribs.array+i;
-		if (!load_vb_buffer(attrib, vb))
+		if (!load_vb_buffer(attrib, vb, program->attribs.array[i]))
 			return false;
 	}
 
 	return true;
 }
 
-bool vertexbuffer_load(gs_device_t device, gs_vertbuffer_t vb)
-{
-	if (device->cur_vertex_buffer == vb)
-		return true;
-
-	device->cur_vertex_buffer = vb;
-	if (!device->cur_vertex_shader || !vb) {
-		gl_bind_vertex_array(0);
-		return true;
-	}
-
-	if (!load_vb_buffers(device->cur_vertex_shader, vb))
-		return false;
-
-	return true;
-}
-
 void device_load_vertexbuffer(gs_device_t device, gs_vertbuffer_t vb)
 {
-	if (!vertexbuffer_load(device, vb))
-		blog(LOG_ERROR, "device_load_vertexbuffer (GL) failed");
+	device->cur_vertex_buffer = vb;
 }