Add #line offsets in the shader preprocessor so error reports point to correct lines from the original shader file, move #version and preamble to top instead of inserting defines after the original #version

This commit is contained in:
Hannu Hanhi 2021-09-18 22:45:54 +03:00
parent 0c70ca9068
commit bbf23a5143

View file

@ -370,6 +370,8 @@ static shader_t gl_shaders[NUMSHADERTARGETS*2];
static shadertarget_t gl_shadertargets[NUMSHADERTARGETS]; static shadertarget_t gl_shadertargets[NUMSHADERTARGETS];
#define WHITESPACE_CHARS " \t"
#define MODEL_LIGHTING_DEFINE "#define SRB2_MODEL_LIGHTING" #define MODEL_LIGHTING_DEFINE "#define SRB2_MODEL_LIGHTING"
#define PALETTE_RENDERING_DEFINE "#define SRB2_PALETTE_RENDERING" #define PALETTE_RENDERING_DEFINE "#define SRB2_PALETTE_RENDERING"
@ -416,12 +418,14 @@ static char *HWR_PreprocessShader(char *original)
const char *line_ending = "\n"; const char *line_ending = "\n";
int line_ending_len; int line_ending_len;
char *read_pos = original; char *read_pos = original;
int insertion_pos = 0;
int original_len = strlen(original); int original_len = strlen(original);
int distance_to_end = original_len; int distance_to_end = original_len;
int new_len; int new_len;
char *new_shader; char *new_shader;
char *write_pos; char *write_pos;
char shader_glsl_version[3];
int version_pos = -1;
int version_len = 0;
if (strstr(original, "\r\n")) if (strstr(original, "\r\n"))
{ {
@ -446,44 +450,62 @@ static char *HWR_PreprocessShader(char *original)
line_ending_len = strlen(line_ending); line_ending_len = strlen(line_ending);
// We need to find a place to put the #define commands. // Find the #version directive, if it exists. Also don't get fooled if it's
// To stay within GLSL specs, they must be *after* the #version define, // inside a comment. Copy the version digits so they can be used in the preamble.
// if there is any. So we need to look for that. And also let's not
// get fooled if there is a #version inside a comment!
// Time for some string parsing :D // Time for some string parsing :D
#define STARTSWITH(str, with_what) !strncmp(str, with_what, sizeof(with_what)-1) #define STARTSWITH(str, with_what) !strncmp(str, with_what, sizeof(with_what)-1)
#define ADVANCE(amount) read_pos += amount; distance_to_end -= amount; #define ADVANCE(amount) read_pos += (amount); distance_to_end -= (amount);
while (true) while (true)
{ {
// we're at the start of a line or at the end of a block comment. // we're at the start of a line or at the end of a block comment.
// first get any possible whitespace out of the way // first get any possible whitespace out of the way
int whitespace_len = strspn(read_pos, " \t"); int whitespace_len = strspn(read_pos, WHITESPACE_CHARS);
if (whitespace_len == distance_to_end) if (whitespace_len == distance_to_end)
break; // we got to the end break; // we got to the end
ADVANCE(whitespace_len) ADVANCE(whitespace_len)
if (STARTSWITH(read_pos, "#version")) if (STARTSWITH(read_pos, "#version"))
{ {
// getting closer // found a version directive (and it's not inside a comment)
INT32 newline_pos = strstr_int(read_pos, line_ending); // now locate, verify and read the version number
INT32 line_comment_pos = strstr_int(read_pos, "//"); int version_number_len;
INT32 block_comment_pos = strstr_int(read_pos, "/*"); version_pos = read_pos - original;
if (newline_pos == INT32_MAX && line_comment_pos == INT32_MAX && ADVANCE(sizeof("#version") - 1)
block_comment_pos == INT32_MAX) whitespace_len = strspn(read_pos, WHITESPACE_CHARS);
if (!whitespace_len)
{ {
// #version is at the end of the file. Probably not a valid shader. CONS_Alert(CONS_ERROR, "HWR_PreprocessShader: Syntax error in #version. Expected space after #version, but got other text.\n");
CONS_Alert(CONS_ERROR, "HWR_PreprocessShader: Shader unexpectedly ends after #version.\n");
return NULL; return NULL;
} }
else else if (whitespace_len == distance_to_end)
{ {
// insert at the earliest occurence of newline or comment after #version CONS_Alert(CONS_ERROR, "HWR_PreprocessShader: Syntax error in #version. Expected version number, but got end of file.\n");
insertion_pos = min(line_comment_pos, block_comment_pos); return NULL;
insertion_pos = min(newline_pos, insertion_pos);
insertion_pos += read_pos - original;
break;
} }
ADVANCE(whitespace_len)
version_number_len = strspn(read_pos, "0123456789");
if (!version_number_len)
{
CONS_Alert(CONS_ERROR, "HWR_PreprocessShader: Syntax error in #version. Expected version number, but got other text.\n");
return NULL;
}
else if (version_number_len != 3)
{
CONS_Alert(CONS_ERROR, "HWR_PreprocessShader: Syntax error in #version. Expected version with 3 digits, but got %d digits.\n", version_number_len);
return NULL;
}
M_Memcpy(shader_glsl_version, read_pos, 3);
ADVANCE(version_number_len)
version_len = (read_pos - original) - version_pos;
whitespace_len = strspn(read_pos, WHITESPACE_CHARS);
ADVANCE(whitespace_len)
if (STARTSWITH(read_pos, "es"))
{
CONS_Alert(CONS_ERROR, "HWR_PreprocessShader: Support for ES shaders is not implemented.\n");
return NULL;
}
break;
} }
else else
{ {
@ -544,12 +566,25 @@ static char *HWR_PreprocessShader(char *original)
#undef STARTSWITH #undef STARTSWITH
#undef ADVANCE #undef ADVANCE
#define ADD_TO_LEN(def) new_len += sizeof(def) - 1 + line_ending_len;
// Calculate length of modified shader. // Calculate length of modified shader.
new_len = original_len; new_len = original_len;
if (cv_glmodellighting.value) if (cv_glmodellighting.value)
new_len += sizeof(MODEL_LIGHTING_DEFINE) - 1 + 2 * line_ending_len; ADD_TO_LEN(MODEL_LIGHTING_DEFINE)
if (cv_glpaletterendering.value) if (cv_glpaletterendering.value)
new_len += sizeof(PALETTE_RENDERING_DEFINE) - 1 + 2 * line_ending_len; ADD_TO_LEN(PALETTE_RENDERING_DEFINE)
#undef ADD_TO_LEN
#define VERSION_PART "#version "
if (new_len != original_len)
{
if (version_pos != -1)
new_len += sizeof(VERSION_PART) - 1 + 3 + line_ending_len;
new_len += sizeof("#line 0") - 1 + line_ending_len;
}
// Allocate memory for modified shader. // Allocate memory for modified shader.
new_shader = Z_Malloc(new_len + 1, PU_STATIC, NULL); new_shader = Z_Malloc(new_len + 1, PU_STATIC, NULL);
@ -557,22 +592,27 @@ static char *HWR_PreprocessShader(char *original)
read_pos = original; read_pos = original;
write_pos = new_shader; write_pos = new_shader;
// Copy the part before our additions. if (new_len != original_len && version_pos != -1)
M_Memcpy(write_pos, original, insertion_pos); {
read_pos += insertion_pos; strcpy(write_pos, VERSION_PART);
write_pos += insertion_pos; write_pos += sizeof(VERSION_PART) - 1;
M_Memcpy(write_pos, shader_glsl_version, 3);
write_pos += 3;
strcpy(write_pos, line_ending);
write_pos += line_ending_len;
}
#undef VERSION_PART
#define WRITE_DEFINE(define) \ #define WRITE_DEFINE(define) \
{ \ { \
strcpy(write_pos, line_ending); \
write_pos += line_ending_len; \
strcpy(write_pos, define); \ strcpy(write_pos, define); \
write_pos += sizeof(define) - 1; \ write_pos += sizeof(define) - 1; \
strcpy(write_pos, line_ending); \ strcpy(write_pos, line_ending); \
write_pos += line_ending_len; \ write_pos += line_ending_len; \
} }
// Write the additions. // Write the defines.
if (cv_glmodellighting.value) if (cv_glmodellighting.value)
WRITE_DEFINE(MODEL_LIGHTING_DEFINE) WRITE_DEFINE(MODEL_LIGHTING_DEFINE)
if (cv_glpaletterendering.value) if (cv_glpaletterendering.value)
@ -580,8 +620,26 @@ static char *HWR_PreprocessShader(char *original)
#undef WRITE_DEFINE #undef WRITE_DEFINE
// Copy the part after our additions. // Write a #line directive, so compiler errors will report line numbers from the
M_Memcpy(write_pos, read_pos, original_len - insertion_pos); // original shader without our preamble lines.
if (new_len != original_len)
{
// line numbering in the #line directive is different for versions 110-150
if (version_pos == -1 || shader_glsl_version[0] == '1')
strcpy(write_pos, "#line 0");
else
strcpy(write_pos, "#line 1");
write_pos += sizeof("#line 0") - 1;
strcpy(write_pos, line_ending);
write_pos += line_ending_len;
}
// Copy the original shader.
M_Memcpy(write_pos, read_pos, original_len);
// Erase the original #version directive, if it exists and was copied.
if (new_len != original_len && version_pos != -1)
memset(write_pos + version_pos, ' ', version_len);
// Terminate the new string. // Terminate the new string.
new_shader[new_len] = '\0'; new_shader[new_len] = '\0';