Argus Camera Sample Argus Camera Sample

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Composer.cpp
Go to the documentation of this file.
1 /*
2 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of NVIDIA CORPORATION nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #define GL_GLEXT_PROTOTYPES
30
31 #include <GLES3/gl31.h>
32 #include <GLES2/gl2ext.h>
33
34 #include <math.h>
35
36 #include "Error.h"
37 #include "UniquePointer.h"
38
39 #include "Composer.h"
40 #include "Renderer.h"
41 #include "Window.h"
42 #include "StreamConsumer.h"
43
44 namespace ArgusSamples
45 {
46
47 Composer::Composer()
48 : m_initialized(false)
49 , m_program(0)
50 , m_windowWidth(0)
51 , m_windowHeight(0)
52 , m_windowAspectRatio(1.0f)
53 , m_clientSequence(0)
54 , m_composerSequence(0)
55 {
56 }
57
58 Composer::~Composer()
59 {
60 shutdown();
61 }
62
63 bool Composer::initialize()
64 {
65 if (m_initialized)
66 return true;
67
68 PROPAGATE_ERROR(m_mutex.initialize());
69
70 PROPAGATE_ERROR(m_sequenceMutex.initialize());
71 PROPAGATE_ERROR(m_sequenceCond.initialize());
72
73 // initialize the window size
74 Window &window = Window::getInstance();
75 PROPAGATE_ERROR(onResize(window.getWidth(), window.getHeight()));
76
77 // and register as observer for size changes
78 PROPAGATE_ERROR(window.registerObserver(this));
79
80 PROPAGATE_ERROR(Thread::initialize());
81 PROPAGATE_ERROR(Thread::waitRunning());
82
83 m_initialized = true;
84
85 return true;
86 }
87
88 bool Composer::shutdown()
89 {
90 if (!m_initialized)
91 return true;
92
93 PROPAGATE_ERROR_CONTINUE(Window::getInstance().unregisterObserver(this));
94
95 // request shutdown of the thread
96 PROPAGATE_ERROR_CONTINUE(Thread::requestShutdown());
97
98 // trigger a re-compose to wake up the thread
99 PROPAGATE_ERROR_CONTINUE(reCompose());
100
101 PROPAGATE_ERROR_CONTINUE(Thread::shutdown());
102
103 for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
104 {
105 PROPAGATE_ERROR_CONTINUE(it->m_consumer->shutdown());
106 delete it->m_consumer;
107 }
108 m_streams.clear();
109
110 m_initialized = false;
111
112 return true;
113 }
114
115 bool Composer::bindStream(EGLStreamKHR eglStream)
116 {
117 if (eglStream == EGL_NO_STREAM_KHR)
118 ORIGINATE_ERROR("Invalid stream");
119
120 PROPAGATE_ERROR(initialize());
121
122 UniquePointer<StreamConsumer> streamConsumer(new StreamConsumer(eglStream));
123 if (!streamConsumer)
124 ORIGINATE_ERROR("Out of memory");
125
126 PROPAGATE_ERROR(streamConsumer->initialize());
127
128 ScopedMutex sm(m_mutex);
129 PROPAGATE_ERROR(sm.expectLocked());
130
131 m_streams.push_back(Stream(streamConsumer.release()));
132
133 // trigger a re-compose because the stream configuration changed
134 PROPAGATE_ERROR(reCompose());
135
136 return true;
137 }
138
139 bool Composer::unbindStream(EGLStreamKHR eglStream)
140 {
141 ScopedMutex sm(m_mutex);
142 PROPAGATE_ERROR(sm.expectLocked());
143
144 for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
145 {
146 if (it->m_consumer->isEGLStream(eglStream))
147 {
148 PROPAGATE_ERROR(it->m_consumer->shutdown());
149 delete it->m_consumer;
150 m_streams.erase(it);
151 // trigger a re-compose because the stream configuration changed
152 PROPAGATE_ERROR(reCompose());
153 return true;
154 }
155 }
156
157 ORIGINATE_ERROR("Stream was not bound");
158
159 return true;
160 }
161
162 bool Composer::setStreamActive(EGLStreamKHR eglStream, bool active)
163 {
164 ScopedMutex sm(m_mutex);
165 PROPAGATE_ERROR(sm.expectLocked());
166
167 for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
168 {
169 if (it->m_consumer->isEGLStream(eglStream))
170 {
171 it->m_active = active;
172 // trigger a re-compose because the stream configuration changed
173 PROPAGATE_ERROR(reCompose());
174 return true;
175 }
176 }
177
178 ORIGINATE_ERROR("Stream was not bound");
179
180 return true;
181 }
182
183 bool Composer::setStreamAspectRatio(EGLStreamKHR eglStream, float aspectRatio)
184 {
185 ScopedMutex sm(m_mutex);
186 PROPAGATE_ERROR(sm.expectLocked());
187
188 for (StreamList::iterator it = m_streams.begin(); it != m_streams.end(); ++it)
189 {
190 if (it->m_consumer->isEGLStream(eglStream))
191 {
192 PROPAGATE_ERROR(it->m_consumer->setStreamAspectRatio(aspectRatio));
193 // trigger a re-compose because the stream configuration changed
194 PROPAGATE_ERROR(reCompose());
195 return true;
196 }
197 }
198
199 ORIGINATE_ERROR("Stream was not bound");
200
201 return true;
202 }
203
204 bool Composer::reCompose()
205 {
206 ScopedMutex sm(m_sequenceMutex);
207 PROPAGATE_ERROR(sm.expectLocked());
208
209 ++m_clientSequence;
210 PROPAGATE_ERROR(m_sequenceCond.signal());
211
212 return true;
213 }
214
215 bool Composer::onResize(uint32_t width, uint32_t height)
216 {
217 m_windowWidth = width;
218 m_windowHeight = height;
219 m_windowAspectRatio = (float)width / (float)height;
220 return true;
221 }
222
223 bool Composer::threadInitialize()
224 {
225 Renderer &renderer = Renderer::getInstance();
226
227 // Initialize the GL context and make it current.
228 PROPAGATE_ERROR(m_context.initialize(&Window::getInstance()));
229 PROPAGATE_ERROR(m_context.makeCurrent());
230
231 // Create the shader program.
232 static const char vtxSrc[] =
233 "#version 300 es\n"
234 "#extension GL_ARB_explicit_uniform_location : require\n"
235 "in layout(location = 0) vec2 vertex;\n"
236 "out vec2 vTexCoord;\n"
237 "layout(location = 0) uniform vec2 offset;\n"
238 "layout(location = 1) uniform vec2 scale;\n"
239 "void main() {\n"
240 " gl_Position = vec4((offset + vertex * scale) * 2.0 - 1.0, 0.0, 1.0);\n"
241 " vTexCoord = vec2(vertex.x, 1.0 - vertex.y);\n"
242 "}\n";
243 static const char frgSrc[] =
244 "#version 300 es\n"
245 "#extension GL_OES_EGL_image_external : require\n"
246 "precision highp float;\n"
247 "uniform samplerExternalOES texSampler;\n"
248 "in vec2 vTexCoord;\n"
249 "out vec4 fragColor;\n"
250 "void main() {\n"
251 " fragColor = texture2D(texSampler, vTexCoord);\n"
252 "}\n";
253 PROPAGATE_ERROR(m_context.createProgram(vtxSrc, frgSrc, &m_program));
254
255 // Setup vertex state.
256 static const GLfloat vertices[] = {
257 0.0f, 0.0f,
258 0.0f, 1.0f,
259 1.0f, 0.0f,
260 1.0f, 1.0f,
261 };
262 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
263 glEnableVertexAttribArray(0);
264
265 if (eglSwapInterval(renderer.getEGLDisplay(), 1) != EGL_TRUE)
266 ORIGINATE_ERROR("Failed to set the swap interval");
267
268 return true;
269 }
270
271 bool Composer::threadExecute()
272 {
273 {
274 // wait for the sequence condition
275 ScopedMutex sm(m_sequenceMutex);
276 PROPAGATE_ERROR(sm.expectLocked());
277
278 PROPAGATE_ERROR(m_sequenceCond.wait(m_sequenceMutex));
279
280 // check if the sequence differs
281 if (m_clientSequence == m_composerSequence)
282 return true;
283
284 // need to compose
285 m_composerSequence = m_clientSequence;
286 }
287
288 glViewport(0,0, m_windowWidth, m_windowHeight);
289
290 glClear(GL_COLOR_BUFFER_BIT);
291
292 // take a copy of the stream list to avoid holding the mutex too long
293 StreamList streamsCopy;
294 {
295 ScopedMutex sm(m_mutex);
296 PROPAGATE_ERROR(sm.expectLocked());
297
298 streamsCopy = m_streams;
299 }
300
301 if (streamsCopy.size() != 0)
302 {
303 // iterate through the streams and render them
304
305 glUseProgram(m_program);
306
307 uint32_t activeStreams = 0;
308 for (StreamList::iterator it = streamsCopy.begin(); it != streamsCopy.end(); ++it)
309 {
310 if (it->m_active)
311 ++activeStreams;
312 }
313
314 const uint32_t cells = static_cast<uint32_t>(ceil(sqrt(activeStreams)));
315 const float scaleX = 1.0f / cells;
316 const float scaleY = 1.0f / cells;
317 uint32_t offsetX = 0, offsetY = cells - 1;
318
319 for (StreamList::iterator it = streamsCopy.begin(); it != streamsCopy.end(); ++it)
320 {
321 if (it->m_active)
322 {
323 #ifdef WAR_EGL_STREAM_RACE
324 // hold the stream texture mutex while drawing with the texture
325 ScopedMutex sm(it->m_consumer->getStreamTextureMutex());
326 PROPAGATE_ERROR(sm.expectLocked());
327 #endif // WAR_EGL_STREAM_RACE
328
329 glBindTexture(GL_TEXTURE_EXTERNAL_OES, it->m_consumer->getStreamTextureID());
330
331 // scale according to aspect ratios
332 float sizeX = it->m_consumer->getStreamAspectRatio() / m_windowAspectRatio;
333 float sizeY = 1.0f;
334
335 if (sizeX > sizeY)
336 {
337 sizeY /= sizeX;
338 sizeX = 1.0f;
339 }
340 else
341 {
342 sizeX /= sizeY;
343 sizeY = 1.0f;
344 }
345
346 glUniform2f(0,
347 (offsetX - (sizeX - 1.0f) * 0.5f) * scaleX,
348 (offsetY - (sizeY - 1.0f) * 0.5f) * scaleY);
349 glUniform2f(1, scaleX * sizeX, scaleY * sizeY);
350
351 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
352
353 glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
354
355 ++offsetX;
356 if (offsetX == cells)
357 {
358 --offsetY;
359 offsetX = 0;
360 }
361 }
362 }
363
364 glUseProgram(0);
365 }
366
367 PROPAGATE_ERROR(m_context.swapBuffers());
368
369 return true;
370 }
371
372 bool Composer::threadShutdown()
373 {
374 glDeleteProgram(m_program);
375 m_program = 0;
376
377 PROPAGATE_ERROR(m_context.cleanup());
378
379 return true;
380 }
381
382 }; // namespace ArgusSamples