|
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "TextureImageEGL.h" |
|
7 #include "GLLibraryEGL.h" |
|
8 #include "GLContext.h" |
|
9 #include "GLUploadHelpers.h" |
|
10 #include "gfxPlatform.h" |
|
11 #include "gfx2DGlue.h" |
|
12 #include "mozilla/gfx/Types.h" |
|
13 |
|
14 namespace mozilla { |
|
15 namespace gl { |
|
16 |
|
17 static GLenum |
|
18 GLFormatForImage(gfx::SurfaceFormat aFormat) |
|
19 { |
|
20 switch (aFormat) { |
|
21 case gfx::SurfaceFormat::B8G8R8A8: |
|
22 case gfx::SurfaceFormat::B8G8R8X8: |
|
23 return LOCAL_GL_RGBA; |
|
24 case gfx::SurfaceFormat::R5G6B5: |
|
25 return LOCAL_GL_RGB; |
|
26 case gfx::SurfaceFormat::A8: |
|
27 return LOCAL_GL_LUMINANCE; |
|
28 default: |
|
29 NS_WARNING("Unknown GL format for Surface format"); |
|
30 } |
|
31 return 0; |
|
32 } |
|
33 |
|
34 static GLenum |
|
35 GLTypeForImage(gfx::SurfaceFormat aFormat) |
|
36 { |
|
37 switch (aFormat) { |
|
38 case gfx::SurfaceFormat::B8G8R8A8: |
|
39 case gfx::SurfaceFormat::B8G8R8X8: |
|
40 case gfx::SurfaceFormat::A8: |
|
41 return LOCAL_GL_UNSIGNED_BYTE; |
|
42 case gfx::SurfaceFormat::R5G6B5: |
|
43 return LOCAL_GL_UNSIGNED_SHORT_5_6_5; |
|
44 default: |
|
45 NS_WARNING("Unknown GL format for Surface format"); |
|
46 } |
|
47 return 0; |
|
48 } |
|
49 |
|
50 TextureImageEGL::TextureImageEGL(GLuint aTexture, |
|
51 const nsIntSize& aSize, |
|
52 GLenum aWrapMode, |
|
53 ContentType aContentType, |
|
54 GLContext* aContext, |
|
55 Flags aFlags, |
|
56 TextureState aTextureState, |
|
57 TextureImage::ImageFormat aImageFormat) |
|
58 : TextureImage(aSize, aWrapMode, aContentType, aFlags) |
|
59 , mGLContext(aContext) |
|
60 , mUpdateFormat(gfx::ImageFormatToSurfaceFormat(aImageFormat)) |
|
61 , mEGLImage(nullptr) |
|
62 , mTexture(aTexture) |
|
63 , mSurface(nullptr) |
|
64 , mConfig(nullptr) |
|
65 , mTextureState(aTextureState) |
|
66 , mBound(false) |
|
67 { |
|
68 if (mUpdateFormat == gfx::SurfaceFormat::UNKNOWN) { |
|
69 mUpdateFormat = gfx::ImageFormatToSurfaceFormat( |
|
70 gfxPlatform::GetPlatform()->OptimalFormatForContent(GetContentType())); |
|
71 } |
|
72 |
|
73 if (mUpdateFormat == gfx::SurfaceFormat::R5G6B5) { |
|
74 mTextureFormat = gfx::SurfaceFormat::R8G8B8X8; |
|
75 } else if (mUpdateFormat == gfx::SurfaceFormat::B8G8R8X8) { |
|
76 mTextureFormat = gfx::SurfaceFormat::B8G8R8X8; |
|
77 } else { |
|
78 mTextureFormat = gfx::SurfaceFormat::B8G8R8A8; |
|
79 } |
|
80 } |
|
81 |
|
82 TextureImageEGL::~TextureImageEGL() |
|
83 { |
|
84 if (mGLContext->IsDestroyed() || !mGLContext->IsOwningThreadCurrent()) { |
|
85 return; |
|
86 } |
|
87 |
|
88 // If we have a context, then we need to delete the texture; |
|
89 // if we don't have a context (either real or shared), |
|
90 // then they went away when the contex was deleted, because it |
|
91 // was the only one that had access to it. |
|
92 mGLContext->MakeCurrent(); |
|
93 mGLContext->fDeleteTextures(1, &mTexture); |
|
94 ReleaseTexImage(); |
|
95 DestroyEGLSurface(); |
|
96 } |
|
97 |
|
98 void |
|
99 TextureImageEGL::GetUpdateRegion(nsIntRegion& aForRegion) |
|
100 { |
|
101 if (mTextureState != Valid) { |
|
102 // if the texture hasn't been initialized yet, force the |
|
103 // client to paint everything |
|
104 aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); |
|
105 } |
|
106 |
|
107 // We can only draw a rectangle, not subregions due to |
|
108 // the way that our texture upload functions work. If |
|
109 // needed, we /could/ do multiple texture uploads if we have |
|
110 // non-overlapping rects, but that's a tradeoff. |
|
111 aForRegion = nsIntRegion(aForRegion.GetBounds()); |
|
112 } |
|
113 |
|
114 gfx::DrawTarget* |
|
115 TextureImageEGL::BeginUpdate(nsIntRegion& aRegion) |
|
116 { |
|
117 NS_ASSERTION(!mUpdateDrawTarget, "BeginUpdate() without EndUpdate()?"); |
|
118 |
|
119 // determine the region the client will need to repaint |
|
120 GetUpdateRegion(aRegion); |
|
121 mUpdateRect = aRegion.GetBounds(); |
|
122 |
|
123 //printf_stderr("BeginUpdate with updateRect [%d %d %d %d]\n", mUpdateRect.x, mUpdateRect.y, mUpdateRect.width, mUpdateRect.height); |
|
124 if (!nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)).Contains(mUpdateRect)) { |
|
125 NS_ERROR("update outside of image"); |
|
126 return nullptr; |
|
127 } |
|
128 |
|
129 //printf_stderr("creating image surface %dx%d format %d\n", mUpdateRect.width, mUpdateRect.height, mUpdateFormat); |
|
130 |
|
131 mUpdateDrawTarget = gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO, |
|
132 gfx::IntSize(mUpdateRect.width, mUpdateRect.height), |
|
133 mUpdateFormat); |
|
134 |
|
135 return mUpdateDrawTarget; |
|
136 } |
|
137 |
|
138 void |
|
139 TextureImageEGL::EndUpdate() |
|
140 { |
|
141 NS_ASSERTION(!!mUpdateDrawTarget, "EndUpdate() without BeginUpdate()?"); |
|
142 |
|
143 //printf_stderr("EndUpdate: slow path"); |
|
144 |
|
145 // This is the slower path -- we didn't have any way to set up |
|
146 // a fast mapping between our cairo target surface and the GL |
|
147 // texture, so we have to upload data. |
|
148 |
|
149 RefPtr<gfx::SourceSurface> updateSurface = nullptr; |
|
150 RefPtr<gfx::DataSourceSurface> uploadImage = nullptr; |
|
151 gfx::IntSize updateSize(mUpdateRect.width, mUpdateRect.height); |
|
152 |
|
153 NS_ASSERTION(mUpdateDrawTarget->GetSize() == updateSize, |
|
154 "Upload image is the wrong size!"); |
|
155 |
|
156 updateSurface = mUpdateDrawTarget->Snapshot(); |
|
157 uploadImage = updateSurface->GetDataSurface(); |
|
158 |
|
159 if (!uploadImage) { |
|
160 return; |
|
161 } |
|
162 |
|
163 mGLContext->MakeCurrent(); |
|
164 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); |
|
165 |
|
166 if (mTextureState != Valid) { |
|
167 NS_ASSERTION(mUpdateRect.x == 0 && mUpdateRect.y == 0 && |
|
168 mUpdateRect.Size() == gfx::ThebesIntSize(mSize), |
|
169 "Bad initial update on non-created texture!"); |
|
170 |
|
171 mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, |
|
172 0, |
|
173 GLFormatForImage(mUpdateFormat), |
|
174 mUpdateRect.width, |
|
175 mUpdateRect.height, |
|
176 0, |
|
177 GLFormatForImage(uploadImage->GetFormat()), |
|
178 GLTypeForImage(uploadImage->GetFormat()), |
|
179 uploadImage->GetData()); |
|
180 } else { |
|
181 mGLContext->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, |
|
182 0, |
|
183 mUpdateRect.x, |
|
184 mUpdateRect.y, |
|
185 mUpdateRect.width, |
|
186 mUpdateRect.height, |
|
187 GLFormatForImage(uploadImage->GetFormat()), |
|
188 GLTypeForImage(uploadImage->GetFormat()), |
|
189 uploadImage->GetData()); |
|
190 } |
|
191 |
|
192 mUpdateDrawTarget = nullptr; |
|
193 mTextureState = Valid; |
|
194 return; // mTexture is bound |
|
195 } |
|
196 |
|
197 bool |
|
198 TextureImageEGL::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0,0) */) |
|
199 { |
|
200 nsIntRect bounds = aRegion.GetBounds(); |
|
201 |
|
202 nsIntRegion region; |
|
203 if (mTextureState != Valid) { |
|
204 bounds = nsIntRect(0, 0, mSize.width, mSize.height); |
|
205 region = nsIntRegion(bounds); |
|
206 } else { |
|
207 region = aRegion; |
|
208 } |
|
209 |
|
210 mTextureFormat = |
|
211 UploadSurfaceToTexture(mGLContext, |
|
212 aSurf, |
|
213 region, |
|
214 mTexture, |
|
215 mTextureState == Created, |
|
216 bounds.TopLeft() + nsIntPoint(aFrom.x, aFrom.y), |
|
217 false); |
|
218 |
|
219 mTextureState = Valid; |
|
220 return true; |
|
221 } |
|
222 |
|
223 void |
|
224 TextureImageEGL::BindTexture(GLenum aTextureUnit) |
|
225 { |
|
226 // Ensure the texture is allocated before it is used. |
|
227 if (mTextureState == Created) { |
|
228 Resize(mSize); |
|
229 } |
|
230 |
|
231 mGLContext->fActiveTexture(aTextureUnit); |
|
232 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); |
|
233 mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0); |
|
234 } |
|
235 |
|
236 void |
|
237 TextureImageEGL::Resize(const gfx::IntSize& aSize) |
|
238 { |
|
239 NS_ASSERTION(!mUpdateDrawTarget, "Resize() while in update?"); |
|
240 |
|
241 if (mSize == aSize && mTextureState != Created) |
|
242 return; |
|
243 |
|
244 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); |
|
245 |
|
246 mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, |
|
247 0, |
|
248 GLFormatForImage(mUpdateFormat), |
|
249 aSize.width, |
|
250 aSize.height, |
|
251 0, |
|
252 GLFormatForImage(mUpdateFormat), |
|
253 GLTypeForImage(mUpdateFormat), |
|
254 nullptr); |
|
255 |
|
256 mTextureState = Allocated; |
|
257 mSize = aSize; |
|
258 } |
|
259 |
|
260 bool |
|
261 TextureImageEGL::BindTexImage() |
|
262 { |
|
263 if (mBound && !ReleaseTexImage()) |
|
264 return false; |
|
265 |
|
266 EGLBoolean success = |
|
267 sEGLLibrary.fBindTexImage(EGL_DISPLAY(), |
|
268 (EGLSurface)mSurface, |
|
269 LOCAL_EGL_BACK_BUFFER); |
|
270 |
|
271 if (success == LOCAL_EGL_FALSE) |
|
272 return false; |
|
273 |
|
274 mBound = true; |
|
275 return true; |
|
276 } |
|
277 |
|
278 bool |
|
279 TextureImageEGL::ReleaseTexImage() |
|
280 { |
|
281 if (!mBound) |
|
282 return true; |
|
283 |
|
284 EGLBoolean success = |
|
285 sEGLLibrary.fReleaseTexImage(EGL_DISPLAY(), |
|
286 (EGLSurface)mSurface, |
|
287 LOCAL_EGL_BACK_BUFFER); |
|
288 |
|
289 if (success == LOCAL_EGL_FALSE) |
|
290 return false; |
|
291 |
|
292 mBound = false; |
|
293 return true; |
|
294 } |
|
295 |
|
296 void |
|
297 TextureImageEGL::DestroyEGLSurface(void) |
|
298 { |
|
299 if (!mSurface) |
|
300 return; |
|
301 |
|
302 sEGLLibrary.fDestroySurface(EGL_DISPLAY(), mSurface); |
|
303 mSurface = nullptr; |
|
304 } |
|
305 |
|
306 already_AddRefed<TextureImage> |
|
307 CreateTextureImageEGL(GLContext *gl, |
|
308 const gfx::IntSize& aSize, |
|
309 TextureImage::ContentType aContentType, |
|
310 GLenum aWrapMode, |
|
311 TextureImage::Flags aFlags, |
|
312 TextureImage::ImageFormat aImageFormat) |
|
313 { |
|
314 nsRefPtr<TextureImage> t = new gl::TiledTextureImage(gl, aSize, aContentType, aFlags, aImageFormat); |
|
315 return t.forget(); |
|
316 } |
|
317 |
|
318 already_AddRefed<TextureImage> |
|
319 TileGenFuncEGL(GLContext *gl, |
|
320 const nsIntSize& aSize, |
|
321 TextureImage::ContentType aContentType, |
|
322 TextureImage::Flags aFlags, |
|
323 TextureImage::ImageFormat aImageFormat) |
|
324 { |
|
325 gl->MakeCurrent(); |
|
326 |
|
327 GLuint texture; |
|
328 gl->fGenTextures(1, &texture); |
|
329 |
|
330 nsRefPtr<TextureImageEGL> teximage = |
|
331 new TextureImageEGL(texture, aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, |
|
332 gl, aFlags, TextureImage::Created, aImageFormat); |
|
333 |
|
334 teximage->BindTexture(LOCAL_GL_TEXTURE0); |
|
335 |
|
336 GLint texfilter = aFlags & TextureImage::UseNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR; |
|
337 gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter); |
|
338 gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter); |
|
339 gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); |
|
340 gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); |
|
341 |
|
342 return teximage.forget(); |
|
343 } |
|
344 |
|
345 } |
|
346 } |