1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "Shader.h"
17 
18 #include <chrono>
19 #include <thread>
20 
21 #include "AndroidOut.h"
22 #include "Model.h"
23 #include "Utility.h"
24 using namespace std::chrono_literals;
25 
loadShader(const std::string & vertexSource,const std::string & fragmentSource,const std::string & positionAttributeName,const std::string & uvAttributeName,const std::string & projectionMatrixUniformName)26 Shader *Shader::loadShader(const std::string &vertexSource, const std::string &fragmentSource,
27                            const std::string &positionAttributeName,
28                            const std::string &uvAttributeName,
29                            const std::string &projectionMatrixUniformName) {
30     Shader *shader = nullptr;
31 
32     GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource);
33     if (!vertexShader) {
34         return nullptr;
35     }
36 
37     GLuint fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentSource);
38     if (!fragmentShader) {
39         glDeleteShader(vertexShader);
40         return nullptr;
41     }
42 
43     GLuint program = glCreateProgram();
44     if (program) {
45         aout << "Program created!" << std::endl;
46         glAttachShader(program, vertexShader);
47         glAttachShader(program, fragmentShader);
48 
49         glLinkProgram(program);
50         GLint linkStatus = GL_FALSE;
51         glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
52         if (linkStatus != GL_TRUE) {
53             GLint logLength = 0;
54             glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
55 
56             // If we fail to link the shader program, log the result for debugging
57             if (logLength) {
58                 GLchar *log = new GLchar[logLength];
59                 glGetProgramInfoLog(program, logLength, nullptr, log);
60                 aout << "Failed to link program with:\n" << log << std::endl;
61                 delete[] log;
62             }
63 
64             glDeleteProgram(program);
65         } else {
66             // Get the attribute and uniform locations by name. You may also choose to hardcode
67             // indices with layout= in your shader, but it is not done in this sample
68             GLint positionAttribute = glGetAttribLocation(program, positionAttributeName.c_str());
69             GLint uvAttribute = glGetAttribLocation(program, uvAttributeName.c_str());
70             GLint projectionMatrixUniform =
71                     glGetUniformLocation(program, projectionMatrixUniformName.c_str());
72 
73             // Only create a new shader if all the attributes are found.
74             if (positionAttribute != -1 && uvAttribute != -1 && projectionMatrixUniform != -1) {
75                 shader = new Shader(program, positionAttribute, uvAttribute,
76                                     projectionMatrixUniform);
77             } else {
78                 glDeleteProgram(program);
79             }
80         }
81     }
82 
83     // The shaders are no longer needed once the program is linked. Release their memory.
84     glDeleteShader(vertexShader);
85     glDeleteShader(fragmentShader);
86 
87     return shader;
88 }
89 
loadShader(GLenum shaderType,const std::string & shaderSource)90 GLuint Shader::loadShader(GLenum shaderType, const std::string &shaderSource) {
91     Utility::assertGlError();
92     GLuint shader = glCreateShader(shaderType);
93     if (shader) {
94         auto *shaderRawString = (GLchar *)shaderSource.c_str();
95         GLint shaderLength = shaderSource.length();
96         glShaderSource(shader, 1, &shaderRawString, &shaderLength);
97         glCompileShader(shader);
98 
99         GLint shaderCompiled = 0;
100         glGetShaderiv(shader, GL_COMPILE_STATUS, &shaderCompiled);
101 
102         // If the shader doesn't compile, log the result to the terminal for debugging
103         if (!shaderCompiled) {
104             GLint infoLength = 0;
105             glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLength);
106 
107             if (infoLength) {
108                 auto *infoLog = new GLchar[infoLength];
109                 glGetShaderInfoLog(shader, infoLength, nullptr, infoLog);
110                 aout << "Failed to compile with:\n" << infoLog << std::endl;
111                 delete[] infoLog;
112             }
113 
114             glDeleteShader(shader);
115             shader = 0;
116         }
117     }
118     return shader;
119 }
120 
activate() const121 void Shader::activate() const {
122     glUseProgram(program_);
123 }
124 
deactivate() const125 void Shader::deactivate() const {
126     glUseProgram(0);
127 }
128 
drawModel(const Model & model) const129 void Shader::drawModel(const Model &model) const {
130     // The position attribute is 3 floats
131     glVertexAttribPointer(position_,            // attrib
132                           3,                    // elements
133                           GL_FLOAT,             // of type float
134                           GL_FALSE,             // don't normalize
135                           sizeof(Vertex),       // stride is Vertex bytes
136                           model.getVertexData() // pull from the start of the vertex data
137     );
138     glEnableVertexAttribArray(position_);
139 
140     // The uv attribute is 2 floats
141     glVertexAttribPointer(uv_,            // attrib
142                           2,              // elements
143                           GL_FLOAT,       // of type float
144                           GL_FALSE,       // don't normalize
145                           sizeof(Vertex), // stride is Vertex bytes
146                           ((uint8_t *)model.getVertexData()) +
147                                   sizeof(Vector3) // offset Vector3 from the start
148     );
149     glEnableVertexAttribArray(uv_);
150 
151     // Setup the texture
152     glActiveTexture(GL_TEXTURE0);
153     glBindTexture(GL_TEXTURE_2D, model.getTexture().getTextureID());
154 
155     // Draw as indexed triangles
156     glDrawElements(GL_TRIANGLES, model.getIndexCount(), GL_UNSIGNED_SHORT, model.getIndexData());
157 
158     glDisableVertexAttribArray(uv_);
159     glDisableVertexAttribArray(position_);
160 }
161 
setProjectionMatrix(float * projectionMatrix) const162 void Shader::setProjectionMatrix(float *projectionMatrix) const {
163     glUniformMatrix4fv(projectionMatrix_, 1, false, projectionMatrix);
164 }