Merge branch 'blender2.7'
[blender.git] / intern / cycles / device / device.cpp
index 317e62b..5b53dc9 100644 (file)
@@ -81,132 +81,276 @@ std::ostream& operator <<(std::ostream &os,
 
 Device::~Device()
 {
-       if(!background && vertex_buffer != 0) {
-               glDeleteBuffers(1, &vertex_buffer);
+       if(!background) {
+               if(vertex_buffer != 0) {
+                       glDeleteBuffers(1, &vertex_buffer);
+               }
+               if(fallback_shader_program != 0) {
+                       glDeleteProgram(fallback_shader_program);
+               }
        }
 }
 
-void Device::draw_pixels(device_memory& rgba, int y, int w, int h, int dx, int dy, int width, int height, bool transparent,
-       const DeviceDrawParams &draw_params)
+/* TODO move shaders to standalone .glsl file. */
+const char *FALLBACK_VERTEX_SHADER =
+"#version 330\n"
+"uniform vec2 fullscreen;\n"
+"in vec2 texCoord;\n"
+"in vec2 pos;\n"
+"out vec2 texCoord_interp;\n"
+"\n"
+"vec2 normalize_coordinates()\n"
+"{\n"
+"      return (vec2(2.0) * (pos / fullscreen)) - vec2(1.0);\n"
+"}\n"
+"\n"
+"void main()\n"
+"{\n"
+"      gl_Position = vec4(normalize_coordinates(), 0.0, 1.0);\n"
+"      texCoord_interp = texCoord;\n"
+"}\n\0";
+
+const char *FALLBACK_FRAGMENT_SHADER =
+"#version 330\n"
+"uniform sampler2D image_texture;\n"
+"in vec2 texCoord_interp;\n"
+"out vec4 fragColor;\n"
+"\n"
+"void main()\n"
+"{\n"
+"      fragColor = texture(image_texture, texCoord_interp);\n"
+"}\n\0";
+
+static void shader_print_errors(const char *task, const char *log, const char *code)
 {
-       assert(rgba.type == MEM_PIXELS);
+       LOG(ERROR) << "Shader: " << task << " error:";
+       LOG(ERROR) << "===== shader string ====";
 
-       mem_copy_from(rgba, y, w, h, rgba.memory_elements_size(1));
+       stringstream stream(code);
+       string partial;
 
-       if(transparent) {
-               glEnable(GL_BLEND);
-               glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+       int line = 1;
+       while(getline(stream, partial, '\n')) {
+               if(line < 10) {
+                       LOG(ERROR) << " " << line << " " << partial;
+               }
+               else {
+                       LOG(ERROR) << line << " " << partial;
+               }
+               line++;
        }
+       LOG(ERROR) << log;
+}
 
-       glColor3f(1.0f, 1.0f, 1.0f);
+static int bind_fallback_shader(void)
+{
+       GLint status;
+       GLchar log[5000];
+       GLsizei length = 0;
+       GLuint program = 0;
 
-       if(rgba.data_type == TYPE_HALF) {
-               /* for multi devices, this assumes the inefficient method that we allocate
-                * all pixels on the device even though we only render to a subset */
-               GLhalf *host_pointer = (GLhalf*)rgba.host_pointer;
-               float vbuffer[16], *basep;
-               float *vp = NULL;
-
-               host_pointer += 4*y*w;
-
-               /* draw half float texture, GLSL shader for display transform assumed to be bound */
-               GLuint texid;
-               glGenTextures(1, &texid);
-               glBindTexture(GL_TEXTURE_2D, texid);
-               glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, w, h, 0, GL_RGBA, GL_HALF_FLOAT, host_pointer);
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
-               glEnable(GL_TEXTURE_2D);
-
-               if(draw_params.bind_display_space_shader_cb) {
-                       draw_params.bind_display_space_shader_cb();
+       struct Shader {
+               const char *source;
+               GLenum type;
+       } shaders[2] = {
+           {FALLBACK_VERTEX_SHADER, GL_VERTEX_SHADER},
+           {FALLBACK_FRAGMENT_SHADER, GL_FRAGMENT_SHADER}
+    };
+
+       program = glCreateProgram();
+
+       for(int i = 0; i < 2; i++) {
+               GLuint shader = glCreateShader(shaders[i].type);
+
+               string source_str = shaders[i].source;
+               const char *c_str = source_str.c_str();
+
+               glShaderSource(shader, 1, &c_str, NULL);
+               glCompileShader(shader);
+
+               glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+
+               if(!status) {
+                       glGetShaderInfoLog(shader, sizeof(log), &length, log);
+                       shader_print_errors("compile", log, c_str);
+                       return 0;
                }
 
-               if(GLEW_VERSION_1_5) {
-                       if(!vertex_buffer)
-                               glGenBuffers(1, &vertex_buffer);
+               glAttachShader(program, shader);
+       }
+
+       /* Link output. */
+       glBindFragDataLocation(program, 0, "fragColor");
 
-                       glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
-                       /* invalidate old contents - avoids stalling if buffer is still waiting in queue to be rendered */
-                       glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW);
+       /* Link and error check. */
+       glLinkProgram(program);
+
+       glGetProgramiv(program, GL_LINK_STATUS, &status);
+       if(!status) {
+               glGetShaderInfoLog(program, sizeof(log), &length, log);
+               shader_print_errors("linking", log, FALLBACK_VERTEX_SHADER);
+               shader_print_errors("linking", log, FALLBACK_FRAGMENT_SHADER);
+               return 0;
+       }
+
+       return program;
+}
+
+bool Device::bind_fallback_display_space_shader(const float width, const float height)
+{
+       if(fallback_status == FALLBACK_SHADER_STATUS_ERROR) {
+               return false;
+       }
 
-                       vp = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
+       if(fallback_status == FALLBACK_SHADER_STATUS_NONE) {
+               fallback_shader_program = bind_fallback_shader();
+               fallback_status = FALLBACK_SHADER_STATUS_ERROR;
 
-                       basep = NULL;
+               if(fallback_shader_program == 0) {
+                       return false;
                }
-               else {
-                       basep = vbuffer;
-                       vp = vbuffer;
+
+               glUseProgram(fallback_shader_program);
+               image_texture_location = glGetUniformLocation(fallback_shader_program, "image_texture");
+               if(image_texture_location < 0) {
+                       LOG(ERROR) << "Shader doesn't containt the 'image_texture' uniform.";
+                       return false;
                }
 
-               if(vp) {
-                       /* texture coordinate - vertex pair */
-                       vp[0] = 0.0f;
-                       vp[1] = 0.0f;
-                       vp[2] = dx;
-                       vp[3] = dy;
-
-                       vp[4] = 1.0f;
-                       vp[5] = 0.0f;
-                       vp[6] = (float)width + dx;
-                       vp[7] = dy;
-
-                       vp[8] = 1.0f;
-                       vp[9] = 1.0f;
-                       vp[10] = (float)width + dx;
-                       vp[11] = (float)height + dy;
-
-                       vp[12] = 0.0f;
-                       vp[13] = 1.0f;
-                       vp[14] = dx;
-                       vp[15] = (float)height + dy;
-
-                       if(vertex_buffer)
-                               glUnmapBuffer(GL_ARRAY_BUFFER);
+               fullscreen_location = glGetUniformLocation(fallback_shader_program, "fullscreen");
+               if(fullscreen_location < 0) {
+                       LOG(ERROR) << "Shader doesn't containt the 'fullscreen' uniform.";
+                       return false;
                }
 
-               glTexCoordPointer(2, GL_FLOAT, 4 * sizeof(float), basep);
-               glVertexPointer(2, GL_FLOAT, 4 * sizeof(float), ((char *)basep) + 2 * sizeof(float));
+               fallback_status = FALLBACK_SHADER_STATUS_SUCCESS;
+       }
 
-               glEnableClientState(GL_VERTEX_ARRAY);
-               glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+       /* Run this every time. */
+       glUseProgram(fallback_shader_program);
+       glUniform1i(image_texture_location, 0);
+       glUniform2f(fullscreen_location, width, height);
+       return true;
+}
 
-               glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+void Device::draw_pixels(
+    device_memory& rgba, int y,
+    int w, int h, int width, int height,
+    int dx, int dy, int dw, int dh,
+    bool transparent, const DeviceDrawParams &draw_params)
+{
+       const bool use_fallback_shader = (draw_params.bind_display_space_shader_cb == NULL);
 
-               glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-               glDisableClientState(GL_VERTEX_ARRAY);
+       assert(rgba.type == MEM_PIXELS);
+       mem_copy_from(rgba, y, w, h, rgba.memory_elements_size(1));
 
-               if(vertex_buffer) {
-                       glBindBuffer(GL_ARRAY_BUFFER, 0);
-               }
+       GLuint texid;
+       glActiveTexture(GL_TEXTURE0);
+       glGenTextures(1, &texid);
+       glBindTexture(GL_TEXTURE_2D, texid);
 
-               if(draw_params.unbind_display_space_shader_cb) {
-                       draw_params.unbind_display_space_shader_cb();
-               }
+       if(rgba.data_type == TYPE_HALF) {
+               GLhalf *data_pointer = (GLhalf*)rgba.host_pointer;
+               data_pointer += 4 * y * w;
+               glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, w, h, 0, GL_RGBA, GL_HALF_FLOAT, data_pointer);
+       }
+       else {
+               uint8_t *data_pointer = (uint8_t*)rgba.host_pointer;
+               data_pointer += 4 * y * w;
+               glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data_pointer);
+       }
+
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+       if(transparent) {
+               glEnable(GL_BLEND);
+               glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+       }
 
-               glBindTexture(GL_TEXTURE_2D, 0);
-               glDisable(GL_TEXTURE_2D);
-               glDeleteTextures(1, &texid);
+       GLint shader_program;
+       if(use_fallback_shader) {
+               if(!bind_fallback_display_space_shader(dw, dh)) {
+                       return;
+               }
+               shader_program = fallback_shader_program;
        }
        else {
-               /* fallback for old graphics cards that don't support GLSL, half float,
-                * and non-power-of-two textures */
-               glPixelZoom((float)width/(float)w, (float)height/(float)h);
-               glRasterPos2f(dx, dy);
+               draw_params.bind_display_space_shader_cb();
+               glGetIntegerv(GL_CURRENT_PROGRAM, &shader_program);
+       }
+
+       if(!vertex_buffer) {
+               glGenBuffers(1, &vertex_buffer);
+       }
+
+       glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
+       /* invalidate old contents - avoids stalling if buffer is still waiting in queue to be rendered */
+       glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW);
+
+       float *vpointer = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
 
-               uint8_t *pixels = (uint8_t*)rgba.host_pointer;
+       if(vpointer) {
+               /* texture coordinate - vertex pair */
+               vpointer[0] = 0.0f;
+               vpointer[1] = 0.0f;
+               vpointer[2] = dx;
+               vpointer[3] = dy;
 
-               pixels += 4*y*w;
+               vpointer[4] = 1.0f;
+               vpointer[5] = 0.0f;
+               vpointer[6] = (float)width + dx;
+               vpointer[7] = dy;
 
-               glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+               vpointer[8] = 1.0f;
+               vpointer[9] = 1.0f;
+               vpointer[10] = (float)width + dx;
+               vpointer[11] = (float)height + dy;
 
-               glRasterPos2f(0.0f, 0.0f);
-               glPixelZoom(1.0f, 1.0f);
+               vpointer[12] = 0.0f;
+               vpointer[13] = 1.0f;
+               vpointer[14] = dx;
+               vpointer[15] = (float)height + dy;
+
+               if(vertex_buffer) {
+                       glUnmapBuffer(GL_ARRAY_BUFFER);
+               }
+       }
+
+       GLuint vertex_array_object;
+       GLuint position_attribute, texcoord_attribute;
+
+       glGenVertexArrays(1, &vertex_array_object);
+       glBindVertexArray(vertex_array_object);
+
+       texcoord_attribute = glGetAttribLocation(shader_program, "texCoord");
+       position_attribute = glGetAttribLocation(shader_program, "pos");
+
+       glEnableVertexAttribArray(texcoord_attribute);
+       glEnableVertexAttribArray(position_attribute);
+
+       glVertexAttribPointer(texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0);
+       glVertexAttribPointer(position_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)(sizeof(float) * 2));
+
+       glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+       if(vertex_buffer) {
+               glBindBuffer(GL_ARRAY_BUFFER, 0);
        }
 
-       if(transparent)
+       if(use_fallback_shader) {
+               glUseProgram(0);
+       }
+       else {
+               draw_params.unbind_display_space_shader_cb();
+       }
+
+       glBindTexture(GL_TEXTURE_2D, 0);
+       glDeleteTextures(1, &texid);
+
+       if(transparent) {
                glDisable(GL_BLEND);
+       }
 }
 
 Device *Device::create(DeviceInfo& info, Stats &stats, Profiler &profiler, bool background)