This fixes a nasty bug that manifests on windows when building without precompiled headers. util/stringops.h used to silently replace strdup with a macro that's compatible with mem_free(). This header would typically be included everywhere due to PCH, but without it the strdup from libc would sometimes be in scope. On most platforms mem_free() is equivalent to free(), but not on windows, because we have to use _aligned_free() there. Attempting to mem_free() the result of a libc strdup() would segfault in such a configuration. Avoid the footgun by banning strdup() entirely. Maybe redefining libc names isn't such a great idea, who knew?
163 lines
3.9 KiB
C
163 lines
3.9 KiB
C
/*
|
|
* This software is licensed under the terms of the MIT License.
|
|
* See COPYING for further information.
|
|
* ---
|
|
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
|
|
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
|
|
*/
|
|
|
|
#include "shader_object.h"
|
|
|
|
#include "../glcommon/debug.h"
|
|
#include "../glcommon/shaders.h"
|
|
|
|
bool gl33_shader_language_supported(const ShaderLangInfo *lang, ShaderLangInfo *out_alternative) {
|
|
if(glcommon_shader_lang_table.data) {
|
|
dynarray_foreach_elem(&glcommon_shader_lang_table, ShaderLangInfo *l, {
|
|
if(!memcmp(l, lang, sizeof(*l))) {
|
|
return true;
|
|
}
|
|
});
|
|
|
|
if(out_alternative) {
|
|
*out_alternative = dynarray_get(&glcommon_shader_lang_table, 0);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// No table. Should never actually happen, but let's just hope it works, right?
|
|
|
|
bool supported = lang->lang == SHLANG_GLSL;
|
|
|
|
if(!supported && out_alternative) {
|
|
out_alternative->lang = SHLANG_GLSL;
|
|
out_alternative->glsl.version.version = 330;
|
|
out_alternative->glsl.version.profile = GLSL_PROFILE_CORE;
|
|
}
|
|
|
|
return supported;
|
|
}
|
|
|
|
static void print_info_log(GLuint shader) {
|
|
GLint len = 0, alen = 0;
|
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len);
|
|
|
|
if(len > 1) {
|
|
char log[len];
|
|
memset(log, 0, len);
|
|
glGetShaderInfoLog(shader, len, &alen, log);
|
|
|
|
log_warn(
|
|
"\n== Shader object compilation log (%u) ==\n%s\n== End of shader object compilation log (%u) ==",
|
|
shader, log, shader
|
|
);
|
|
}
|
|
}
|
|
|
|
ShaderObject *gl33_shader_object_compile(ShaderSource *source) {
|
|
assert(r_shader_language_supported(&source->lang, NULL));
|
|
|
|
GLuint gl_handle = glCreateShader(
|
|
source->stage == SHADER_STAGE_VERTEX
|
|
? GL_VERTEX_SHADER
|
|
: GL_FRAGMENT_SHADER
|
|
);
|
|
|
|
GLint status;
|
|
|
|
// log_debug("Source code for %s:\n%s", path, source->content);
|
|
|
|
glShaderSource(
|
|
gl_handle, 1,
|
|
(const GLchar*[]) { source->content },
|
|
(GLint[]) { source->content_size - 1 }
|
|
);
|
|
|
|
glCompileShader(gl_handle);
|
|
|
|
#if defined DEBUG && !defined STATIC_GLES3
|
|
if(GLAD_GL_ANGLE_translated_shader_source) {
|
|
GLint srclen;
|
|
glGetShaderiv(gl_handle, GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE, &srclen);
|
|
|
|
if(srclen > 0) {
|
|
char buf[srclen];
|
|
glGetTranslatedShaderSourceANGLE(gl_handle, sizeof(buf), NULL, buf);
|
|
log_debug("Final shader source (as translated by ANGLE):\n%s", buf);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
glGetShaderiv(gl_handle, GL_COMPILE_STATUS, &status);
|
|
print_info_log(gl_handle);
|
|
|
|
ShaderObject *shobj = NULL;
|
|
|
|
if(status) {
|
|
uint nattribs = source->meta.glsl.num_attributes;
|
|
|
|
shobj = ALLOC(ShaderObject, {
|
|
.gl_handle = gl_handle,
|
|
.stage = source->stage,
|
|
.num_attribs = nattribs,
|
|
});
|
|
snprintf(shobj->debug_label, sizeof(shobj->debug_label), "Shader object #%i", gl_handle);
|
|
|
|
if(nattribs > 0) {
|
|
shobj->attribs = ALLOC_ARRAY(nattribs, typeof(*shobj->attribs));
|
|
}
|
|
|
|
for(uint i = 0; i < nattribs; ++i) {
|
|
GLSLAttribute *a = source->meta.glsl.attributes + i;
|
|
shobj->attribs[i].name = mem_strdup(a->name);
|
|
shobj->attribs[i].location = a->location;
|
|
}
|
|
} else {
|
|
glDeleteShader(gl_handle);
|
|
}
|
|
|
|
return shobj;
|
|
}
|
|
|
|
void gl33_shader_object_destroy(ShaderObject *shobj) {
|
|
glDeleteShader(shobj->gl_handle);
|
|
|
|
uint nattribs = shobj->num_attribs;
|
|
|
|
for(uint i = 0; i < nattribs; ++i) {
|
|
mem_free(shobj->attribs[i].name);
|
|
}
|
|
|
|
mem_free(shobj->attribs);
|
|
mem_free(shobj);
|
|
}
|
|
|
|
void gl33_shader_object_set_debug_label(ShaderObject *shobj, const char *label) {
|
|
glcommon_set_debug_label(shobj->debug_label, "Shader object", GL_SHADER, shobj->gl_handle, label);
|
|
}
|
|
|
|
const char *gl33_shader_object_get_debug_label(ShaderObject *shobj) {
|
|
return shobj->debug_label;
|
|
}
|
|
|
|
bool gl33_shader_object_transfer(ShaderObject *dst, ShaderObject *src) {
|
|
if(UNLIKELY(dst->stage != src->stage)) {
|
|
log_error("Shader object changed stage");
|
|
return false;
|
|
}
|
|
|
|
glDeleteShader(dst->gl_handle);
|
|
|
|
uint nattribs = dst->num_attribs;
|
|
|
|
for(uint i = 0; i < nattribs; ++i) {
|
|
mem_free(dst->attribs[i].name);
|
|
}
|
|
|
|
mem_free(dst->attribs);
|
|
*dst = *src;
|
|
mem_free(src);
|
|
|
|
return true;
|
|
}
|