1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "WindowSurface.h"
17 #include "FBConfig.h"
18 #include "FrameBuffer.h"
19 #include <GLES/glext.h>
20 #include "EGLDispatch.h"
21 #include "GLDispatch.h"
22 #include "GL2Dispatch.h"
23 #include <stdio.h>
24 #include <string.h>
25 #include "GLErrorLog.h"
26 
WindowSurface()27 WindowSurface::WindowSurface() :
28     m_fbObj(0),
29     m_depthRB(0),
30     m_stencilRB(0),
31     m_eglSurface(NULL),
32     m_attachedColorBuffer(NULL),
33     m_readContext(NULL),
34     m_drawContext(NULL),
35     m_width(0),
36     m_height(0),
37     m_pbufWidth(0),
38     m_pbufHeight(0)
39 {
40 }
41 
~WindowSurface()42 WindowSurface::~WindowSurface()
43 {
44     s_egl.eglDestroySurface(FrameBuffer::getFB()->getDisplay(), m_eglSurface);
45 }
46 
create(int p_config,int p_width,int p_height)47 WindowSurface *WindowSurface::create(int p_config, int p_width, int p_height)
48 {
49     const FBConfig *fbconf = FBConfig::get(p_config);
50     if (!fbconf) {
51         return NULL;
52     }
53 
54     // allocate space for the WindowSurface object
55     WindowSurface *win = new WindowSurface();
56     if (!win) {
57         return NULL;
58     }
59     win->m_fbconf = fbconf;
60 
61     //
62     // Create a pbuffer to be used as the egl surface
63     // for that window.
64     //
65     if (!win->resizePbuffer(p_width, p_height)) {
66         delete win;
67         return NULL;
68     }
69 
70     win->m_width = p_width;
71     win->m_height = p_height;
72 
73     return win;
74 }
75 
76 //
77 // flushColorBuffer - The function makes sure that the
78 //    previous attached color buffer is updated, if copy or blit should be done
79 //    in order to update it - it is being done here.
80 //
flushColorBuffer()81 bool WindowSurface::flushColorBuffer()
82 {
83     if (m_attachedColorBuffer.Ptr() != NULL) {
84         return blitToColorBuffer();
85     }
86     return true;
87 }
88 
89 //
90 // setColorBuffer - this function is called when a new color buffer needs to
91 //    be attached to the surface. The function doesn't make sure that the
92 //    previous attached color buffer is updated, this is done by flushColorBuffer
93 //
setColorBuffer(ColorBufferPtr p_colorBuffer)94 void WindowSurface::setColorBuffer(ColorBufferPtr p_colorBuffer)
95 {
96     m_attachedColorBuffer = p_colorBuffer;
97 
98     //
99     // resize the window if the attached color buffer is of different
100     // size
101     //
102     unsigned int cbWidth = m_attachedColorBuffer->getWidth();
103     unsigned int cbHeight = m_attachedColorBuffer->getHeight();
104 
105     if (cbWidth != m_width || cbHeight != m_height) {
106 
107         if (m_pbufWidth && m_pbufHeight) {
108             // if we use pbuffer, need to resize it
109             resizePbuffer(cbWidth, cbHeight);
110         }
111 
112         m_width = cbWidth;
113         m_height = cbHeight;
114     }
115 }
116 
117 //
118 // This function is called after the context and eglSurface is already
119 // bound in the current thread (eglMakeCurrent has been called).
120 // This function should take actions required on the other surface objects
121 // when being bind/unbound
122 //
bind(RenderContextPtr p_ctx,SurfaceBindType p_bindType)123 void WindowSurface::bind(RenderContextPtr p_ctx, SurfaceBindType p_bindType)
124 {
125     if (p_bindType == SURFACE_BIND_READ) {
126         m_readContext = p_ctx;
127     }
128     else if (p_bindType == SURFACE_BIND_DRAW) {
129         m_drawContext = p_ctx;
130     }
131     else if (p_bindType == SURFACE_BIND_READDRAW) {
132         m_readContext = p_ctx;
133         m_drawContext = p_ctx;
134     }
135     else {
136         return;  // bad param
137     }
138 
139 }
140 
blitToColorBuffer()141 bool WindowSurface::blitToColorBuffer()
142 {
143     if (!m_width && !m_height) return false;
144 
145     if (m_attachedColorBuffer->getWidth() != m_width ||
146         m_attachedColorBuffer->getHeight() != m_height) {
147         // XXX: should never happen - how this needs to be handled?
148         fprintf(stderr, "Dimensions do not match\n");
149         return false;
150     }
151 
152     //
153     // Make the surface current
154     //
155     EGLContext prevContext = s_egl.eglGetCurrentContext();
156     EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
157     EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
158     FrameBuffer *fb = FrameBuffer::getFB();
159     if (!m_drawContext.Ptr()) {
160         fprintf(stderr, "Draw context is NULL\n");
161         return false;
162     }
163     if (!s_egl.eglMakeCurrent(fb->getDisplay(), m_eglSurface,
164                               m_eglSurface, m_drawContext->getEGLContext())) {
165         fprintf(stderr, "Error making draw context current\n");
166         return false;
167     }
168 
169     m_attachedColorBuffer->blitFromCurrentReadBuffer();
170 
171     // restore current context/surface
172     s_egl.eglMakeCurrent(fb->getDisplay(), prevDrawSurf,
173                          prevReadSurf, prevContext);
174     return true;
175 }
176 
resizePbuffer(unsigned int p_width,unsigned int p_height)177 bool WindowSurface::resizePbuffer(unsigned int p_width, unsigned int p_height)
178 {
179     if (m_eglSurface &&
180         m_pbufWidth == p_width &&
181         m_pbufHeight == p_height) {
182         // no need to resize
183         return true;
184     }
185 
186     FrameBuffer *fb = FrameBuffer::getFB();
187 
188     EGLContext prevContext = s_egl.eglGetCurrentContext();
189     EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
190     EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
191     EGLSurface prevPbuf = m_eglSurface;
192     bool needRebindContext = m_eglSurface &&
193                              (prevReadSurf == m_eglSurface ||
194                               prevDrawSurf == m_eglSurface);
195 
196     if (needRebindContext) {
197         s_egl.eglMakeCurrent(fb->getDisplay(), EGL_NO_SURFACE,
198                               EGL_NO_SURFACE, EGL_NO_CONTEXT);
199     }
200 
201     //
202     // Destroy previous surface
203     //
204     if (m_eglSurface) {
205         s_egl.eglDestroySurface(fb->getDisplay(), m_eglSurface);
206         m_eglSurface = NULL;
207     }
208 
209     //
210     // Create pbuffer surface.
211     //
212     EGLint pbufAttribs[5];
213     pbufAttribs[0] = EGL_WIDTH;
214     pbufAttribs[1] = p_width;
215     pbufAttribs[2] = EGL_HEIGHT;
216     pbufAttribs[3] = p_height;
217     pbufAttribs[4] = EGL_NONE;
218 
219     m_eglSurface = s_egl.eglCreatePbufferSurface(fb->getDisplay(),
220                                                  m_fbconf->getEGLConfig(),
221                                                  pbufAttribs);
222     if (m_eglSurface == EGL_NO_SURFACE) {
223         fprintf(stderr, "Renderer error: failed to create/resize pbuffer!!\n");
224         return false;
225     }
226 
227     m_pbufWidth = p_width;
228     m_pbufHeight = p_height;
229 
230     if (needRebindContext) {
231         s_egl.eglMakeCurrent(fb->getDisplay(),
232                      (prevDrawSurf==prevPbuf) ? m_eglSurface : prevDrawSurf,
233                      (prevReadSurf==prevPbuf) ? m_eglSurface : prevReadSurf,
234                      prevContext);
235     }
236 
237     return true;
238 }
239