1/*
2Copyright (c) 2013, Broadcom Europe Ltd
3Copyright (c) 2016, Tim Gover
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions are met:
8 * Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 * Neither the name of the copyright holder nor the
14 names of its contributors may be used to endorse or promote products
15 derived from this software without specific prior written permission.
16
17THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
21DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*/
28
29/* Make the render output CPU accessible by defining a framebuffer texture
30 * stored in a VCSM (VideoCore shared memory) EGL image.
31 *
32 * This example just demonstrates how to use use the APIs by using the CPU.
33 * to blit an animated rectangle into frame-buffer texture in shared memory.
34 *
35 * A more realistic example would be to do a blur, edge-detect in GLSL then pass
36 * the buffer to OpenCV. There may be some benefit in using multiple GL contexts
37 * to reduce the impact of serializing operations with a glFlush.
38 *
39 * N.B VCSM textures are raster scan order textures. This makes it very
40 * convenient to read and modify VCSM frame-buffer textures from the CPU.
41 * However, if the output of the CPU stage is drawn again as a texture that
42 * is rotated or scaled then it can sometimes be better to use glTexImage2D
43 * to allow the driver to convert this back into the native texture format.
44 *
45 * Example usage
46 * raspistill -p 0,0,1024,1024 -gw 0,0,1024,1024 -t 10000 --gl -gs vcsm_square
47 */
48/* Uncomment the next line to compare with the glReadPixels implementation. VCSM
49 * should run at about 40fps with a 1024x1024 texture compared to about 20fps
50 * using glReadPixels.
51 */
52//#define USE_READPIXELS
53
54#include "vcsm_square.h"
55#include <GLES2/gl2.h>
56#include <EGL/egl.h>
57#include <EGL/eglext.h>
58#include "RaspiTex.h"
59#include "RaspiTexUtil.h"
60#include "user-vcsm.h"
61
62/* Draw a scaled quad showing the entire texture with the
63 * origin defined as an attribute */
64static RASPITEXUTIL_SHADER_PROGRAM_T vcsm_square_oes_shader =
65{
66 .vertex_source =
67 "attribute vec2 vertex;\n"
68 "varying vec2 texcoord;\n"
69 "void main(void) {\n"
70 " texcoord = 0.5 * (vertex + 1.0);\n" \
71 " gl_Position = vec4(vertex, 0.0, 1.0);\n"
72 "}\n",
73
74 .fragment_source =
75 "#extension GL_OES_EGL_image_external : require\n"
76 "uniform samplerExternalOES tex;\n"
77 "varying vec2 texcoord;\n"
78 "void main(void) {\n"
79 " gl_FragColor = texture2D(tex, texcoord);\n"
80 "}\n",
81 .uniform_names = {"tex"},
82 .attribute_names = {"vertex"},
83};
84static RASPITEXUTIL_SHADER_PROGRAM_T vcsm_square_shader =
85{
86 .vertex_source =
87 "attribute vec2 vertex;\n"
88 "varying vec2 texcoord;\n"
89 "void main(void) {\n"
90 " texcoord = 0.5 * (vertex + 1.0);\n" \
91 " gl_Position = vec4(vertex, 0.0, 1.0);\n"
92 "}\n",
93
94 .fragment_source =
95 "uniform sampler2D tex;\n"
96 "varying vec2 texcoord;\n"
97 "void main(void) {\n"
98 " gl_FragColor = texture2D(tex, texcoord);\n"
99 "}\n",
100 .uniform_names = {"tex"},
101 .attribute_names = {"vertex"},
102};
103
104static GLfloat quad_varray[] = {
105 -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f,
106 -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f,
107};
108
109static GLuint quad_vbo;
110
111#ifdef USE_READPIXELS
112unsigned char *pixel_buffer;
113#else
114static struct egl_image_brcm_vcsm_info vcsm_info;
115static EGLImageKHR eglFbImage;
116#endif
117static GLuint fb_tex_name;
118static GLuint fb_name;
119
120
121// VCSM buffer dimensions must be a power of two. Use glViewPort to draw NPOT
122// rectangles within the VCSM buffer.
123static int fb_width = 1024;
124static int fb_height = 1024;
125
126static const EGLint vcsm_square_egl_config_attribs[] =
127{
128 EGL_RED_SIZE, 8,
129 EGL_GREEN_SIZE, 8,
130 EGL_BLUE_SIZE, 8,
131 EGL_ALPHA_SIZE, 8,
132 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
133 EGL_NONE
134};
135
136static int vcsm_square_init(RASPITEX_STATE *raspitex_state)
137{
138 int rc = vcsm_init();
139 vcos_log_trace("%s: vcsm_init %d", VCOS_FUNCTION, rc);
140
141 raspitex_state->egl_config_attribs = vcsm_square_egl_config_attribs;
142 rc = raspitexutil_gl_init_2_0(raspitex_state);
143
144 if (rc != 0)
145 goto end;
146
147 // Shader for drawing the YUV OES texture
148 rc = raspitexutil_build_shader_program(&vcsm_square_oes_shader);
149 GLCHK(glUseProgram(vcsm_square_oes_shader.program));
150 GLCHK(glUniform1i(vcsm_square_oes_shader.uniform_locations[0], 0)); // tex unit
151
152 // Shader for drawing VCSM sampler2D texture
153 rc = raspitexutil_build_shader_program(&vcsm_square_shader);
154 GLCHK(glUseProgram(vcsm_square_shader.program));
155 GLCHK(glUniform1i(vcsm_square_shader.uniform_locations[0], 0)); // tex unit
156
157 GLCHK(glGenFramebuffers(1, &fb_name));
158 GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, fb_name));
159
160 GLCHK(glGenTextures(1, &fb_tex_name));
161 GLCHK(glBindTexture(GL_TEXTURE_2D, fb_tex_name));
162 GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
163 GLCHK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
164
165#ifdef USE_READPIXELS
166 printf("Using glReadPixels\n");
167 GLCHK(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fb_width, fb_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL));
168 pixel_buffer = malloc(fb_width * fb_height * 4);
169 if (! pixel_buffer) {
170 rc = -1;
171 goto end;
172 }
173#else /* USE_READPIXELS */
174 printf("Using VCSM\n");
175 vcsm_info.width = fb_width;
176 vcsm_info.height = fb_height;
177 eglFbImage = eglCreateImageKHR(raspitex_state->display, EGL_NO_CONTEXT,
178 EGL_IMAGE_BRCM_VCSM, &vcsm_info, NULL);
179 if (eglFbImage == EGL_NO_IMAGE_KHR || vcsm_info.vcsm_handle == 0) {
180 vcos_log_error("%s: Failed to create EGL VCSM image\n", VCOS_FUNCTION);
181 rc = -1;
182 goto end;
183 }
184
185 GLCHK(glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglFbImage));
186#endif /* USE_READPIXELS */
187
188 GLCHK(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb_tex_name, 0));
189 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
190 vcos_log_error("GL_FRAMEBUFFER is not complete\n");
191 rc = -1;
192 goto end;
193 }
194 GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
195 GLCHK(glGenBuffers(1, &quad_vbo));
196 GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo));
197 GLCHK(glBufferData(GL_ARRAY_BUFFER, sizeof(quad_varray), quad_varray, GL_STATIC_DRAW));
198
199 GLCHK(glClearColor(0, 0, 0, 0));
200end:
201 return rc;
202}
203
204// Write the shared memory texture writing something to each line. This is
205// just to show that the buffer really is CPU modifiable.
206static void vcsm_square_draw_pattern(unsigned char *buffer)
207{
208 static unsigned x_offset;
209
210 unsigned char *line_start = (unsigned char *) buffer;
211 unsigned width = fb_width > 32 ? 32 : fb_width;
212 int i = 0;
213 size_t stride = fb_width << 2;
214
215 x_offset = (x_offset + 1) % (fb_width - width);
216 for (i = 0; i < fb_height; i++) {
217 memset(line_start + (x_offset << 2), ~0, width << 2);
218 line_start += stride;
219 }
220}
221
222#ifdef USE_READPIXELS
223static int vcsm_square_redraw_readpixels(RASPITEX_STATE *raspitex_state)
224{
225 vcos_log_trace("%s", VCOS_FUNCTION);
226
227 GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, fb_name));
228 GLCHK(glViewport(0,0,fb_width,fb_height));
229 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
230
231 // Fill the viewport with the camFill the viewport with the camera image
232 GLCHK(glUseProgram(vcsm_square_oes_shader.program));
233 GLCHK(glActiveTexture(GL_TEXTURE0));
234 GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->y_texture));
235 GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo));
236 GLCHK(glEnableVertexAttribArray(vcsm_square_oes_shader.attribute_locations[0]));
237 GLCHK(glVertexAttribPointer(vcsm_square_oes_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0));
238 GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6));
239
240 GLCHK(glReadPixels(0, 0, fb_width, fb_height, GL_RGBA, GL_UNSIGNED_BYTE, pixel_buffer));
241
242 vcsm_square_draw_pattern(pixel_buffer);
243
244 // Enable default window surface
245 GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
246
247 // Draw the modified texture buffer to the screen
248 GLCHK(glViewport(raspitex_state->x, raspitex_state->y, raspitex_state->width, raspitex_state->height));
249 GLCHK(glUseProgram(vcsm_square_shader.program));
250 GLCHK(glActiveTexture(GL_TEXTURE0));
251 GLCHK(glBindTexture(GL_TEXTURE_2D, fb_tex_name));
252 GLCHK(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, fb_width, fb_height, GL_RGBA, GL_UNSIGNED_BYTE, pixel_buffer));
253 GLCHK(glEnableVertexAttribArray(vcsm_square_shader.attribute_locations[0]));
254 GLCHK(glVertexAttribPointer(vcsm_square_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0));
255 GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6));
256
257 GLCHK(glDisableVertexAttribArray(vcsm_square_shader.attribute_locations[0]));
258 GLCHK(glUseProgram(0));
259
260 return 0;
261}
262#else /* USE_READPIXELS */
263static int vcsm_square_redraw(RASPITEX_STATE *raspitex_state)
264{
265 unsigned char *vcsm_buffer = NULL;
266 VCSM_CACHE_TYPE_T cache_type;
267
268 vcos_log_trace("%s", VCOS_FUNCTION);
269
270 glClearColor(255, 0, 0, 255);
271
272 GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, fb_name));
273 GLCHK(glViewport(0, 0, fb_width, fb_height));
274 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
275
276 // Fill the viewport with the camFill the viewport with the camera image
277 GLCHK(glUseProgram(vcsm_square_oes_shader.program));
278 GLCHK(glActiveTexture(GL_TEXTURE0));
279 GLCHK(glBindTexture(GL_TEXTURE_EXTERNAL_OES, raspitex_state->y_texture));
280 GLCHK(glBindBuffer(GL_ARRAY_BUFFER, quad_vbo));
281 GLCHK(glEnableVertexAttribArray(vcsm_square_oes_shader.attribute_locations[0]));
282 GLCHK(glVertexAttribPointer(vcsm_square_oes_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0));
283 GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6));
284
285 GLCHK(glFinish());
286
287 // Make the buffer CPU addressable with host cache enabled
288 vcsm_buffer = (unsigned char *) vcsm_lock_cache(vcsm_info.vcsm_handle, VCSM_CACHE_TYPE_HOST, &cache_type);
289 if (! vcsm_buffer) {
290 vcos_log_error("Failed to lock VCSM buffer for handle %d\n", vcsm_info.vcsm_handle);
291 return -1;
292 }
293 vcos_log_trace("Locked vcsm handle %d at %p\n", vcsm_info.vcsm_handle, vcsm_buffer);
294
295 vcsm_square_draw_pattern(vcsm_buffer);
296
297 // Release the locked texture memory to flush the CPU cache and allow GPU
298 // to read it
299 vcsm_unlock_ptr(vcsm_buffer);
300
301 // Enable default window surface
302 GLCHK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
303
304 // Draw the modified texture buffer to the screen
305 GLCHK(glViewport(raspitex_state->x, raspitex_state->y, raspitex_state->width, raspitex_state->height));
306 GLCHK(glUseProgram(vcsm_square_shader.program));
307 GLCHK(glActiveTexture(GL_TEXTURE0));
308 GLCHK(glBindTexture(GL_TEXTURE_2D, fb_tex_name));
309 GLCHK(glEnableVertexAttribArray(vcsm_square_shader.attribute_locations[0]));
310 GLCHK(glVertexAttribPointer(vcsm_square_shader.attribute_locations[0], 2, GL_FLOAT, GL_FALSE, 0, 0));
311 GLCHK(glDrawArrays(GL_TRIANGLES, 0, 6));
312
313 GLCHK(glDisableVertexAttribArray(vcsm_square_shader.attribute_locations[0]));
314 GLCHK(glUseProgram(0));
315
316 return 0;
317}
318#endif /* USE_READPIXELS */
319
320int vcsm_square_open(RASPITEX_STATE *raspitex_state)
321{
322 vcos_log_trace("%s", VCOS_FUNCTION);
323
324 raspitex_state->ops.gl_init = vcsm_square_init;
325#ifdef USE_READPIXELS
326 raspitex_state->ops.redraw = vcsm_square_redraw_readpixels;
327#else
328 raspitex_state->ops.redraw = vcsm_square_redraw;
329#endif /* USE_READPIXELS */
330 raspitex_state->ops.update_y_texture = raspitexutil_update_y_texture;
331 return 0;
332}
333