/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <jni.h> #include <stdlib.h> #include <time.h> #include "gles3jni.h" const Vertex QUAD[4] = { // Square with diagonal < 2 so that it fits in a [-1 .. 1]^2 square // regardless of rotation. {{-0.7f, -0.7f}, {0x00, 0xFF, 0x00}}, {{ 0.7f, -0.7f}, {0x00, 0x00, 0xFF}}, {{-0.7f, 0.7f}, {0xFF, 0x00, 0x00}}, {{ 0.7f, 0.7f}, {0xFF, 0xFF, 0xFF}}, }; bool checkGlError(const char* funcName) { GLint err = glGetError(); if (err != GL_NO_ERROR) { ALOGE("GL error after %s(): 0x%08x\n", funcName, err); return true; } return false; } GLuint createShader(GLenum shaderType, const char* src) { GLuint shader = glCreateShader(shaderType); if (!shader) { checkGlError("glCreateShader"); return 0; } glShaderSource(shader, 1, &src, NULL); GLint compiled = GL_FALSE; glCompileShader(shader); glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); if (!compiled) { GLint infoLogLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLen); if (infoLogLen > 0) { GLchar* infoLog = (GLchar*)malloc(infoLogLen); if (infoLog) { glGetShaderInfoLog(shader, infoLogLen, NULL, infoLog); ALOGE("Could not compile %s shader:\n%s\n", shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment", infoLog); free(infoLog); } } glDeleteShader(shader); return 0; } return shader; } GLuint createProgram(const char* vtxSrc, const char* fragSrc) { GLuint vtxShader = 0; GLuint fragShader = 0; GLuint program = 0; GLint linked = GL_FALSE; vtxShader = createShader(GL_VERTEX_SHADER, vtxSrc); if (!vtxShader) goto exit; fragShader = createShader(GL_FRAGMENT_SHADER, fragSrc); if (!fragShader) goto exit; program = glCreateProgram(); if (!program) { checkGlError("glCreateProgram"); goto exit; } glAttachShader(program, vtxShader); glAttachShader(program, fragShader); glLinkProgram(program); glGetProgramiv(program, GL_LINK_STATUS, &linked); if (!linked) { ALOGE("Could not link program"); GLint infoLogLen = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLen); if (infoLogLen) { GLchar* infoLog = (GLchar*)malloc(infoLogLen); if (infoLog) { glGetProgramInfoLog(program, infoLogLen, NULL, infoLog); ALOGE("Could not link program:\n%s\n", infoLog); free(infoLog); } } glDeleteProgram(program); program = 0; } exit: glDeleteShader(vtxShader); glDeleteShader(fragShader); return program; } static void printGlString(const char* name, GLenum s) { const char* v = (const char*)glGetString(s); ALOGV("GL %s: %s\n", name, v); } // ---------------------------------------------------------------------------- Renderer::Renderer() : mNumInstances(0), mLastFrameNs(0) { memset(mScale, 0, sizeof(mScale)); memset(mAngularVelocity, 0, sizeof(mAngularVelocity)); memset(mAngles, 0, sizeof(mAngles)); } Renderer::~Renderer() { } void Renderer::resize(int w, int h) { float* offsets = mapOffsetBuf(); calcSceneParams(w, h, offsets); unmapOffsetBuf(); for (unsigned int i = 0; i < mNumInstances; i++) { mAngles[i] = drand48() * TWO_PI; mAngularVelocity[i] = MAX_ROT_SPEED * (2.0*drand48() - 1.0); } mLastFrameNs = 0; glViewport(0, 0, w, h); } void Renderer::calcSceneParams(unsigned int w, unsigned int h, float* offsets) { // number of cells along the larger screen dimension const float NCELLS_MAJOR = MAX_INSTANCES_PER_SIDE; // cell size in scene space const float CELL_SIZE = 2.0f / NCELLS_MAJOR; // Calculations are done in "landscape", i.e. assuming dim[0] >= dim[1]. // Only at the end are values put in the opposite order if h > w. const float dim[2] = {fmaxf(w,h), fminf(w,h)}; const float aspect[2] = {dim[0] / dim[1], dim[1] / dim[0]}; const float scene2clip[2] = {1.0f, aspect[0]}; const int ncells[2] = { NCELLS_MAJOR, (int)floorf(NCELLS_MAJOR * aspect[1]) }; float centers[2][MAX_INSTANCES_PER_SIDE]; for (int d = 0; d < 2; d++) { float offset = -ncells[d] / NCELLS_MAJOR; // -1.0 for d=0 for (int i = 0; i < ncells[d]; i++) { centers[d][i] = scene2clip[d] * (CELL_SIZE*(i + 0.5f) + offset); } } int major = w >= h ? 0 : 1; int minor = w >= h ? 1 : 0; // outer product of centers[0] and centers[1] for (int i = 0; i < ncells[0]; i++) { for (int j = 0; j < ncells[1]; j++) { int idx = i*ncells[1] + j; offsets[2*idx + major] = centers[0][i]; offsets[2*idx + minor] = centers[1][j]; } } mNumInstances = ncells[0] * ncells[1]; mScale[major] = 0.5f * CELL_SIZE * scene2clip[0]; mScale[minor] = 0.5f * CELL_SIZE * scene2clip[1]; } void Renderer::step() { timespec now; clock_gettime(CLOCK_MONOTONIC, &now); uint64_t nowNs = now.tv_sec*1000000000ull + now.tv_nsec; if (mLastFrameNs > 0) { float dt = float(nowNs - mLastFrameNs) * 0.000000001f; for (unsigned int i = 0; i < mNumInstances; i++) { mAngles[i] += mAngularVelocity[i] * dt; if (mAngles[i] >= TWO_PI) { mAngles[i] -= TWO_PI; } else if (mAngles[i] <= -TWO_PI) { mAngles[i] += TWO_PI; } } float* transforms = mapTransformBuf(); for (unsigned int i = 0; i < mNumInstances; i++) { float s = sinf(mAngles[i]); float c = cosf(mAngles[i]); transforms[4*i + 0] = c * mScale[0]; transforms[4*i + 1] = s * mScale[1]; transforms[4*i + 2] = -s * mScale[0]; transforms[4*i + 3] = c * mScale[1]; } unmapTransformBuf(); } mLastFrameNs = nowNs; } void Renderer::render() { step(); glClearColor(0.2f, 0.2f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); draw(mNumInstances); checkGlError("Renderer::render"); } // ---------------------------------------------------------------------------- static Renderer* g_renderer = NULL; extern "C" { JNIEXPORT void JNICALL Java_com_android_gles3jni_GLES3JNILib_init(JNIEnv* env, jobject obj); JNIEXPORT void JNICALL Java_com_android_gles3jni_GLES3JNILib_resize(JNIEnv* env, jobject obj, jint width, jint height); JNIEXPORT void JNICALL Java_com_android_gles3jni_GLES3JNILib_step(JNIEnv* env, jobject obj); }; #if !defined(DYNAMIC_ES3) static GLboolean gl3stubInit() { return GL_TRUE; } #endif JNIEXPORT void JNICALL Java_com_android_gles3jni_GLES3JNILib_init(JNIEnv* env, jobject obj) { if (g_renderer) { delete g_renderer; g_renderer = NULL; } printGlString("Version", GL_VERSION); printGlString("Vendor", GL_VENDOR); printGlString("Renderer", GL_RENDERER); printGlString("Extensions", GL_EXTENSIONS); const char* versionStr = (const char*)glGetString(GL_VERSION); if (strstr(versionStr, "OpenGL ES 3.") && gl3stubInit()) { g_renderer = createES3Renderer(); } else if (strstr(versionStr, "OpenGL ES 2.")) { g_renderer = createES2Renderer(); } else { ALOGE("Unsupported OpenGL ES version"); } } JNIEXPORT void JNICALL Java_com_android_gles3jni_GLES3JNILib_resize(JNIEnv* env, jobject obj, jint width, jint height) { if (g_renderer) { g_renderer->resize(width, height); } } JNIEXPORT void JNICALL Java_com_android_gles3jni_GLES3JNILib_step(JNIEnv* env, jobject obj) { if (g_renderer) { g_renderer->render(); } }