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 FrameBuffer *fb = FrameBuffer::getFB();
62 const FrameBufferCaps &caps = fb->getCaps();
63
64 //
65 // Create a pbuffer to be used as the egl surface
66 // for that window.
67 //
68 if (!win->resizePbuffer(p_width, p_height)) {
69 delete win;
70 return NULL;
71 }
72
73 win->m_width = p_width;
74 win->m_height = p_height;
75
76 return win;
77 }
78
79 //
80 // flushColorBuffer - The function makes sure that the
81 // previous attached color buffer is updated, if copy or blit should be done
82 // in order to update it - it is being done here.
83 //
flushColorBuffer()84 void WindowSurface::flushColorBuffer()
85 {
86 if (m_attachedColorBuffer.Ptr() != NULL) {
87 blitToColorBuffer();
88 }
89 }
90
91 //
92 // setColorBuffer - this function is called when a new color buffer needs to
93 // be attached to the surface. The function doesn't make sure that the
94 // previous attached color buffer is updated, this is done by flushColorBuffer
95 //
setColorBuffer(ColorBufferPtr p_colorBuffer)96 void WindowSurface::setColorBuffer(ColorBufferPtr p_colorBuffer)
97 {
98 m_attachedColorBuffer = p_colorBuffer;
99
100 //
101 // resize the window if the attached color buffer is of different
102 // size
103 //
104 unsigned int cbWidth = m_attachedColorBuffer->getWidth();
105 unsigned int cbHeight = m_attachedColorBuffer->getHeight();
106
107 if (cbWidth != m_width || cbHeight != m_height) {
108
109 if (m_pbufWidth && m_pbufHeight) {
110 // if we use pbuffer, need to resize it
111 resizePbuffer(cbWidth, cbHeight);
112 }
113
114 m_width = cbWidth;
115 m_height = cbHeight;
116 }
117 }
118
119 //
120 // This function is called after the context and eglSurface is already
121 // bound in the current thread (eglMakeCurrent has been called).
122 // This function should take actions required on the other surface objects
123 // when being bind/unbound
124 //
bind(RenderContextPtr p_ctx,SurfaceBindType p_bindType)125 void WindowSurface::bind(RenderContextPtr p_ctx, SurfaceBindType p_bindType)
126 {
127 if (p_bindType == SURFACE_BIND_READ) {
128 m_readContext = p_ctx;
129 }
130 else if (p_bindType == SURFACE_BIND_DRAW) {
131 m_drawContext = p_ctx;
132 }
133 else if (p_bindType == SURFACE_BIND_READDRAW) {
134 m_readContext = p_ctx;
135 m_drawContext = p_ctx;
136 }
137 else {
138 return; // bad param
139 }
140
141 }
142
blitToColorBuffer()143 void WindowSurface::blitToColorBuffer()
144 {
145 if (!m_width && !m_height) return;
146
147 if (m_attachedColorBuffer->getWidth() != m_width ||
148 m_attachedColorBuffer->getHeight() != m_height) {
149 // XXX: should never happen - how this needs to be handled?
150 return;
151 }
152
153 //
154 // Make the surface current
155 //
156 EGLContext prevContext = s_egl.eglGetCurrentContext();
157 EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
158 EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
159 FrameBuffer *fb = FrameBuffer::getFB();
160 if (!s_egl.eglMakeCurrent(fb->getDisplay(), m_eglSurface,
161 m_eglSurface, m_drawContext->getEGLContext())) {
162 return;
163 }
164
165 m_attachedColorBuffer->blitFromCurrentReadBuffer();
166
167 // restore current context/surface
168 s_egl.eglMakeCurrent(fb->getDisplay(), prevDrawSurf,
169 prevReadSurf, prevContext);
170
171 }
172
resizePbuffer(unsigned int p_width,unsigned int p_height)173 bool WindowSurface::resizePbuffer(unsigned int p_width, unsigned int p_height)
174 {
175 if (m_eglSurface &&
176 m_pbufWidth == p_width &&
177 m_pbufHeight == p_height) {
178 // no need to resize
179 return true;
180 }
181
182 FrameBuffer *fb = FrameBuffer::getFB();
183
184 EGLContext prevContext = s_egl.eglGetCurrentContext();
185 EGLSurface prevReadSurf = s_egl.eglGetCurrentSurface(EGL_READ);
186 EGLSurface prevDrawSurf = s_egl.eglGetCurrentSurface(EGL_DRAW);
187 EGLSurface prevPbuf = m_eglSurface;
188 bool needRebindContext = m_eglSurface &&
189 (prevReadSurf == m_eglSurface ||
190 prevDrawSurf == m_eglSurface);
191
192 if (needRebindContext) {
193 s_egl.eglMakeCurrent(fb->getDisplay(), EGL_NO_SURFACE,
194 EGL_NO_SURFACE, EGL_NO_CONTEXT);
195 }
196
197 //
198 // Destroy previous surface
199 //
200 if (m_eglSurface) {
201 s_egl.eglDestroySurface(fb->getDisplay(), m_eglSurface);
202 m_eglSurface = NULL;
203 }
204
205 const FrameBufferCaps &caps = fb->getCaps();
206
207 //
208 // Create pbuffer surface.
209 //
210 EGLint pbufAttribs[5];
211 pbufAttribs[0] = EGL_WIDTH;
212 pbufAttribs[1] = p_width;
213 pbufAttribs[2] = EGL_HEIGHT;
214 pbufAttribs[3] = p_height;
215 pbufAttribs[4] = EGL_NONE;
216
217 m_eglSurface = s_egl.eglCreatePbufferSurface(fb->getDisplay(),
218 m_fbconf->getEGLConfig(),
219 pbufAttribs);
220 if (m_eglSurface == EGL_NO_SURFACE) {
221 fprintf(stderr, "Renderer error: failed to create/resize pbuffer!!\n");
222 return false;
223 }
224
225 m_pbufWidth = p_width;
226 m_pbufHeight = p_height;
227
228 if (needRebindContext) {
229 s_egl.eglMakeCurrent(fb->getDisplay(),
230 (prevDrawSurf==prevPbuf) ? m_eglSurface : prevDrawSurf,
231 (prevReadSurf==prevPbuf) ? m_eglSurface : prevReadSurf,
232 prevContext);
233 }
234
235 return true;
236 }
237