diff options
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | kmscube/CMakeLists.txt | 17 | ||||
-rw-r--r-- | kmscube/cube.h | 98 | ||||
-rw-r--r-- | kmscube/esTransform.c | 235 | ||||
-rw-r--r-- | kmscube/esUtil.h | 303 | ||||
-rw-r--r-- | kmscube/kmscube.cpp | 617 |
6 files changed, 1275 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 05ee49e..539c219 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ ENDIF(NOT CMAKE_BUILD_TYPE) set(LIBKMS_ENABLE_PYTHON ON CACHE BOOL "Enable Python wrappers") set(LIBKMS_ENABLE_LUA OFF CACHE BOOL "Enable Lua wrappers") +set(LIBKMS_ENABLE_KMSCUBE OFF CACHE BOOL "Enable kmscube") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") @@ -26,6 +27,10 @@ add_subdirectory(libkms++) add_subdirectory(libkmstest) add_subdirectory(tests) +if(LIBKMS_ENABLE_KMSCUBE) +add_subdirectory(kmscube) +endif() + if(LIBKMS_ENABLE_PYTHON) add_subdirectory(py) endif() diff --git a/kmscube/CMakeLists.txt b/kmscube/CMakeLists.txt new file mode 100644 index 0000000..4730d04 --- /dev/null +++ b/kmscube/CMakeLists.txt @@ -0,0 +1,17 @@ +include_directories(${LIBDRM_INCLUDE_DIRS}) +link_directories(${LIBDRM_LIBRARY_DIRS}) + +pkg_check_modules(GLESv2 glesv2 REQUIRED) +include_directories(${GLESv2_INCLUDE_DIRS}) +link_directories(${GLESv2_LIBRARY_DIRS}) + +pkg_check_modules(EGL egl REQUIRED) +include_directories(${EGL_INCLUDE_DIRS}) +link_directories(${EGL_LIBRARY_DIRS}) + +pkg_check_modules(GBM gbm REQUIRED) +include_directories(${GBM_INCLUDE_DIRS}) +link_directories(${GBM_LIBRARY_DIRS}) + +add_executable (kmscube kmscube.cpp esTransform.c esUtil.h cube.h) +target_link_libraries(kmscube kms++ kmstest ${LIBDRM_LIBRARIES} ${GLESv2_LIBRARIES} ${EGL_LIBRARIES} ${GBM_LIBRARIES}) diff --git a/kmscube/cube.h b/kmscube/cube.h new file mode 100644 index 0000000..9616c74 --- /dev/null +++ b/kmscube/cube.h @@ -0,0 +1,98 @@ +static const GLfloat vVertices[] = { + // front + -1.0f, -1.0f, +1.0f, // point blue + +1.0f, -1.0f, +1.0f, // point magenta + -1.0f, +1.0f, +1.0f, // point cyan + +1.0f, +1.0f, +1.0f, // point white + // back + +1.0f, -1.0f, -1.0f, // point red + -1.0f, -1.0f, -1.0f, // point black + +1.0f, +1.0f, -1.0f, // point yellow + -1.0f, +1.0f, -1.0f, // point green + // right + +1.0f, -1.0f, +1.0f, // point magenta + +1.0f, -1.0f, -1.0f, // point red + +1.0f, +1.0f, +1.0f, // point white + +1.0f, +1.0f, -1.0f, // point yellow + // left + -1.0f, -1.0f, -1.0f, // point black + -1.0f, -1.0f, +1.0f, // point blue + -1.0f, +1.0f, -1.0f, // point green + -1.0f, +1.0f, +1.0f, // point cyan + // top + -1.0f, +1.0f, +1.0f, // point cyan + +1.0f, +1.0f, +1.0f, // point white + -1.0f, +1.0f, -1.0f, // point green + +1.0f, +1.0f, -1.0f, // point yellow + // bottom + -1.0f, -1.0f, -1.0f, // point black + +1.0f, -1.0f, -1.0f, // point red + -1.0f, -1.0f, +1.0f, // point blue + +1.0f, -1.0f, +1.0f // point magenta +}; + +static const GLfloat vColors[] = { + // front + 0.0f, 0.0f, 1.0f, // blue + 1.0f, 0.0f, 1.0f, // magenta + 0.0f, 1.0f, 1.0f, // cyan + 1.0f, 1.0f, 1.0f, // white + // back + 1.0f, 0.0f, 0.0f, // red + 0.0f, 0.0f, 0.0f, // black + 1.0f, 1.0f, 0.0f, // yellow + 0.0f, 1.0f, 0.0f, // green + // right + 1.0f, 0.0f, 1.0f, // magenta + 1.0f, 0.0f, 0.0f, // red + 1.0f, 1.0f, 1.0f, // white + 1.0f, 1.0f, 0.0f, // yellow + // left + 0.0f, 0.0f, 0.0f, // black + 0.0f, 0.0f, 1.0f, // blue + 0.0f, 1.0f, 0.0f, // green + 0.0f, 1.0f, 1.0f, // cyan + // top + 0.0f, 1.0f, 1.0f, // cyan + 1.0f, 1.0f, 1.0f, // white + 0.0f, 1.0f, 0.0f, // green + 1.0f, 1.0f, 0.0f, // yellow + // bottom + 0.0f, 0.0f, 0.0f, // black + 1.0f, 0.0f, 0.0f, // red + 0.0f, 0.0f, 1.0f, // blue + 1.0f, 0.0f, 1.0f // magenta +}; + +static const GLfloat vNormals[] = { + // front + +0.0f, +0.0f, +1.0f, // forward + +0.0f, +0.0f, +1.0f, // forward + +0.0f, +0.0f, +1.0f, // forward + +0.0f, +0.0f, +1.0f, // forward + // back + +0.0f, +0.0f, -1.0f, // backbard + +0.0f, +0.0f, -1.0f, // backbard + +0.0f, +0.0f, -1.0f, // backbard + +0.0f, +0.0f, -1.0f, // backbard + // right + +1.0f, +0.0f, +0.0f, // right + +1.0f, +0.0f, +0.0f, // right + +1.0f, +0.0f, +0.0f, // right + +1.0f, +0.0f, +0.0f, // right + // left + -1.0f, +0.0f, +0.0f, // left + -1.0f, +0.0f, +0.0f, // left + -1.0f, +0.0f, +0.0f, // left + -1.0f, +0.0f, +0.0f, // left + // top + +0.0f, +1.0f, +0.0f, // up + +0.0f, +1.0f, +0.0f, // up + +0.0f, +1.0f, +0.0f, // up + +0.0f, +1.0f, +0.0f, // up + // bottom + +0.0f, -1.0f, +0.0f, // down + +0.0f, -1.0f, +0.0f, // down + +0.0f, -1.0f, +0.0f, // down + +0.0f, -1.0f, +0.0f // down +}; diff --git a/kmscube/esTransform.c b/kmscube/esTransform.c new file mode 100644 index 0000000..bb69d89 --- /dev/null +++ b/kmscube/esTransform.c @@ -0,0 +1,235 @@ +// +// Book: OpenGL(R) ES 2.0 Programming Guide +// Authors: Aaftab Munshi, Dan Ginsburg, Dave Shreiner +// ISBN-10: 0321502795 +// ISBN-13: 9780321502797 +// Publisher: Addison-Wesley Professional +// URLs: http://safari.informit.com/9780321563835 +// http://www.opengles-book.com +// + +/* + * (c) 2009 Aaftab Munshi, Dan Ginsburg, Dave Shreiner + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +// ESUtil.c +// +// A utility library for OpenGL ES. This library provides a +// basic common framework for the example applications in the +// OpenGL ES 2.0 Programming Guide. +// + +/// +// Includes +// +#include "esUtil.h" +#include <math.h> +#include <string.h> + +#define PI 3.1415926535897932384626433832795f + +void ESUTIL_API +esScale(ESMatrix *result, GLfloat sx, GLfloat sy, GLfloat sz) +{ + result->m[0][0] *= sx; + result->m[0][1] *= sx; + result->m[0][2] *= sx; + result->m[0][3] *= sx; + + result->m[1][0] *= sy; + result->m[1][1] *= sy; + result->m[1][2] *= sy; + result->m[1][3] *= sy; + + result->m[2][0] *= sz; + result->m[2][1] *= sz; + result->m[2][2] *= sz; + result->m[2][3] *= sz; +} + +void ESUTIL_API +esTranslate(ESMatrix *result, GLfloat tx, GLfloat ty, GLfloat tz) +{ + result->m[3][0] += (result->m[0][0] * tx + result->m[1][0] * ty + result->m[2][0] * tz); + result->m[3][1] += (result->m[0][1] * tx + result->m[1][1] * ty + result->m[2][1] * tz); + result->m[3][2] += (result->m[0][2] * tx + result->m[1][2] * ty + result->m[2][2] * tz); + result->m[3][3] += (result->m[0][3] * tx + result->m[1][3] * ty + result->m[2][3] * tz); +} + +void ESUTIL_API +esRotate(ESMatrix *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z) +{ + GLfloat sinAngle, cosAngle; + GLfloat mag = sqrtf(x * x + y * y + z * z); + + sinAngle = sinf ( angle * PI / 180.0f ); + cosAngle = cosf ( angle * PI / 180.0f ); + if ( mag > 0.0f ) + { + GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs; + GLfloat oneMinusCos; + ESMatrix rotMat; + + x /= mag; + y /= mag; + z /= mag; + + xx = x * x; + yy = y * y; + zz = z * z; + xy = x * y; + yz = y * z; + zx = z * x; + xs = x * sinAngle; + ys = y * sinAngle; + zs = z * sinAngle; + oneMinusCos = 1.0f - cosAngle; + + rotMat.m[0][0] = (oneMinusCos * xx) + cosAngle; + rotMat.m[0][1] = (oneMinusCos * xy) - zs; + rotMat.m[0][2] = (oneMinusCos * zx) + ys; + rotMat.m[0][3] = 0.0F; + + rotMat.m[1][0] = (oneMinusCos * xy) + zs; + rotMat.m[1][1] = (oneMinusCos * yy) + cosAngle; + rotMat.m[1][2] = (oneMinusCos * yz) - xs; + rotMat.m[1][3] = 0.0F; + + rotMat.m[2][0] = (oneMinusCos * zx) - ys; + rotMat.m[2][1] = (oneMinusCos * yz) + xs; + rotMat.m[2][2] = (oneMinusCos * zz) + cosAngle; + rotMat.m[2][3] = 0.0F; + + rotMat.m[3][0] = 0.0F; + rotMat.m[3][1] = 0.0F; + rotMat.m[3][2] = 0.0F; + rotMat.m[3][3] = 1.0F; + + esMatrixMultiply( result, &rotMat, result ); + } +} + +void ESUTIL_API +esFrustum(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ) +{ + float deltaX = right - left; + float deltaY = top - bottom; + float deltaZ = farZ - nearZ; + ESMatrix frust; + + if ( (nearZ <= 0.0f) || (farZ <= 0.0f) || + (deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f) ) + return; + + frust.m[0][0] = 2.0f * nearZ / deltaX; + frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f; + + frust.m[1][1] = 2.0f * nearZ / deltaY; + frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f; + + frust.m[2][0] = (right + left) / deltaX; + frust.m[2][1] = (top + bottom) / deltaY; + frust.m[2][2] = -(nearZ + farZ) / deltaZ; + frust.m[2][3] = -1.0f; + + frust.m[3][2] = -2.0f * nearZ * farZ / deltaZ; + frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f; + + esMatrixMultiply(result, &frust, result); +} + + +void ESUTIL_API +esPerspective(ESMatrix *result, float fovy, float aspect, float nearZ, float farZ) +{ + GLfloat frustumW, frustumH; + + frustumH = tanf( fovy / 360.0f * PI ) * nearZ; + frustumW = frustumH * aspect; + + esFrustum( result, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ ); +} + +void ESUTIL_API +esOrtho(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ) +{ + float deltaX = right - left; + float deltaY = top - bottom; + float deltaZ = farZ - nearZ; + ESMatrix ortho; + + if ( (deltaX == 0.0f) || (deltaY == 0.0f) || (deltaZ == 0.0f) ) + return; + + esMatrixLoadIdentity(&ortho); + ortho.m[0][0] = 2.0f / deltaX; + ortho.m[3][0] = -(right + left) / deltaX; + ortho.m[1][1] = 2.0f / deltaY; + ortho.m[3][1] = -(top + bottom) / deltaY; + ortho.m[2][2] = -2.0f / deltaZ; + ortho.m[3][2] = -(nearZ + farZ) / deltaZ; + + esMatrixMultiply(result, &ortho, result); +} + + +void ESUTIL_API +esMatrixMultiply(ESMatrix *result, ESMatrix *srcA, ESMatrix *srcB) +{ + ESMatrix tmp; + int i; + + for (i=0; i<4; i++) + { + tmp.m[i][0] = (srcA->m[i][0] * srcB->m[0][0]) + + (srcA->m[i][1] * srcB->m[1][0]) + + (srcA->m[i][2] * srcB->m[2][0]) + + (srcA->m[i][3] * srcB->m[3][0]) ; + + tmp.m[i][1] = (srcA->m[i][0] * srcB->m[0][1]) + + (srcA->m[i][1] * srcB->m[1][1]) + + (srcA->m[i][2] * srcB->m[2][1]) + + (srcA->m[i][3] * srcB->m[3][1]) ; + + tmp.m[i][2] = (srcA->m[i][0] * srcB->m[0][2]) + + (srcA->m[i][1] * srcB->m[1][2]) + + (srcA->m[i][2] * srcB->m[2][2]) + + (srcA->m[i][3] * srcB->m[3][2]) ; + + tmp.m[i][3] = (srcA->m[i][0] * srcB->m[0][3]) + + (srcA->m[i][1] * srcB->m[1][3]) + + (srcA->m[i][2] * srcB->m[2][3]) + + (srcA->m[i][3] * srcB->m[3][3]) ; + } + memcpy(result, &tmp, sizeof(ESMatrix)); +} + + +void ESUTIL_API +esMatrixLoadIdentity(ESMatrix *result) +{ + memset(result, 0x0, sizeof(ESMatrix)); + result->m[0][0] = 1.0f; + result->m[1][1] = 1.0f; + result->m[2][2] = 1.0f; + result->m[3][3] = 1.0f; +} + diff --git a/kmscube/esUtil.h b/kmscube/esUtil.h new file mode 100644 index 0000000..f734382 --- /dev/null +++ b/kmscube/esUtil.h @@ -0,0 +1,303 @@ +// +// Book: OpenGL(R) ES 2.0 Programming Guide +// Authors: Aaftab Munshi, Dan Ginsburg, Dave Shreiner +// ISBN-10: 0321502795 +// ISBN-13: 9780321502797 +// Publisher: Addison-Wesley Professional +// URLs: http://safari.informit.com/9780321563835 +// http://www.opengles-book.com +// + +/* + * (c) 2009 Aaftab Munshi, Dan Ginsburg, Dave Shreiner + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +// +/// \file ESUtil.h +/// \brief A utility library for OpenGL ES. This library provides a +/// basic common framework for the example applications in the +/// OpenGL ES 2.0 Programming Guide. +// +#ifndef ESUTIL_H +#define ESUTIL_H + +/// +// Includes +// +#include <GLES2/gl2.h> +#include <EGL/egl.h> + +#ifdef __cplusplus + +extern "C" { +#endif + + +/// +// Macros +// +#define ESUTIL_API +#define ESCALLBACK + + +/// esCreateWindow flag - RGB color buffer +#define ES_WINDOW_RGB 0 +/// esCreateWindow flag - ALPHA color buffer +#define ES_WINDOW_ALPHA 1 +/// esCreateWindow flag - depth buffer +#define ES_WINDOW_DEPTH 2 +/// esCreateWindow flag - stencil buffer +#define ES_WINDOW_STENCIL 4 +/// esCreateWindow flat - multi-sample buffer +#define ES_WINDOW_MULTISAMPLE 8 + + +/// +// Types +// + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +typedef struct +{ + GLfloat m[4][4]; +} ESMatrix; + +typedef struct _escontext +{ + /// Put your user data here... + void* userData; + + /// Window width + GLint width; + + /// Window height + GLint height; + + /// Window handle + EGLNativeWindowType hWnd; + + /// EGL display + EGLDisplay eglDisplay; + + /// EGL context + EGLContext eglContext; + + /// EGL surface + EGLSurface eglSurface; + + /// Callbacks + void (ESCALLBACK *drawFunc) ( struct _escontext * ); + void (ESCALLBACK *keyFunc) ( struct _escontext *, unsigned char, int, int ); + void (ESCALLBACK *updateFunc) ( struct _escontext *, float deltaTime ); +} ESContext; + + +/// +// Public Functions +// + +// +/// +/// \brief Initialize ES framework context. This must be called before calling any other functions. +/// \param esContext Application context +// +void ESUTIL_API esInitContext ( ESContext *esContext ); + +// +/// \brief Create a window with the specified parameters +/// \param esContext Application context +/// \param title Name for title bar of window +/// \param width Width in pixels of window to create +/// \param height Height in pixels of window to create +/// \param flags Bitfield for the window creation flags +/// ES_WINDOW_RGB - specifies that the color buffer should have R,G,B channels +/// ES_WINDOW_ALPHA - specifies that the color buffer should have alpha +/// ES_WINDOW_DEPTH - specifies that a depth buffer should be created +/// ES_WINDOW_STENCIL - specifies that a stencil buffer should be created +/// ES_WINDOW_MULTISAMPLE - specifies that a multi-sample buffer should be created +/// \return GL_TRUE if window creation is succesful, GL_FALSE otherwise +GLboolean ESUTIL_API esCreateWindow ( ESContext *esContext, const char *title, GLint width, GLint height, GLuint flags ); + +// +/// \brief Start the main loop for the OpenGL ES application +/// \param esContext Application context +// +void ESUTIL_API esMainLoop ( ESContext *esContext ); + +// +/// \brief Register a draw callback function to be used to render each frame +/// \param esContext Application context +/// \param drawFunc Draw callback function that will be used to render the scene +// +void ESUTIL_API esRegisterDrawFunc ( ESContext *esContext, void (ESCALLBACK *drawFunc) ( ESContext* ) ); + +// +/// \brief Register an update callback function to be used to update on each time step +/// \param esContext Application context +/// \param updateFunc Update callback function that will be used to render the scene +// +void ESUTIL_API esRegisterUpdateFunc ( ESContext *esContext, void (ESCALLBACK *updateFunc) ( ESContext*, float ) ); + +// +/// \brief Register an keyboard input processing callback function +/// \param esContext Application context +/// \param keyFunc Key callback function for application processing of keyboard input +// +void ESUTIL_API esRegisterKeyFunc ( ESContext *esContext, + void (ESCALLBACK *drawFunc) ( ESContext*, unsigned char, int, int ) ); +// +/// \brief Log a message to the debug output for the platform +/// \param formatStr Format string for error log. +// +void ESUTIL_API esLogMessage ( const char *formatStr, ... ); + +// +/// +/// \brief Load a shader, check for compile errors, print error messages to output log +/// \param type Type of shader (GL_VERTEX_SHADER or GL_FRAGMENT_SHADER) +/// \param shaderSrc Shader source string +/// \return A new shader object on success, 0 on failure +// +GLuint ESUTIL_API esLoadShader ( GLenum type, const char *shaderSrc ); + +// +/// +/// \brief Load a vertex and fragment shader, create a program object, link program. +/// Errors output to log. +/// \param vertShaderSrc Vertex shader source code +/// \param fragShaderSrc Fragment shader source code +/// \return A new program object linked with the vertex/fragment shader pair, 0 on failure +// +GLuint ESUTIL_API esLoadProgram ( const char *vertShaderSrc, const char *fragShaderSrc ); + + +// +/// \brief Generates geometry for a sphere. Allocates memory for the vertex data and stores +/// the results in the arrays. Generate index list for a TRIANGLE_STRIP +/// \param numSlices The number of slices in the sphere +/// \param vertices If not NULL, will contain array of float3 positions +/// \param normals If not NULL, will contain array of float3 normals +/// \param texCoords If not NULL, will contain array of float2 texCoords +/// \param indices If not NULL, will contain the array of indices for the triangle strip +/// \return The number of indices required for rendering the buffers (the number of indices stored in the indices array +/// if it is not NULL ) as a GL_TRIANGLE_STRIP +// +int ESUTIL_API esGenSphere ( int numSlices, float radius, GLfloat **vertices, GLfloat **normals, + GLfloat **texCoords, GLuint **indices ); + +// +/// \brief Generates geometry for a cube. Allocates memory for the vertex data and stores +/// the results in the arrays. Generate index list for a TRIANGLES +/// \param scale The size of the cube, use 1.0 for a unit cube. +/// \param vertices If not NULL, will contain array of float3 positions +/// \param normals If not NULL, will contain array of float3 normals +/// \param texCoords If not NULL, will contain array of float2 texCoords +/// \param indices If not NULL, will contain the array of indices for the triangle strip +/// \return The number of indices required for rendering the buffers (the number of indices stored in the indices array +/// if it is not NULL ) as a GL_TRIANGLES +// +int ESUTIL_API esGenCube ( float scale, GLfloat **vertices, GLfloat **normals, + GLfloat **texCoords, GLuint **indices ); + +// +/// \brief Loads a 24-bit TGA image from a file +/// \param fileName Name of the file on disk +/// \param width Width of loaded image in pixels +/// \param height Height of loaded image in pixels +/// \return Pointer to loaded image. NULL on failure. +// +char* ESUTIL_API esLoadTGA ( char *fileName, int *width, int *height ); + + +// +/// \brief multiply matrix specified by result with a scaling matrix and return new matrix in result +/// \param result Specifies the input matrix. Scaled matrix is returned in result. +/// \param sx, sy, sz Scale factors along the x, y and z axes respectively +// +void ESUTIL_API esScale(ESMatrix *result, GLfloat sx, GLfloat sy, GLfloat sz); + +// +/// \brief multiply matrix specified by result with a translation matrix and return new matrix in result +/// \param result Specifies the input matrix. Translated matrix is returned in result. +/// \param tx, ty, tz Scale factors along the x, y and z axes respectively +// +void ESUTIL_API esTranslate(ESMatrix *result, GLfloat tx, GLfloat ty, GLfloat tz); + +// +/// \brief multiply matrix specified by result with a rotation matrix and return new matrix in result +/// \param result Specifies the input matrix. Rotated matrix is returned in result. +/// \param angle Specifies the angle of rotation, in degrees. +/// \param x, y, z Specify the x, y and z coordinates of a vector, respectively +// +void ESUTIL_API esRotate(ESMatrix *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); + +// +// \brief multiply matrix specified by result with a perspective matrix and return new matrix in result +/// \param result Specifies the input matrix. new matrix is returned in result. +/// \param left, right Coordinates for the left and right vertical clipping planes +/// \param bottom, top Coordinates for the bottom and top horizontal clipping planes +/// \param nearZ, farZ Distances to the near and far depth clipping planes. Both distances must be positive. +// +void ESUTIL_API esFrustum(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ); + +// +/// \brief multiply matrix specified by result with a perspective matrix and return new matrix in result +/// \param result Specifies the input matrix. new matrix is returned in result. +/// \param fovy Field of view y angle in degrees +/// \param aspect Aspect ratio of screen +/// \param nearZ Near plane distance +/// \param farZ Far plane distance +// +void ESUTIL_API esPerspective(ESMatrix *result, float fovy, float aspect, float nearZ, float farZ); + +// +/// \brief multiply matrix specified by result with a perspective matrix and return new matrix in result +/// \param result Specifies the input matrix. new matrix is returned in result. +/// \param left, right Coordinates for the left and right vertical clipping planes +/// \param bottom, top Coordinates for the bottom and top horizontal clipping planes +/// \param nearZ, farZ Distances to the near and far depth clipping planes. These values are negative if plane is behind the viewer +// +void ESUTIL_API esOrtho(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ); + +// +/// \brief perform the following operation - result matrix = srcA matrix * srcB matrix +/// \param result Returns multiplied matrix +/// \param srcA, srcB Input matrices to be multiplied +// +void ESUTIL_API esMatrixMultiply(ESMatrix *result, ESMatrix *srcA, ESMatrix *srcB); + +// +//// \brief return an indentity matrix +//// \param result returns identity matrix +// +void ESUTIL_API esMatrixLoadIdentity(ESMatrix *result); + +#ifdef __cplusplus +} +#endif + +#endif // ESUTIL_H diff --git a/kmscube/kmscube.cpp b/kmscube/kmscube.cpp new file mode 100644 index 0000000..d2125a6 --- /dev/null +++ b/kmscube/kmscube.cpp @@ -0,0 +1,617 @@ +/* + * Copyright (c) 2012 Arvin Schnell <arvin.schnell@gmail.com> + * Copyright (c) 2012 Rob Clark <rob@ti.com> + * Copyright (c) 2015 Tomi Valkeinen <tomi.valkeinen@ti.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* Based on a egl cube test app originally written by Arvin Schnell */ + +#include <chrono> +#include <cstdio> +#include <vector> +#include <memory> +#include <algorithm> + +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <gbm.h> + +#include "esUtil.h" + +#include <kms++.h> +#include "test.h" + +using namespace kms; +using namespace std; + +static bool s_verbose; +static int s_flip_pending; +static bool s_need_exit; + +class GbmDevice +{ +public: + GbmDevice(Card& card) + { + m_dev = gbm_create_device(card.fd()); + FAIL_IF(!m_dev, "failed to create gbm device"); + } + + ~GbmDevice() + { + gbm_device_destroy(m_dev); + } + + GbmDevice(const GbmDevice& other) = delete; + GbmDevice& operator=(const GbmDevice& other) = delete; + + operator struct gbm_device*() const { return m_dev; } + +private: + struct gbm_device* m_dev; +}; + +class GbmSurface +{ +public: + GbmSurface(GbmDevice& gdev, int width, int height) + { + m_surface = gbm_surface_create(gdev, width, height, + GBM_FORMAT_XRGB8888, + GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + FAIL_IF(!m_surface, "failed to create gbm surface"); + } + + ~GbmSurface() + { + gbm_surface_destroy(m_surface); + } + + GbmSurface(const GbmSurface& other) = delete; + GbmSurface& operator=(const GbmSurface& other) = delete; + + operator struct gbm_surface*() const { return m_surface; } +private: + struct gbm_surface* m_surface; +}; + +struct GLState { + GLState(GbmDevice& gdev) + { + EGLint major, minor, n; + GLuint vertex_shader, fragment_shader; + GLint ret; + EGLBoolean b; + +#include "cube.h" + + static const EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + static const EGLint config_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 0, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + static const char *vertex_shader_source = + "uniform mat4 modelviewMatrix; \n" + "uniform mat4 modelviewprojectionMatrix;\n" + "uniform mat3 normalMatrix; \n" + " \n" + "attribute vec4 in_position; \n" + "attribute vec3 in_normal; \n" + "attribute vec4 in_color; \n" + "\n" + "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n" + " \n" + "varying vec4 vVaryingColor; \n" + " \n" + "void main() \n" + "{ \n" + " gl_Position = modelviewprojectionMatrix * in_position;\n" + " vec3 vEyeNormal = normalMatrix * in_normal;\n" + " vec4 vPosition4 = modelviewMatrix * in_position;\n" + " vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n" + " vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n" + " float diff = max(0.0, dot(vEyeNormal, vLightDir));\n" + " vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n" + "} \n"; + + static const char *fragment_shader_source = + "precision mediump float; \n" + " \n" + "varying vec4 vVaryingColor; \n" + " \n" + "void main() \n" + "{ \n" + " gl_FragColor = vVaryingColor; \n" + "} \n"; + + m_display = eglGetDisplay(gdev); + FAIL_IF(!m_display, "failed to get egl display"); + + b = eglInitialize(m_display, &major, &minor); + FAIL_IF(!b, "failed to initialize"); + + if (s_verbose) { + printf("Using display %p with EGL version %d.%d\n", m_display, major, minor); + + printf("EGL_VENDOR: %s\n", eglQueryString(m_display, EGL_VENDOR)); + printf("EGL_VERSION: %s\n", eglQueryString(m_display, EGL_VERSION)); + printf("EGL_EXTENSIONS: %s\n", eglQueryString(m_display, EGL_EXTENSIONS)); + printf("EGL_CLIENT_APIS: %s\n", eglQueryString(m_display, EGL_CLIENT_APIS)); + } + + b = eglBindAPI(EGL_OPENGL_ES_API); + FAIL_IF(!b, "failed to bind api EGL_OPENGL_ES_API"); + + b = eglChooseConfig(m_display, config_attribs, &m_config, 1, &n); + FAIL_IF(!b || n != 1, "failed to choose config"); + + auto getconf = [this](EGLint a) { EGLint v = -1; eglGetConfigAttrib(m_display, m_config, a, &v); return v; }; + + if (s_verbose) { + printf("EGL Config %d: color buf %d/%d/%d/%d = %d, depth %d, stencil %d\n", + getconf(EGL_CONFIG_ID), + getconf(EGL_ALPHA_SIZE), + getconf(EGL_RED_SIZE), + getconf(EGL_GREEN_SIZE), + getconf(EGL_BLUE_SIZE), + getconf(EGL_BUFFER_SIZE), + getconf(EGL_DEPTH_SIZE), + getconf(EGL_STENCIL_SIZE)); + } + + m_context = eglCreateContext(m_display, m_config, EGL_NO_CONTEXT, context_attribs); + FAIL_IF(!m_context, "failed to create context"); + + eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_context); + + if (s_verbose) { + printf("GL_VENDOR: %s\n", glGetString(GL_VENDOR)); + printf("GL_VERSION: %s\n", glGetString(GL_VERSION)); + printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER)); + printf("GL_EXTENSIONS: %s\n", glGetString(GL_EXTENSIONS)); + } + + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + + glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL); + glCompileShader(vertex_shader); + + glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret); + FAIL_IF(!ret, "vertex shader compilation failed!"); + + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + + glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL); + glCompileShader(fragment_shader); + + glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret); + FAIL_IF(!ret, "fragment shader compilation failed!"); + + GLuint program = glCreateProgram(); + + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + + glBindAttribLocation(program, 0, "in_position"); + glBindAttribLocation(program, 1, "in_normal"); + glBindAttribLocation(program, 2, "in_color"); + + glLinkProgram(program); + + glGetProgramiv(program, GL_LINK_STATUS, &ret); + FAIL_IF(!ret, "program linking failed!"); + + glUseProgram(program); + + m_modelviewmatrix = glGetUniformLocation(program, "modelviewMatrix"); + m_modelviewprojectionmatrix = glGetUniformLocation(program, "modelviewprojectionMatrix"); + m_normalmatrix = glGetUniformLocation(program, "normalMatrix"); + + glEnable(GL_CULL_FACE); + + GLintptr positionsoffset = 0; + GLintptr colorsoffset = sizeof(vVertices); + GLintptr normalsoffset = sizeof(vVertices) + sizeof(vColors); + GLuint vbo; + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW); + glBufferSubData(GL_ARRAY_BUFFER, positionsoffset, sizeof(vVertices), &vVertices[0]); + glBufferSubData(GL_ARRAY_BUFFER, colorsoffset, sizeof(vColors), &vColors[0]); + glBufferSubData(GL_ARRAY_BUFFER, normalsoffset, sizeof(vNormals), &vNormals[0]); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)positionsoffset); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)normalsoffset); + glEnableVertexAttribArray(1); + glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)colorsoffset); + glEnableVertexAttribArray(2); + } + + ~GLState() + { + eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(m_display); + } + + GLState(const GLState& other) = delete; + GLState& operator=(const GLState& other) = delete; + + EGLDisplay display() const { return m_display; } + EGLConfig config() const { return m_config; } + EGLContext context() const { return m_context; } + + GLint modelviewmatrix() const { return m_modelviewmatrix; } + GLint modelviewprojectionmatrix() const { return m_modelviewprojectionmatrix; } + GLint normalmatrix() const { return m_normalmatrix; } + +private: + EGLDisplay m_display; + EGLConfig m_config; + EGLContext m_context; + GLint m_modelviewmatrix, m_modelviewprojectionmatrix, m_normalmatrix; +}; + +struct Surface +{ + Surface(Card& card, GbmDevice& gdev, const GLState& gl, int width, int height) + : card(card), gdev(gdev), gl(gl), gsurface(gdev, width, height), m_width(width), m_height(height), + bo_prev(0), bo_next(0) + { + esurface = eglCreateWindowSurface(gl.display(), gl.config(), gsurface, NULL); + FAIL_IF(esurface == EGL_NO_SURFACE, "failed to create egl surface"); + } + + ~Surface() + { + if (bo_next) + gbm_surface_release_buffer(gsurface, bo_next); + eglDestroySurface(gl.display(), esurface); + } + + void make_current() + { + eglMakeCurrent(gl.display(), esurface, esurface, gl.context()); + glViewport(0, 0, m_width, m_height); + } + + void clear() + { + glClearColor(0.5, 0.5, 0.5, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + } + + static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data) + { + auto fb = reinterpret_cast<Framebuffer*>(data); + delete fb; + } + + static Framebuffer* drm_fb_get_from_bo(struct gbm_bo *bo, Card& card) + { + auto fb = reinterpret_cast<Framebuffer*>(gbm_bo_get_user_data(bo)); + if (fb) + return fb; + + uint32_t width = gbm_bo_get_width(bo); + uint32_t height = gbm_bo_get_height(bo); + uint32_t stride = gbm_bo_get_stride(bo); + uint32_t handle = gbm_bo_get_handle(bo).u32; + + fb = new ExtFramebuffer(card, width, height, 24, 32, stride, handle); + + gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback); + + return fb; + } + + struct Framebuffer* lock_next() + { + bo_prev = bo_next; + eglSwapBuffers(gl.display(), esurface); + bo_next = gbm_surface_lock_front_buffer(gsurface); + FAIL_IF(!bo_next, "could not lock gbm buffer"); + return drm_fb_get_from_bo(bo_next, card); + } + + void free_prev() + { + if (bo_prev) { + gbm_surface_release_buffer(gsurface, bo_prev); + bo_prev = 0; + } + } + + Card& card; + GbmDevice& gdev; + const GLState& gl; + + GbmSurface gsurface; + EGLSurface esurface; + + int m_width; + int m_height; + + struct gbm_bo* bo_prev; + struct gbm_bo* bo_next; +}; + +static void draw(uint32_t framenum, Surface& surface) +{ + const GLState& gl = surface.gl; + + ESMatrix modelview; + + esMatrixLoadIdentity(&modelview); + esTranslate(&modelview, 0.0f, 0.0f, -8.0f); + esRotate(&modelview, 45.0f + (0.75f * framenum), 1.0f, 0.0f, 0.0f); + esRotate(&modelview, 45.0f - (0.5f * framenum), 0.0f, 1.0f, 0.0f); + esRotate(&modelview, 10.0f + (0.45f * framenum), 0.0f, 0.0f, 1.0f); + + GLfloat aspect = (GLfloat)(surface.m_height) / (GLfloat)(surface.m_width); + + ESMatrix projection; + esMatrixLoadIdentity(&projection); + esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f); + + ESMatrix modelviewprojection; + esMatrixLoadIdentity(&modelviewprojection); + esMatrixMultiply(&modelviewprojection, &modelview, &projection); + + float normal[9]; + normal[0] = modelview.m[0][0]; + normal[1] = modelview.m[0][1]; + normal[2] = modelview.m[0][2]; + normal[3] = modelview.m[1][0]; + normal[4] = modelview.m[1][1]; + normal[5] = modelview.m[1][2]; + normal[6] = modelview.m[2][0]; + normal[7] = modelview.m[2][1]; + normal[8] = modelview.m[2][2]; + + glUniformMatrix4fv(gl.modelviewmatrix(), 1, GL_FALSE, &modelview.m[0][0]); + glUniformMatrix4fv(gl.modelviewprojectionmatrix(), 1, GL_FALSE, &modelviewprojection.m[0][0]); + glUniformMatrix3fv(gl.normalmatrix(), 1, GL_FALSE, normal); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glDrawArrays(GL_TRIANGLE_STRIP, 4, 4); + glDrawArrays(GL_TRIANGLE_STRIP, 8, 4); + glDrawArrays(GL_TRIANGLE_STRIP, 12, 4); + glDrawArrays(GL_TRIANGLE_STRIP, 16, 4); + glDrawArrays(GL_TRIANGLE_STRIP, 20, 4); +} + +class OutputHandler : private PageFlipHandlerBase +{ +public: + OutputHandler(Card& card, GbmDevice& gdev, const GLState& gl, Connector* connector, Crtc* crtc, Videomode& mode, Plane* plane, float rotation_mult) + : m_frame_num(0), m_connector(connector), m_crtc(crtc), m_plane(plane), m_mode(mode), + m_rotation_mult(rotation_mult) + { + m_surface = unique_ptr<Surface>(new Surface(card, gdev, gl, mode.hdisplay, mode.vdisplay)); + if (m_plane) + m_surface2 = unique_ptr<Surface>(new Surface(card, gdev, gl, 400, 400)); + } + + OutputHandler(const OutputHandler& other) = delete; + OutputHandler& operator=(const OutputHandler& other) = delete; + + void setup() + { + int ret; + + m_surface->make_current(); + m_surface->clear(); + struct Framebuffer* fb = m_surface->lock_next(); + + struct Framebuffer* planefb = 0; + + if (m_plane) { + m_surface2->make_current(); + m_surface2->clear(); + planefb = m_surface2->lock_next(); + } + + + ret = m_crtc->set_mode(m_connector, *fb, m_mode); + FAIL_IF(ret, "failed to set mode"); + + if (m_plane) { + ret = m_crtc->set_plane(m_plane, *planefb, + 0, 0, planefb->width(), planefb->height(), + 0, 0, planefb->width(), planefb->height()); + FAIL_IF(ret, "failed to set plane"); + } + } + + void start_flipping() + { + m_t1 = chrono::steady_clock::now(); + queue_next(); + } + +private: + void handle_page_flip(uint32_t frame, double time) + { + ++m_frame_num; + + if (m_frame_num % 100 == 0) { + auto t2 = chrono::steady_clock::now(); + chrono::duration<float> fsec = t2 - m_t1; + printf("fps: %f\n", 100.0 / fsec.count()); + m_t1 = t2; + } + + s_flip_pending--; + + m_surface->free_prev(); + if (m_plane) + m_surface2->free_prev(); + + if (s_need_exit) + return; + + queue_next(); + } + + void queue_next() + { + m_surface->make_current(); + m_surface->clear(); + draw(m_frame_num * m_rotation_mult, *m_surface); + struct Framebuffer* fb = m_surface->lock_next(); + + struct Framebuffer* planefb = 0; + + if (m_plane) { + m_surface2->make_current(); + m_surface2->clear(); + draw(m_frame_num * m_rotation_mult * 2, *m_surface2); + planefb = m_surface2->lock_next(); + } + + if (m_crtc->card().has_atomic()) { + int r; + + AtomicReq req(m_crtc->card()); + + req.add(m_crtc, "FB_ID", fb->id()); + if (m_plane) + req.add(m_plane, "FB_ID", planefb->id()); + + r = req.test(); + FAIL_IF(r, "atomic test failed"); + + r = req.commit(this); + FAIL_IF(r, "atomic commit failed"); + } else { + int ret; + + ret = m_crtc->page_flip(*fb, this); + FAIL_IF(ret, "failed to queue page flip"); + + if (m_plane) { + ret = m_crtc->set_plane(m_plane, *planefb, + 0, 0, planefb->width(), planefb->height(), + 0, 0, planefb->width(), planefb->height()); + FAIL_IF(ret, "failed to set plane"); + } + } + + s_flip_pending++; + } + + int m_frame_num; + chrono::steady_clock::time_point m_t1; + + Connector* m_connector; + Crtc* m_crtc; + Plane* m_plane; + Videomode m_mode; + + unique_ptr<Surface> m_surface; + unique_ptr<Surface> m_surface2; + + float m_rotation_mult; +}; + +int main(int argc, char *argv[]) +{ + for (int i = 1; i < argc; ++i) { + if (argv[i] == string("-v")) + s_verbose = true; + } + + Card card; + + GbmDevice gdev(card); + const GLState gl(gdev); + + vector<unique_ptr<OutputHandler>> outputs; + vector<Plane*> used_planes; + + float rot_mult = 1; + + for (auto pipe : card.get_connected_pipelines()) { + auto connector = pipe.connector; + auto crtc = pipe.crtc; + auto mode = connector->get_default_mode(); + + Plane* plane = 0; + + for (Plane* p : crtc->get_possible_planes()) { + if (find(used_planes.begin(), used_planes.end(), p) != used_planes.end()) + continue; + + if (p->plane_type() != PlaneType::Overlay) + continue; + + plane = p; + break; + } + + if (plane) + used_planes.push_back(plane); + + auto out = new OutputHandler(card, gdev, gl, connector, crtc, mode, plane, rot_mult); + outputs.emplace_back(out); + + rot_mult *= 1.33; + } + + for (auto& out : outputs) + out->setup(); + + for (auto& out : outputs) + out->start_flipping(); + + while (!s_need_exit || s_flip_pending) { + fd_set fds; + FD_ZERO(&fds); + if (!s_need_exit) + FD_SET(0, &fds); + FD_SET(card.fd(), &fds); + + int ret = select(card.fd() + 1, &fds, NULL, NULL, NULL); + + FAIL_IF(ret < 0, "select error: %d", ret); + FAIL_IF(ret == 0, "select timeout"); + + if (FD_ISSET(0, &fds)) + s_need_exit = true; + + if (FD_ISSET(card.fd(), &fds)) + card.call_page_flip_handlers(); + } + + return 0; +} |