Merge "drm_hwcomposer: lazily generate shaders for GLWorkerCompositor" into mnc-dr-dev
diff --git a/glworker.cpp b/glworker.cpp
index f72c0e1..0b98a51 100644
--- a/glworker.cpp
+++ b/glworker.cpp
@@ -149,13 +149,15 @@
 
 static AutoGLShader CompileAndCheckShader(GLenum type, unsigned source_count,
                                           const GLchar **sources,
-                                          std::string *shader_log) {
+                                          std::ostringstream *shader_log) {
   GLint status;
   AutoGLShader shader(glCreateShader(type));
   if (shader.get() == 0) {
-    *shader_log = "glCreateShader failed";
+    if (shader_log)
+      *shader_log << "Failed glCreateShader call";
     return 0;
   }
+
   glShaderSource(shader.get(), source_count, sources, NULL);
   glCompileShader(shader.get());
   glGetShaderiv(shader.get(), GL_COMPILE_STATUS, &status);
@@ -163,8 +165,14 @@
     if (shader_log) {
       GLint log_length;
       glGetShaderiv(shader.get(), GL_INFO_LOG_LENGTH, &log_length);
-      shader_log->resize(log_length);
-      glGetShaderInfoLog(shader.get(), log_length, NULL, &(*shader_log)[0]);
+      std::string info_log(log_length, ' ');
+      glGetShaderInfoLog(shader.get(), log_length, NULL, &info_log.front());
+      *shader_log << "Failed to compile shader:\n" << info_log.c_str()
+                  << "\nShader Source:\n";
+      for (unsigned i = 0; i < source_count; i++) {
+        *shader_log << sources[i];
+      }
+      *shader_log << "\n";
     }
     return 0;
   }
@@ -236,73 +244,52 @@
   return fragment_shader_stream.str();
 }
 
