Created
October 11, 2019 15:02
-
-
Save anarsoul/1b71253c2b18ed15ee86e36a51a54dd8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <assert.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <gbm.h> | |
#include <png.h> | |
#include <epoxy/gl.h> | |
#include <epoxy/egl.h> | |
GLuint program; | |
EGLDisplay display; | |
EGLSurface surface; | |
EGLContext context; | |
struct gbm_device *gbm; | |
struct gbm_surface *gs; | |
#define TARGET_SIZE 512 | |
EGLConfig get_config(void) | |
{ | |
EGLint egl_config_attribs[] = { | |
EGL_BUFFER_SIZE, 32, | |
EGL_DEPTH_SIZE, EGL_DONT_CARE, | |
EGL_STENCIL_SIZE, EGL_DONT_CARE, | |
EGL_SURFACE_TYPE, EGL_WINDOW_BIT, | |
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, | |
EGL_NONE, | |
}; | |
EGLint num_configs; | |
assert(eglGetConfigs(display, NULL, 0, &num_configs) == EGL_TRUE); | |
EGLConfig *configs = malloc(num_configs * sizeof(EGLConfig)); | |
assert(eglChooseConfig(display, egl_config_attribs, | |
configs, num_configs, &num_configs) == EGL_TRUE); | |
assert(num_configs); | |
printf("num config %d\n", num_configs); | |
// Find a config whose native visual ID is the desired GBM format. | |
for (int i = 0; i < num_configs; ++i) { | |
EGLint gbm_format; | |
assert(eglGetConfigAttrib(display, configs[i], | |
EGL_NATIVE_VISUAL_ID, &gbm_format) == EGL_TRUE); | |
printf("gbm format %x\n", gbm_format); | |
if (gbm_format == GBM_FORMAT_XRGB8888) { | |
EGLConfig ret = configs[i]; | |
free(configs); | |
return ret; | |
} | |
} | |
// Failed to find a config with matching GBM format. | |
abort(); | |
} | |
void RenderTargetInit(void) | |
{ | |
assert(epoxy_has_egl_extension(EGL_NO_DISPLAY, "EGL_KHR_platform_gbm")); | |
int fd = open("/dev/dri/card0", O_RDWR); | |
assert(fd >= 0); | |
gbm = gbm_create_device(fd); | |
assert(gbm != NULL); | |
assert((display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, gbm, NULL)) != EGL_NO_DISPLAY); | |
EGLint majorVersion; | |
EGLint minorVersion; | |
assert(eglInitialize(display, &majorVersion, &minorVersion) == EGL_TRUE); | |
assert(eglBindAPI(EGL_OPENGL_ES_API) == EGL_TRUE); | |
EGLConfig config = get_config(); | |
gs = gbm_surface_create( | |
gbm, TARGET_SIZE, TARGET_SIZE, GBM_FORMAT_XRGB8888, | |
/*GBM_BO_USE_LINEAR|*/GBM_BO_USE_SCANOUT|GBM_BO_USE_RENDERING); | |
assert(gs); | |
assert((surface = eglCreateWindowSurface(display, config, gs, NULL)) != EGL_NO_SURFACE); | |
const EGLint contextAttribs[] = { | |
EGL_CONTEXT_CLIENT_VERSION, 2, | |
EGL_NONE | |
}; | |
assert((context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs)) != EGL_NO_CONTEXT); | |
assert(eglMakeCurrent(display, surface, surface, context) == EGL_TRUE); | |
} | |
GLuint LoadShader(const char *name, GLenum type) | |
{ | |
FILE *f; | |
int size; | |
char *buff; | |
GLuint shader; | |
GLint compiled; | |
const GLchar *source[1]; | |
assert((f = fopen(name, "r")) != NULL); | |
// get file size | |
fseek(f, 0, SEEK_END); | |
size = ftell(f); | |
fseek(f, 0, SEEK_SET); | |
assert((buff = malloc(size)) != NULL); | |
assert(fread(buff, 1, size, f) == size); | |
source[0] = buff; | |
fclose(f); | |
shader = glCreateShader(type); | |
glShaderSource(shader, 1, source, &size); | |
glCompileShader(shader); | |
free(buff); | |
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); | |
if (!compiled) { | |
GLint infoLen = 0; | |
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); | |
if (infoLen > 1) { | |
char *infoLog = malloc(infoLen); | |
glGetShaderInfoLog(shader, infoLen, NULL, infoLog); | |
fprintf(stderr, "Error compiling shader %s:\n%s\n", name, infoLog); | |
free(infoLog); | |
} | |
glDeleteShader(shader); | |
return 0; | |
} | |
return shader; | |
} | |
void InitGLES(void) | |
{ | |
GLint linked; | |
GLuint vertexShader; | |
GLuint fragmentShader; | |
assert((vertexShader = LoadShader("vert.glsl", GL_VERTEX_SHADER)) != 0); | |
assert((fragmentShader = LoadShader("frag.glsl", GL_FRAGMENT_SHADER)) != 0); | |
assert((program = glCreateProgram()) != 0); | |
glAttachShader(program, vertexShader); | |
glAttachShader(program, fragmentShader); | |
glLinkProgram(program); | |
glGetProgramiv(program, GL_LINK_STATUS, &linked); | |
if (!linked) { | |
GLint infoLen = 0; | |
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); | |
if (infoLen > 1) { | |
char *infoLog = malloc(infoLen); | |
glGetProgramInfoLog(program, infoLen, NULL, infoLog); | |
fprintf(stderr, "Error linking program:\n%s\n", infoLog); | |
free(infoLog); | |
} | |
glDeleteProgram(program); | |
exit(1); | |
} | |
glClearColor(0, 0, 0, 0); | |
glViewport(0, 0, TARGET_SIZE, TARGET_SIZE); | |
//glEnable(GL_DEPTH_TEST); | |
glUseProgram(program); | |
} | |
void *readImage(char *filename, int *width, int *height) | |
{ | |
char header[8]; // 8 is the maximum size that can be checked | |
/* open file and test for it being a png */ | |
FILE *fp = fopen(filename, "rb"); | |
assert(fp); | |
fread(header, 1, 8, fp); | |
assert(!png_sig_cmp(header, 0, 8)); | |
/* initialize stuff */ | |
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |
assert(png_ptr); | |
png_infop info_ptr = png_create_info_struct(png_ptr); | |
assert(info_ptr); | |
assert(!setjmp(png_jmpbuf(png_ptr))); | |
png_init_io(png_ptr, fp); | |
png_set_sig_bytes(png_ptr, 8); | |
png_read_info(png_ptr, info_ptr); | |
*width = png_get_image_width(png_ptr, info_ptr); | |
*height = png_get_image_height(png_ptr, info_ptr); | |
int color_type = png_get_color_type(png_ptr, info_ptr); | |
assert(color_type == PNG_COLOR_TYPE_RGB); | |
int bit_depth = png_get_bit_depth(png_ptr, info_ptr); | |
assert(bit_depth == 8); | |
int pitch = png_get_rowbytes(png_ptr, info_ptr); | |
int number_of_passes = png_set_interlace_handling(png_ptr); | |
png_read_update_info(png_ptr, info_ptr); | |
/* read file */ | |
assert(!setjmp(png_jmpbuf(png_ptr))); | |
png_bytep buffer = malloc(*height * pitch); | |
void *ret = buffer; | |
assert(buffer); | |
png_bytep *row_pointers = malloc(sizeof(png_bytep) * *height); | |
assert(row_pointers); | |
for (int i = 0; i < *height; i++) { | |
row_pointers[i] = buffer; | |
buffer += pitch; | |
} | |
png_read_image(png_ptr, row_pointers); | |
fclose(fp); | |
free(row_pointers); | |
return ret; | |
} | |
int writeImage(char* filename, int width, int height, void *buffer, char* title) | |
{ | |
int code = 0; | |
FILE *fp = NULL; | |
png_structp png_ptr = NULL; | |
png_infop info_ptr = NULL; | |
// Open file for writing (binary mode) | |
fp = fopen(filename, "wb"); | |
if (fp == NULL) { | |
fprintf(stderr, "Could not open file %s for writing\n", filename); | |
code = 1; | |
goto finalise; | |
} | |
// Initialize write structure | |
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |
if (png_ptr == NULL) { | |
fprintf(stderr, "Could not allocate write struct\n"); | |
code = 1; | |
goto finalise; | |
} | |
// Initialize info structure | |
info_ptr = png_create_info_struct(png_ptr); | |
if (info_ptr == NULL) { | |
fprintf(stderr, "Could not allocate info struct\n"); | |
code = 1; | |
goto finalise; | |
} | |
// Setup Exception handling | |
if (setjmp(png_jmpbuf(png_ptr))) { | |
fprintf(stderr, "Error during png creation\n"); | |
code = 1; | |
goto finalise; | |
} | |
png_init_io(png_ptr, fp); | |
// Write header (8 bit colour depth) | |
png_set_IHDR(png_ptr, info_ptr, width, height, | |
8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, | |
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); | |
// Set title | |
if (title != NULL) { | |
png_text title_text; | |
title_text.compression = PNG_TEXT_COMPRESSION_NONE; | |
title_text.key = "Title"; | |
title_text.text = title; | |
png_set_text(png_ptr, info_ptr, &title_text, 1); | |
} | |
png_write_info(png_ptr, info_ptr); | |
// Write image data | |
int i; | |
for (i = 0; i < height; i++) | |
png_write_row(png_ptr, (png_bytep)buffer + i * width * 4); | |
// End write | |
png_write_end(png_ptr, NULL); | |
finalise: | |
if (fp != NULL) fclose(fp); | |
if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); | |
if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL); | |
return code; | |
} | |
void Render(void) | |
{ | |
int w, h; | |
void *data = readImage("crate-base-2048x2048.png", &w, &h); | |
glActiveTexture(GL_TEXTURE0); | |
GLuint texid = 0; | |
glGenTextures(1, &texid); | |
glBindTexture(GL_TEXTURE_2D, texid); | |
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_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); | |
GLenum err = glGetError(); | |
printf("err: %.4x\n", err); | |
assert(err == GL_NO_ERROR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); | |
//for (int i = 0; i < 6; i++) { | |
// glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); | |
//} | |
glGenerateMipmap(GL_TEXTURE_2D); | |
GLfloat vertex[] = { | |
-1, -1, 0, | |
-1, 1, 0, | |
1, 1, 0, | |
1, -1, 0 | |
}; | |
GLfloat tex[] = { | |
1, 1, // 0, | |
1, 0, // 0, | |
0, 0, // 0, | |
0, 1, // 0, | |
}; | |
GLint position = glGetAttribLocation(program, "positionIn"); | |
glEnableVertexAttribArray(position); | |
glVertexAttribPointer(position, 3, GL_FLOAT, 0, 0, vertex); | |
GLint texIn = glGetAttribLocation(program, "texIn"); | |
glEnableVertexAttribArray(texIn); | |
glVertexAttribPointer(texIn, 2, GL_FLOAT, 0, 0, tex); | |
GLint sample = glGetUniformLocation(program, "tex"); | |
glUniform1i(sample, 0); | |
glClear(GL_COLOR_BUFFER_BIT); | |
glDrawArrays(GL_TRIANGLES, 0, 3); | |
err = glGetError(); | |
printf("err: %.4x\n", err); | |
assert(err == GL_NO_ERROR); | |
eglSwapBuffers(display, surface); | |
#if 1 | |
GLubyte result[TARGET_SIZE * TARGET_SIZE * 4] = {0}; | |
glReadPixels(0, 0, TARGET_SIZE, TARGET_SIZE, GL_RGBA, GL_UNSIGNED_BYTE, result); | |
err = glGetError(); | |
printf("err: %.4x\n", err); | |
//assert(err == GL_NO_ERROR); | |
#else | |
struct gbm_bo *bo = gbm_surface_lock_front_buffer(gs); | |
assert(bo); | |
uint32_t stride; | |
void *map_data; | |
GLubyte *result = gbm_bo_map(bo, 0, 0, TARGET_SIZE, TARGET_SIZE, | |
GBM_BO_TRANSFER_READ, &stride, &map_data); | |
assert(result); | |
assert(stride == TARGET_SIZE * 4); | |
#endif | |
assert(!writeImage("screenshot.png", TARGET_SIZE, TARGET_SIZE, result, "hello")); | |
} | |
int main(void) | |
{ | |
RenderTargetInit(); | |
InitGLES(); | |
Render(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment