Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
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"
14 namespace mozilla {
15 namespace gl {
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 }
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 }
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 }
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 }
82 TextureImageEGL::~TextureImageEGL()
83 {
84 if (mGLContext->IsDestroyed() || !mGLContext->IsOwningThreadCurrent()) {
85 return;
86 }
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 }
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 }
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 }
114 gfx::DrawTarget*
115 TextureImageEGL::BeginUpdate(nsIntRegion& aRegion)
116 {
117 NS_ASSERTION(!mUpdateDrawTarget, "BeginUpdate() without EndUpdate()?");
119 // determine the region the client will need to repaint
120 GetUpdateRegion(aRegion);
121 mUpdateRect = aRegion.GetBounds();
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 }
129 //printf_stderr("creating image surface %dx%d format %d\n", mUpdateRect.width, mUpdateRect.height, mUpdateFormat);
131 mUpdateDrawTarget = gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO,
132 gfx::IntSize(mUpdateRect.width, mUpdateRect.height),
133 mUpdateFormat);
135 return mUpdateDrawTarget;
136 }
138 void
139 TextureImageEGL::EndUpdate()
140 {
141 NS_ASSERTION(!!mUpdateDrawTarget, "EndUpdate() without BeginUpdate()?");
143 //printf_stderr("EndUpdate: slow path");
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.
149 RefPtr<gfx::SourceSurface> updateSurface = nullptr;
150 RefPtr<gfx::DataSourceSurface> uploadImage = nullptr;
151 gfx::IntSize updateSize(mUpdateRect.width, mUpdateRect.height);
153 NS_ASSERTION(mUpdateDrawTarget->GetSize() == updateSize,
154 "Upload image is the wrong size!");
156 updateSurface = mUpdateDrawTarget->Snapshot();
157 uploadImage = updateSurface->GetDataSurface();
159 if (!uploadImage) {
160 return;
161 }
163 mGLContext->MakeCurrent();
164 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
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!");
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 }
192 mUpdateDrawTarget = nullptr;
193 mTextureState = Valid;
194 return; // mTexture is bound
195 }
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();
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 }
210 mTextureFormat =
211 UploadSurfaceToTexture(mGLContext,
212 aSurf,
213 region,
214 mTexture,
215 mTextureState == Created,
216 bounds.TopLeft() + nsIntPoint(aFrom.x, aFrom.y),
217 false);
219 mTextureState = Valid;
220 return true;
221 }
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 }
231 mGLContext->fActiveTexture(aTextureUnit);
232 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
233 mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
234 }
236 void
237 TextureImageEGL::Resize(const gfx::IntSize& aSize)
238 {
239 NS_ASSERTION(!mUpdateDrawTarget, "Resize() while in update?");
241 if (mSize == aSize && mTextureState != Created)
242 return;
244 mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
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);
256 mTextureState = Allocated;
257 mSize = aSize;
258 }
260 bool
261 TextureImageEGL::BindTexImage()
262 {
263 if (mBound && !ReleaseTexImage())
264 return false;
266 EGLBoolean success =
267 sEGLLibrary.fBindTexImage(EGL_DISPLAY(),
268 (EGLSurface)mSurface,
269 LOCAL_EGL_BACK_BUFFER);
271 if (success == LOCAL_EGL_FALSE)
272 return false;
274 mBound = true;
275 return true;
276 }
278 bool
279 TextureImageEGL::ReleaseTexImage()
280 {
281 if (!mBound)
282 return true;
284 EGLBoolean success =
285 sEGLLibrary.fReleaseTexImage(EGL_DISPLAY(),
286 (EGLSurface)mSurface,
287 LOCAL_EGL_BACK_BUFFER);
289 if (success == LOCAL_EGL_FALSE)
290 return false;
292 mBound = false;
293 return true;
294 }
296 void
297 TextureImageEGL::DestroyEGLSurface(void)
298 {
299 if (!mSurface)
300 return;
302 sEGLLibrary.fDestroySurface(EGL_DISPLAY(), mSurface);
303 mSurface = nullptr;
304 }
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 }
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();
327 GLuint texture;
328 gl->fGenTextures(1, &texture);
330 nsRefPtr<TextureImageEGL> teximage =
331 new TextureImageEGL(texture, aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType,
332 gl, aFlags, TextureImage::Created, aImageFormat);
334 teximage->BindTexture(LOCAL_GL_TEXTURE0);
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);
342 return teximage.forget();
343 }
345 }
346 }