-static int GenerateShaders(std::vector<AutoGLProgram> *blend_programs) {
-  // Limits: GL_MAX_VARYING_COMPONENTS, GL_MAX_TEXTURE_IMAGE_UNITS,
-  // GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
-  int i, ret = 1;
-  GLint max_texture_images, status;
-  AutoGLShader vertex_shader, fragment_shader;
-  AutoGLProgram program;
-  std::string shader_log;
+static AutoGLProgram GenerateProgram(unsigned num_textures,
+                                     std::ostringstream *shader_log) {
+  std::string vertex_shader_string = GenerateVertexShader(num_textures);
+  const GLchar *vertex_shader_source = vertex_shader_string.c_str();
+  AutoGLShader vertex_shader = CompileAndCheckShader(
+      GL_VERTEX_SHADER, 1, &vertex_shader_source, shader_log);
+  if (!vertex_shader.get())
+    return 0;
 
-  glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_images);
+  std::string fragment_shader_string = GenerateFragmentShader(num_textures);
+  const GLchar *fragment_shader_source = fragment_shader_string.c_str();
+  AutoGLShader fragment_shader = CompileAndCheckShader(
+      GL_FRAGMENT_SHADER, 1, &fragment_shader_source, shader_log);
+  if (!fragment_shader.get())
+    return 0;
 
-  for (i = 1; i <= max_texture_images; i++) {
-    std::string vertex_shader_string = GenerateVertexShader(i);
-    const GLchar *vertex_shader_source = vertex_shader_string.c_str();
-    vertex_shader = CompileAndCheckShader(
-        GL_VERTEX_SHADER, 1, &vertex_shader_source, ret ? &shader_log : NULL);
-    if (!vertex_shader.get()) {
-      if (ret)
-        ALOGE("Failed to make vertex shader:\n%sshader source:\n%s",
-              shader_log.c_str(), vertex_shader_source);
-      break;
-    }
-
-    std::string fragment_shader_string = GenerateFragmentShader(i);
-    const GLchar *fragment_shader_source = fragment_shader_string.c_str();
-    fragment_shader =
-        CompileAndCheckShader(GL_FRAGMENT_SHADER, 1, &fragment_shader_source,
-                              ret ? &shader_log : NULL);
-    if (!fragment_shader.get()) {
-      if (ret)
-        ALOGE("Failed to make fragment shader:\n%sshader source:\n%s",
-              shader_log.c_str(), fragment_shader_source);
-      break;
-    }
-
-    program = AutoGLProgram(glCreateProgram());
-    if (!program.get()) {
-      if (ret)
-        ALOGE("Failed to create program %s", GetGLError());
-      break;
-    }
-
-    glAttachShader(program.get(), vertex_shader.get());
-    glAttachShader(program.get(), fragment_shader.get());
-    glBindAttribLocation(program.get(), 0, "vPosition");
-    glBindAttribLocation(program.get(), 1, "vTexCoords");
-    glLinkProgram(program.get());
-    glDetachShader(program.get(), vertex_shader.get());
-    glDetachShader(program.get(), fragment_shader.get());
-
-    glGetProgramiv(program.get(), GL_LINK_STATUS, &status);
-    if (!status) {
-      if (ret) {
-        GLint log_length;
-        glGetProgramiv(program.get(), GL_INFO_LOG_LENGTH, &log_length);
-        std::string program_log(log_length, ' ');
-        glGetProgramInfoLog(program.get(), log_length, NULL, &program_log[0]);
-        ALOGE("Failed to link program: \n%s", program_log.c_str());
-      }
-      break;
-    }
-
-    ret = 0;
-    blend_programs->emplace_back(std::move(program));
+  AutoGLProgram program(glCreateProgram());
+  if (!program.get()) {
+    if (shader_log)
+      *shader_log << "Failed to create program: " << GetGLError() << "\n";
+    return 0;
   }
 
-  return ret;
+  glAttachShader(program.get(), vertex_shader.get());
+  glAttachShader(program.get(), fragment_shader.get());
+  glBindAttribLocation(program.get(), 0, "vPosition");
+  glBindAttribLocation(program.get(), 1, "vTexCoords");
+  glLinkProgram(program.get());
+  glDetachShader(program.get(), vertex_shader.get());
+  glDetachShader(program.get(), fragment_shader.get());
+
+  GLint status;
+  glGetProgramiv(program.get(), GL_LINK_STATUS, &status);
+  if (!status) {
+    if (shader_log) {
+      GLint log_length;
+      glGetProgramiv(program.get(), GL_INFO_LOG_LENGTH, &log_length);
+      std::string program_log(log_length, ' ');
+      glGetProgramInfoLog(program.get(), log_length, NULL,
+                          &program_log.front());
+      *shader_log << "Failed to link program:\n" << program_log.c_str() << "\n";
+    }
+    return 0;
+  }
+
+  return program;
 }
 
 struct RenderingCommand {
@@ -552,7 +539,10 @@
   glBindBuffer(GL_ARRAY_BUFFER, 0);
   vertex_buffer_.reset(vertex_buffer);
 
-  if (GenerateShaders(&blend_programs_)) {
+  std::ostringstream shader_log;
+  blend_programs_.emplace_back(GenerateProgram(1, &shader_log));
+  if (blend_programs_.back().get() == 0) {
+    ALOGE("%s", shader_log.str().c_str());
     return 1;
   }
 
@@ -634,19 +624,18 @@
   glEnable(GL_SCISSOR_TEST);
 
   for (const RenderingCommand &cmd : commands) {
-    if (cmd.texture_count <= 0) {
+    if (cmd.texture_count == 0)
       continue;
-    }
 
     // TODO(zachr): handle the case of too many overlapping textures for one
     // area by falling back to rendering as many layers as possible using
     // multiple blending passes.
-    if (cmd.texture_count > blend_programs_.size()) {
+    GLint program = PrepareAndCacheProgram(cmd.texture_count);
+    if (program == 0) {
       ALOGE("Too many layers to render in one area");
       continue;
     }
 
-    GLint program = blend_programs_[cmd.texture_count - 1].get();
     glUseProgram(program);
     GLint gl_viewport_loc = glGetUniformLocation(program, "uViewport");
     GLint gl_crop_loc = glGetUniformLocation(program, "uLayerCrop");
@@ -799,4 +788,22 @@
   return &cached_framebuffers_.back();
 }
 
+GLint GLWorkerCompositor::PrepareAndCacheProgram(unsigned texture_count) {
+  if (blend_programs_.size() >= texture_count) {
+    GLint program = blend_programs_[texture_count - 1].get();
+    if (program != 0)
+      return program;
+  }
+
+  AutoGLProgram program = GenerateProgram(texture_count, NULL);
+  if (program.get() != 0) {
+    if (blend_programs_.size() < texture_count)
+      blend_programs_.resize(texture_count);
+    blend_programs_[texture_count - 1] = std::move(program);
+    return blend_programs_[texture_count - 1].get();
+  }
+
+  return 0;
+}
+
 }  // namespace android
diff --git a/glworker.h b/glworker.h
index 3201739..222cf6f 100644
--- a/glworker.h
+++ b/glworker.h
@@ -68,6 +68,8 @@
   CachedFramebuffer *PrepareAndCacheFramebuffer(
       const sp<GraphicBuffer> &framebuffer);
 
+  GLint PrepareAndCacheProgram(unsigned texture_count);
+
   EGLDisplay egl_display_;
   EGLContext egl_ctx_;