Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upuserland/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.c
Go to file| /* | |
| Copyright (c) 2013, Broadcom Europe Ltd | |
| Copyright (c) 2016, Tim Gover | |
| All rights reserved. | |
| Redistribution and use in source and binary forms, with or without | |
| modification, are permitted provided that the following conditions are met: | |
| * Redistributions of source code must retain the above copyright | |
| notice, this list of conditions and the following disclaimer. | |
| * Redistributions in binary form must reproduce the above copyright | |
| notice, this list of conditions and the following disclaimer in the | |
| documentation and/or other materials provided with the distribution. | |
| * Neither the name of the copyright holder nor the | |
| names of its contributors may be used to endorse or promote products | |
| derived from this software without specific prior written permission. | |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY | |
| DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| /* Make the render output CPU accessible by defining a framebuffer texture | |
| * stored in a VCSM (VideoCore shared memory) EGL image. | |
| * | |
| * This example just demonstrates how to use use the APIs by using the CPU. | |
| * to blit an animated rectangle into frame-buffer texture in shared memory. | |
| * | |
| * A more realistic example would be to do a blur, edge-detect in GLSL then pass | |
| * the buffer to OpenCV. There may be some benefit in using multiple GL contexts | |
| * to reduce the impact of serializing operations with a glFlush. | |
| * | |
| * N.B VCSM textures are raster scan order textures. This makes it very | |
| * convenient to read and modify VCSM frame-buffer textures from the CPU. | |
| * However, if the output of the CPU stage is drawn again as a texture that | |
| * is rotated or scaled then it can sometimes be better to use glTexImage2D | |
| * to allow the driver to convert this back into the native texture format. | |
| * | |
| * Example usage | |
| * raspistill -p 0,0,1024,1024 -gw 0,0,1024,1024 -t 10000 --gl -gs vcsm_square | |
| */ | |
| /* Uncomment the next line to compare with the glReadPixels implementation. VCSM | |
| * should run at about 40fps with a 1024x1024 texture compared to about 20fps | |
| * using glReadPixels. | |
| */ | |
| //#define USE_READPIXELS | |
| #include "vcsm_square.h" | |
| #include <GLES2/gl2.h> | |
| #include <EGL/egl.h> | |
| #include <EGL/eglext.h> | |
| #include "RaspiTex.h" | |
| #include "RaspiTexUtil.h" | |
| #include "user-vcsm.h" | |
| /* Draw a scaled quad showing the entire texture with the | |
| * origin defined as an attribute */ | |
| static RASPITEXUTIL_SHADER_PROGRAM_T vcsm_square_oes_shader = | |
| { | |
| .vertex_source = | |
| "attribute vec2 vertex;\n" | |
| "varying vec2 texcoord;\n" | |
| "void main(void) {\n" | |
| " texcoord = 0.5 * (vertex + 1.0);\n" \ | |
| " gl_Position = vec4(vertex, 0.0, 1.0);\n" | |
| "}\n", | |
| .fragment_source = | |
| "#extension GL_OES_EGL_image_external : require\n" | |
| "uniform samplerExternalOES tex;\n" | |
| "varying vec2 texcoord;\n" | |
| "void main(void) {\n" | |
| " gl_FragColor = texture2D(tex, texcoord);\n" | |
| "}\n", | |
| .uniform_names = {"tex"}, | |
| .attribute_names = {"vertex"}, | |
| }; | |
| static RASPITEXUTIL_SHADER_PROGRAM_T vcsm_square_shader = | |
| { | |
| .vertex_source = | |
| "attribute vec2 vertex;\n" | |
| "varying vec2 texcoord;\n" | |
| "void main(void) {\n" | |
| " texcoord = 0.5 * (vertex + 1.0);\n" \ | |
| " gl_Position = vec4(vertex, 0.0, 1.0);\n" | |
| "}\n", | |
| .fragment_source = | |
| "uniform sampler2D tex;\n" | |
| "varying vec2 texcoord;\n" | |
| "void main(void) {\n" | |
| " gl_FragColor = texture2D(tex, texcoord);\n" | |
| "}\n", | |
| .uniform_names = {"tex"}, | |
| .attribute_names = {"vertex"}, | |
| }; | |
| static GLfloat quad_varray[] = { | |
| -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, | |
| -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, | |
| }; | |
| static GLuint quad_vbo; | |
| #ifdef USE_READPIXELS | |
| unsigned char *pixel_buffer; | |
| #else | |
| static struct egl_image_brcm_vcsm_info vcsm_info; | |
| static EGLImageKHR eglFbImage; | |
| #endif | |
| static GLuint fb_tex_name; | |
| static GLuint fb_name; | |
| // VCSM buffer dimensions must be a power of two. Use glViewPort to draw NPOT | |
| // rectangles within the VCSM buffer. | |
| static int fb_width = 1024; | |
| static int fb_height = 1024; | |
| static const EGLint vcsm_square_egl_config_attribs[] = | |
| { | |
| EGL_RED_SIZE, 8, | |
| EGL_GREEN_SIZE, 8, | |
| EGL_BLUE_SIZE, 8, | |
| EGL_ALPHA_SIZE, 8, | |
| EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, | |
| EGL_NONE | |
| }; | |
| static int vcsm_square_init(RASPITEX_STATE *raspitex_state) | |
| { | |
| int rc = vcsm_init(); | |
| vcos_log_trace("%s: vcsm_init %d", VCOS_FUNCTION, rc); | |
| raspitex_state->egl_config_attribs = vcsm_square_egl_config_attribs; | |
| rc = raspitexutil_gl_init_2_0(raspitex_state); | |
| if (rc != 0) | |
| goto end; | |
| // Shader for drawing the YUV OES texture | |
| rc = raspitexutil_build_shader_program(&vcsm_square_oes_shader); | |
| GLCHK(glUseProgram(vcsm_square_oes_shader.program)); | |
| GLCHK(glUniform1i(vcsm_square_oes_shader.uniform_locations[0], 0)); // tex unit | |
| // Shader for drawing VCSM sampler2D texture | |
| rc = raspitexutil_build_shader_program(&vcsm_square_shader); | |
| GLCHK(glUseProgram(vcsm_square_shader.program)); | |
| GLCHK(glUniform1i(vcsm_square_shader.uniform_locations[0], 0)); // tex unit | |
| GLCHK(glGenFramebuffers(1, &fb_name)); | |
| GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, fb_name)); | |
| GLCHK(glGenTextures(1, &fb_tex_name)); | |
| GLCHK(glBindTexture(GL_TEXTURE_2D, fb_tex_name)); | |
| GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); | |
| GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); | |
| #ifdef USE_READPIXELS | |
| printf("Using glReadPixels\n"); | |
| GLCHK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fb_width, fb_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)); | |
| pixel_buffer = malloc(fb_width * fb_height * 4); | |
| if (! pixel_buffer) { | |
| rc = -1; | |
| goto end; | |
| } | |
| #else /* USE_READPIXELS */ | |
| printf("Using VCSM\n"); | |
| vcsm_info.width = fb_width; | |
| vcsm_info.height = fb_height; | |
| eglFbImage = eglCreateImageKHR(raspitex_state->display, EGL_NO_CONTEXT, | |
| EGL_IMAGE_BRCM_VCSM, &vcsm_info, NULL); | |
| if (eglFbImage == EGL_NO_IMAGE_KHR || vcsm_info.vcsm_handle == 0) { | |
| vcos_log_error("%s: Failed to create EGL VCSM image\n", VCOS_FUNCTION); | |
| rc = -1; | |
| goto end; | |
| } | |
| GLCHK(glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglFbImage)); | |
| #endif /* USE_READPIXELS */ | |
| GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_tex_name, 0)); | |
| if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { | |
| vcos_log_error("GL_FRAMEBUFFER is not complete\n"); | |
| rc = -1; | |
| goto end; | |
| } | |
| GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); | |
| GLCHK(glGenBuffers(1, &quad_vbo)); | |
| GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); | |
| GLCHK(glBufferData(GL_ARRAY_BUFFER, sizeof(quad_varray), quad_varray, GL_STATIC_DRAW)); | |
| GLCHK(glClearColor(0, 0, 0, 0)); | |
| end: | |
| return rc; | |
| } | |
| // Write the shared memory texture writing something to each line. This is | |
| // just to show that the buffer really is CPU modifiable. | |
| static void vcsm_square_draw_pattern(unsigned char *buffer) | |
| { | |
| static unsigned x_offset; | |
| unsigned char *line_start = (unsigned char *) buffer; | |
| unsigned width = fb_width > 32 ? 32 : fb_width; | |
| int i = 0; | |
| size_t stride = fb_width << 2; | |
| x_offset = (x_offset + 1) % (fb_width - width); | |
| for (i = 0; i < fb_height; i++) { | |
| memset(line_start + (x_offset << 2), ~0, width << 2); | |
| line_start += stride; | |
| } | |
| } | |
| #ifdef USE_READPIXELS | |
| static int vcsm_square_redraw_readpixels(RASPITEX_STATE *raspitex_state) | |
| { | |
| vcos_log_trace("%s", VCOS_FUNCTION); | |
| GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, fb_name)); | |
| GLCHK(glViewport(0,0,fb_width,fb_height)); | |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
| // Fill the viewport with the camFill the viewport with the camera image | |
| GLCHK(glUseProgram(vcsm_square_oes_shader.program)); | |
| GLCHK(glActiveTexture(GL_TEXTURE0)); | |
| GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->y_texture)); | |
| GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); | |
| GLCHK(glEnableVertexAttribArray(vcsm_square_oes_shader.attribute_locations[0])); | |
| GLCHK(glVertexAttribPointer(vcsm_square_oes_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); | |
| GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); | |
| GLCHK(glReadPixels(0, 0, fb_width, fb_height, GL_RGBA, GL_UNSIGNED_BYTE, pixel_buffer)); | |
| vcsm_square_draw_pattern(pixel_buffer); | |
| // Enable default window surface | |
| GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); | |
| // Draw the modified texture buffer to the screen | |
| GLCHK(glViewport(raspitex_state->x, raspitex_state->y, raspitex_state->width, raspitex_state->height)); | |
| GLCHK(glUseProgram(vcsm_square_shader.program)); | |
| GLCHK(glActiveTexture(GL_TEXTURE0)); | |
| GLCHK(glBindTexture(GL_TEXTURE_2D, fb_tex_name)); | |
| GLCHK(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_width, fb_height, GL_RGBA, GL_UNSIGNED_BYTE, pixel_buffer)); | |
| GLCHK(glEnableVertexAttribArray(vcsm_square_shader.attribute_locations[0])); | |
| GLCHK(glVertexAttribPointer(vcsm_square_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); | |
| GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); | |
| GLCHK(glDisableVertexAttribArray(vcsm_square_shader.attribute_locations[0])); | |
| GLCHK(glUseProgram(0)); | |
| return 0; | |
| } | |
| #else /* USE_READPIXELS */ | |
| static int vcsm_square_redraw(RASPITEX_STATE *raspitex_state) | |
| { | |
| unsigned char *vcsm_buffer = NULL; | |
| VCSM_CACHE_TYPE_T cache_type; | |
| vcos_log_trace("%s", VCOS_FUNCTION); | |
| glClearColor(255, 0, 0, 255); | |
| GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, fb_name)); | |
| GLCHK(glViewport(0, 0, fb_width, fb_height)); | |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
| // Fill the viewport with the camFill the viewport with the camera image | |
| GLCHK(glUseProgram(vcsm_square_oes_shader.program)); | |
| GLCHK(glActiveTexture(GL_TEXTURE0)); | |
| GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->y_texture)); | |
| GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo)); | |
| GLCHK(glEnableVertexAttribArray(vcsm_square_oes_shader.attribute_locations[0])); | |
| GLCHK(glVertexAttribPointer(vcsm_square_oes_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); | |
| GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); | |
| GLCHK(glFinish()); | |
| // Make the buffer CPU addressable with host cache enabled | |
| vcsm_buffer = (unsigned char *) vcsm_lock_cache(vcsm_info.vcsm_handle, VCSM_CACHE_TYPE_HOST, &cache_type); | |
| if (! vcsm_buffer) { | |
| vcos_log_error("Failed to lock VCSM buffer for handle %d\n", vcsm_info.vcsm_handle); | |
| return -1; | |
| } | |
| vcos_log_trace("Locked vcsm handle %d at %p\n", vcsm_info.vcsm_handle, vcsm_buffer); | |
| vcsm_square_draw_pattern(vcsm_buffer); | |
| // Release the locked texture memory to flush the CPU cache and allow GPU | |
| // to read it | |
| vcsm_unlock_ptr(vcsm_buffer); | |
| // Enable default window surface | |
| GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, 0)); | |
| // Draw the modified texture buffer to the screen | |
| GLCHK(glViewport(raspitex_state->x, raspitex_state->y, raspitex_state->width, raspitex_state->height)); | |
| GLCHK(glUseProgram(vcsm_square_shader.program)); | |
| GLCHK(glActiveTexture(GL_TEXTURE0)); | |
| GLCHK(glBindTexture(GL_TEXTURE_2D, fb_tex_name)); | |
| GLCHK(glEnableVertexAttribArray(vcsm_square_shader.attribute_locations[0])); | |
| GLCHK(glVertexAttribPointer(vcsm_square_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0)); | |
| GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6)); | |
| GLCHK(glDisableVertexAttribArray(vcsm_square_shader.attribute_locations[0])); | |
| GLCHK(glUseProgram(0)); | |
| return 0; | |
| } | |
| #endif /* USE_READPIXELS */ | |
| int vcsm_square_open(RASPITEX_STATE *raspitex_state) | |
| { | |
| vcos_log_trace("%s", VCOS_FUNCTION); | |
| raspitex_state->ops.gl_init = vcsm_square_init; | |
| #ifdef USE_READPIXELS | |
| raspitex_state->ops.redraw = vcsm_square_redraw_readpixels; | |
| #else | |
| raspitex_state->ops.redraw = vcsm_square_redraw; | |
| #endif /* USE_READPIXELS */ | |
| raspitex_state->ops.update_y_texture = raspitexutil_update_y_texture; | |
| return 0; | |
| } |