mirror of
https://github.com/elima/gpu-playground.git
synced 2025-06-06 23:46:17 +00:00
259 lines
7.0 KiB
C
259 lines
7.0 KiB
C
#include <assert.h>
|
|
#include <GLES2/gl2.h>
|
|
#include <GLFW/glfw3.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "image.h"
|
|
|
|
#define IMAGE_FILENAME_DEFAULT "./igalia-white-text.png"
|
|
|
|
static bool
|
|
gl_utils_print_shader_log (GLuint shader)
|
|
{
|
|
GLint length;
|
|
char buffer[4096] = {0};
|
|
GLint success;
|
|
|
|
glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &length);
|
|
if (length == 0)
|
|
return true;
|
|
|
|
glGetShaderInfoLog (shader, 4096, NULL, buffer);
|
|
if (strlen (buffer) > 0)
|
|
printf ("Shader compilation log: %s\n", buffer);
|
|
|
|
glGetShaderiv (shader, GL_COMPILE_STATUS, &success);
|
|
|
|
return success == GL_TRUE;
|
|
}
|
|
|
|
static GLuint
|
|
gl_utils_load_shader (const char *shader_source, GLenum type)
|
|
{
|
|
GLuint shader = glCreateShader (type);
|
|
|
|
glShaderSource (shader, 1, &shader_source, NULL);
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
glCompileShader (shader);
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
|
|
gl_utils_print_shader_log (shader);
|
|
|
|
return shader;
|
|
}
|
|
|
|
static GLuint
|
|
create_shader_program (void)
|
|
{
|
|
const char *VERTEX_SOURCE =
|
|
"attribute vec2 pos;\n"
|
|
"attribute vec2 texture;\n"
|
|
"varying vec2 v_texture;\n"
|
|
"void main() {\n"
|
|
" v_texture = texture;\n"
|
|
" gl_Position = vec4(pos, 0, 1);\n"
|
|
"}\n";
|
|
|
|
const char *FRAGMENT_SOURCE =
|
|
"precision mediump float;\n"
|
|
"uniform sampler2D u_tex;\n"
|
|
"varying vec2 v_texture;\n"
|
|
"void main() {\n"
|
|
" gl_FragColor = texture2D(u_tex, v_texture);\n"
|
|
"}\n";
|
|
|
|
GLuint vertex_shader = gl_utils_load_shader (VERTEX_SOURCE,
|
|
GL_VERTEX_SHADER);
|
|
assert (vertex_shader >= 0);
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
|
|
GLuint fragment_shader = gl_utils_load_shader (FRAGMENT_SOURCE,
|
|
GL_FRAGMENT_SHADER);
|
|
assert (fragment_shader >= 0);
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
|
|
GLuint program = glCreateProgram ();
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
glAttachShader (program, vertex_shader);
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
glAttachShader (program, fragment_shader);
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
|
|
glBindAttribLocation (program, 0, "pos");
|
|
glBindAttribLocation (program, 1, "texture");
|
|
|
|
glLinkProgram (program);
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
|
|
glDeleteShader (vertex_shader);
|
|
glDeleteShader (fragment_shader);
|
|
|
|
return program;
|
|
}
|
|
|
|
int32_t
|
|
main (int32_t argc, char *argv[])
|
|
{
|
|
printf ("Usage: %s <path-to-PNG-or-JPEG-image>\n", argv[0]);
|
|
|
|
/* Load an decode an image. */
|
|
static struct o_image image;
|
|
|
|
const char *image_url;
|
|
if (argc > 1)
|
|
image_url = argv[1];
|
|
else
|
|
image_url = IMAGE_FILENAME_DEFAULT;
|
|
|
|
/* This loads the image header (metadata), but doesn't load any pixel
|
|
* data or do any decoding.
|
|
*/
|
|
if (! o_image_init_from_filename (&image, image_url))
|
|
return -1;
|
|
|
|
GLFWwindow* window;
|
|
|
|
/* Initialize GLFW. */
|
|
if (! glfwInit ())
|
|
return -1;
|
|
|
|
/* Select an OpenGL-ES 2.0 profile. */
|
|
glfwWindowHint (GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
|
|
glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 2);
|
|
glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 0);
|
|
|
|
/* Create a windowed mode window and its OpenGL context */
|
|
window = glfwCreateWindow (image.width,
|
|
image.height,
|
|
"GL Image Loader",
|
|
NULL,
|
|
NULL);
|
|
if (window == NULL) {
|
|
glfwTerminate ();
|
|
return -1;
|
|
}
|
|
|
|
/* Make the window's context current */
|
|
glfwMakeContextCurrent (window);
|
|
|
|
/* Dump some GL capabilities. */
|
|
const GLubyte *gles_version = glGetString (GL_VERSION);
|
|
printf ("%s\n", (char *) gles_version);
|
|
|
|
/* Create a texture for the image. */
|
|
GLuint tex;
|
|
glGenTextures (1, &tex);
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
assert (tex > 0);
|
|
glBindTexture (GL_TEXTURE_2D, tex);
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
/* Load the image into the texture, progressively in chunks of max
|
|
* BLOCK_SIZE bytes. */
|
|
#define BLOCK_SIZE (8192 * 1)
|
|
uint8_t buf[BLOCK_SIZE] = {0, };
|
|
size_t first_row;
|
|
size_t num_rows;
|
|
|
|
GLuint format = image.format == O_IMAGE_FORMAT_RGB ?
|
|
GL_RGB : GL_RGBA;
|
|
|
|
/* Allocate the texture size. */
|
|
glTexImage2D (GL_TEXTURE_2D,
|
|
0,
|
|
format,
|
|
image.width, image.height,
|
|
0,
|
|
format,
|
|
GL_UNSIGNED_BYTE,
|
|
NULL);
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
|
|
ssize_t size_read;
|
|
do {
|
|
size_read = o_image_read (&image,
|
|
buf,
|
|
BLOCK_SIZE,
|
|
&first_row,
|
|
&num_rows);
|
|
assert (size_read >= 0);
|
|
if (size_read > 0) {
|
|
glTexSubImage2D (GL_TEXTURE_2D,
|
|
0,
|
|
0, first_row,
|
|
image.width, num_rows,
|
|
format,
|
|
GL_UNSIGNED_BYTE,
|
|
buf);
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
}
|
|
} while (size_read > 0);
|
|
|
|
glBindTexture (GL_TEXTURE_2D, 0);
|
|
#undef BLOCK_SIZE
|
|
|
|
/* Create shader program to sample the texture. */
|
|
GLuint program = create_shader_program ();
|
|
glUseProgram (program);
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
|
|
/* Loop until the user closes the window */
|
|
while (! glfwWindowShouldClose (window)) {
|
|
/* Render here */
|
|
glClearColor (0.25, 0.25, 0.25, 0.5);
|
|
glClear (GL_COLOR_BUFFER_BIT);
|
|
|
|
/* Bind the texture. */
|
|
glActiveTexture (GL_TEXTURE0);
|
|
glBindTexture (GL_TEXTURE_2D, tex);
|
|
assert (glGetError () == GL_NO_ERROR);
|
|
|
|
/* Enable blending for transparent PNGs. */
|
|
glEnable (GL_BLEND);
|
|
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
/* Draw a quad. */
|
|
static const GLfloat s_vertices[4][2] = {
|
|
{ -1.0, 1.0 },
|
|
{ 1.0, 1.0 },
|
|
{ -1.0, -1.0 },
|
|
{ 1.0, -1.0 },
|
|
};
|
|
|
|
static const GLfloat s_texturePos[4][2] = {
|
|
{ 0, 0 },
|
|
{ 1, 0 },
|
|
{ 0, 1 },
|
|
{ 1, 1 },
|
|
};
|
|
|
|
glVertexAttribPointer (0, 2, GL_FLOAT, GL_FALSE, 0, s_vertices);
|
|
glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, 0, s_texturePos);
|
|
|
|
glEnableVertexAttribArray (0);
|
|
glEnableVertexAttribArray (1);
|
|
|
|
glDrawArrays (GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
glDisableVertexAttribArray (0);
|
|
glDisableVertexAttribArray (1);
|
|
|
|
/* Swap front and back buffers */
|
|
glfwSwapBuffers (window);
|
|
|
|
/* Poll and process events */
|
|
glfwPollEvents ();
|
|
}
|
|
|
|
glfwTerminate ();
|
|
|
|
return 0;
|
|
}
|