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