summaryrefslogtreecommitdiff
path: root/gfx/src/core/shader.c
blob: af2f89fe15e9b6894fb2d93bc09747543d8acd2c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include "shader.h"

#include "gl_util.h"
#include <gfx_assert.h>

#include <cstring.h>
#include <log/log.h>

#include <stdlib.h>
#include <string.h>

static GLenum shader_type_to_gl(ShaderType type) {
  switch (type) {
  case VertexShader:
    return GL_VERTEX_SHADER;
  case FragmentShader:
    return GL_FRAGMENT_SHADER;
  }
  FAIL("shader_type_to_gl(): missing case");
  return GL_INVALID_ENUM;
}

static lstring make_defines_string(const ShaderDesc* desc) {
  lstring defines = {0};
  for (size_t i = 0; i < desc->num_defines; ++i) {
    const ShaderCompilerDefine* define = &desc->defines[i];
    lstring_append_cstr(&defines, "#define ");
    lstring_append_cstr(&defines, sstring_cstr(&define->name));
    lstring_append_cstr(&defines, " ");
    lstring_append_cstr(&defines, sstring_cstr(&define->value));
    lstring_append_cstr(&defines, "\n");
  }
  return defines;
}

/// Creates an OpenGL shader.
/// Returns non-zero on success, 0 on failure.
static GLuint create_shader(const ShaderDesc* desc) {
  const GLuint shader = glCreateShader(shader_type_to_gl(desc->type));
  if (!shader) {
    return 0;
  }

#if GFX_GL_CONTEXT == GFX_GL_CONTEXT_ES
  const char* header = "#version 300 es\n\nprecision highp float;";
#else
  const char* header = "#version 400 core\n\n";
#endif

  lstring defines = make_defines_string(desc);

  const char* source_bits[]    = {header, lstring_cstr(&defines), desc->code};
  const GLint source_lengths[] = {
      strlen(header), lstring_length(defines), strlen(desc->code)};

  glShaderSource(shader, 3, source_bits, source_lengths);
  glCompileShader(shader);
  GLint result;
  glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
  if (result == GL_FALSE) {
    GLint log_len;
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len);
    if (log_len > 0) {
      char* log = calloc(log_len, sizeof(char));
      glGetShaderInfoLog(shader, log_len, NULL, log);
      static const char* sep = "----------";
      LOGE("Failed loading shader: %s\n%s\n%s\n%s", log, sep, desc->code, sep);
      free(log);
    } else {
      LOGE("Failed loading shader:\n%s", desc->code);
    }
    glDeleteShader(shader);
    return 0;
  }
  ASSERT_GL;
  return shader;
}

bool gfx_compile_shader(Shader* shader, const ShaderDesc* desc) {
  shader->id = create_shader(desc);
  return shader->id != 0;
}

void gfx_del_shader(Shader* shader) {
  assert(shader);
  
  if (shader->id) {
    glDeleteShader(shader->id);
    shader->id = 0;
  }
  ASSERT_GL;
}