Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "BufferTexture.h"
8
9
#include <utility>
10
11
#include "libyuv.h"
12
#include "mozilla/fallible.h"
13
#include "mozilla/gfx/2D.h"
14
#include "mozilla/gfx/Logging.h"
15
#include "mozilla/layers/CompositableForwarder.h"
16
#include "mozilla/layers/ISurfaceAllocator.h"
17
#include "mozilla/layers/ImageDataSerializer.h"
18
19
#ifdef MOZ_WIDGET_GTK
20
# include "gfxPlatformGtk.h"
21
#endif
22
23
using mozilla::ipc::IShmemAllocator;
24
25
namespace mozilla {
26
namespace layers {
27
28
class MemoryTextureData : public BufferTextureData {
29
public:
30
static MemoryTextureData* Create(gfx::IntSize aSize,
31
gfx::SurfaceFormat aFormat,
32
gfx::BackendType aMoz2DBackend,
33
LayersBackend aLayersBackend,
34
TextureFlags aFlags,
35
TextureAllocationFlags aAllocFlags,
36
IShmemAllocator* aAllocator);
37
38
virtual TextureData* CreateSimilar(
39
LayersIPCChannel* aAllocator, LayersBackend aLayersBackend,
40
TextureFlags aFlags = TextureFlags::DEFAULT,
41
TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
42
43
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
44
45
virtual void Deallocate(LayersIPCChannel*) override;
46
47
MemoryTextureData(const BufferDescriptor& aDesc,
48
gfx::BackendType aMoz2DBackend, uint8_t* aBuffer,
49
size_t aBufferSize)
50
: BufferTextureData(aDesc, aMoz2DBackend),
51
mBuffer(aBuffer),
52
mBufferSize(aBufferSize) {
53
MOZ_ASSERT(aBuffer);
54
MOZ_ASSERT(aBufferSize);
55
}
56
57
virtual uint8_t* GetBuffer() override { return mBuffer; }
58
59
virtual size_t GetBufferSize() override { return mBufferSize; }
60
61
protected:
62
uint8_t* mBuffer;
63
size_t mBufferSize;
64
};
65
66
class ShmemTextureData : public BufferTextureData {
67
public:
68
static ShmemTextureData* Create(gfx::IntSize aSize,
69
gfx::SurfaceFormat aFormat,
70
gfx::BackendType aMoz2DBackend,
71
LayersBackend aLayersBackend,
72
TextureFlags aFlags,
73
TextureAllocationFlags aAllocFlags,
74
IShmemAllocator* aAllocator);
75
76
virtual TextureData* CreateSimilar(
77
LayersIPCChannel* aAllocator, LayersBackend aLayersBackend,
78
TextureFlags aFlags = TextureFlags::DEFAULT,
79
TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
80
81
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
82
83
virtual void Deallocate(LayersIPCChannel* aAllocator) override;
84
85
ShmemTextureData(const BufferDescriptor& aDesc,
86
gfx::BackendType aMoz2DBackend, mozilla::ipc::Shmem aShmem)
87
: BufferTextureData(aDesc, aMoz2DBackend), mShmem(aShmem) {
88
MOZ_ASSERT(mShmem.Size<uint8_t>());
89
}
90
91
virtual uint8_t* GetBuffer() override { return mShmem.get<uint8_t>(); }
92
93
virtual size_t GetBufferSize() override { return mShmem.Size<uint8_t>(); }
94
95
protected:
96
mozilla::ipc::Shmem mShmem;
97
};
98
99
static bool UsingX11Compositor() {
100
#ifdef MOZ_WIDGET_GTK
101
return gfx::gfxVars::UseXRender();
102
#endif
103
return false;
104
}
105
106
bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat,
107
LayersBackend aLayersBackend,
108
bool aSupportsTextureDirectMapping) {
109
if (aSupportsTextureDirectMapping) {
110
return false;
111
}
112
113
return aLayersBackend != LayersBackend::LAYERS_BASIC ||
114
UsingX11Compositor() || aFormat == gfx::SurfaceFormat::UNKNOWN;
115
}
116
117
BufferTextureData* BufferTextureData::Create(
118
gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
119
gfx::BackendType aMoz2DBackend, LayersBackend aLayersBackend,
120
TextureFlags aFlags, TextureAllocationFlags aAllocFlags,
121
mozilla::ipc::IShmemAllocator* aAllocator, bool aIsSameProcess) {
122
if (!aAllocator || aIsSameProcess) {
123
return MemoryTextureData::Create(aSize, aFormat, aMoz2DBackend,
124
aLayersBackend, aFlags, aAllocFlags,
125
aAllocator);
126
} else {
127
return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend,
128
aLayersBackend, aFlags, aAllocFlags,
129
aAllocator);
130
}
131
}
132
133
BufferTextureData* BufferTextureData::CreateInternal(
134
LayersIPCChannel* aAllocator, const BufferDescriptor& aDesc,
135
gfx::BackendType aMoz2DBackend, int32_t aBufferSize,
136
TextureFlags aTextureFlags) {
137
if (!aAllocator || aAllocator->IsSameProcess()) {
138
uint8_t* buffer = new (fallible) uint8_t[aBufferSize];
139
if (!buffer) {
140
return nullptr;
141
}
142
143
GfxMemoryImageReporter::DidAlloc(buffer);
144
145
return new MemoryTextureData(aDesc, aMoz2DBackend, buffer, aBufferSize);
146
} else {
147
ipc::Shmem shm;
148
if (!aAllocator->AllocUnsafeShmem(aBufferSize, OptimalShmemType(), &shm)) {
149
return nullptr;
150
}
151
152
return new ShmemTextureData(aDesc, aMoz2DBackend, shm);
153
}
154
}
155
156
BufferTextureData* BufferTextureData::CreateForYCbCr(
157
KnowsCompositor* aAllocator, gfx::IntSize aYSize, uint32_t aYStride,
158
gfx::IntSize aCbCrSize, uint32_t aCbCrStride, StereoMode aStereoMode,
159
gfx::ColorDepth aColorDepth, gfx::YUVColorSpace aYUVColorSpace,
160
gfx::ColorRange aColorRange, TextureFlags aTextureFlags) {
161
uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize(
162
aYSize, aYStride, aCbCrSize, aCbCrStride);
163
if (bufSize == 0) {
164
return nullptr;
165
}
166
167
uint32_t yOffset;
168
uint32_t cbOffset;
169
uint32_t crOffset;
170
ImageDataSerializer::ComputeYCbCrOffsets(aYStride, aYSize.height, aCbCrStride,
171
aCbCrSize.height, yOffset, cbOffset,
172
crOffset);
173
174
bool supportsTextureDirectMapping =
175
aAllocator->SupportsTextureDirectMapping() &&
176
aAllocator->GetMaxTextureSize() >
177
std::max(aYSize.width,
178
std::max(aYSize.height,
179
std::max(aCbCrSize.width, aCbCrSize.height)));
180
181
bool hasIntermediateBuffer =
182
aAllocator
183
? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
184
aAllocator->GetCompositorBackendType(),
185
supportsTextureDirectMapping)
186
: true;
187
188
YCbCrDescriptor descriptor =
189
YCbCrDescriptor(aYSize, aYStride, aCbCrSize, aCbCrStride, yOffset,
190
cbOffset, crOffset, aStereoMode, aColorDepth,
191
aYUVColorSpace, aColorRange, hasIntermediateBuffer);
192
193
return CreateInternal(
194
aAllocator ? aAllocator->GetTextureForwarder() : nullptr, descriptor,
195
gfx::BackendType::NONE, bufSize, aTextureFlags);
196
}
197
198
void BufferTextureData::FillInfo(TextureData::Info& aInfo) const {
199
aInfo.size = GetSize();
200
aInfo.format = GetFormat();
201
aInfo.hasSynchronization = false;
202
aInfo.canExposeMappedData = true;
203
204
if (mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor) {
205
aInfo.hasIntermediateBuffer =
206
mDescriptor.get_YCbCrDescriptor().hasIntermediateBuffer();
207
} else {
208
aInfo.hasIntermediateBuffer =
209
mDescriptor.get_RGBDescriptor().hasIntermediateBuffer();
210
}
211
212
switch (aInfo.format) {
213
case gfx::SurfaceFormat::YUV:
214
case gfx::SurfaceFormat::UNKNOWN:
215
aInfo.supportsMoz2D = false;
216
break;
217
default:
218
aInfo.supportsMoz2D = true;
219
}
220
}
221
222
gfx::IntSize BufferTextureData::GetSize() const {
223
return ImageDataSerializer::SizeFromBufferDescriptor(mDescriptor);
224
}
225
226
Maybe<gfx::IntSize> BufferTextureData::GetCbCrSize() const {
227
return ImageDataSerializer::CbCrSizeFromBufferDescriptor(mDescriptor);
228
}
229
230
Maybe<gfx::YUVColorSpace> BufferTextureData::GetYUVColorSpace() const {
231
return ImageDataSerializer::YUVColorSpaceFromBufferDescriptor(mDescriptor);
232
}
233
234
Maybe<gfx::ColorDepth> BufferTextureData::GetColorDepth() const {
235
return ImageDataSerializer::ColorDepthFromBufferDescriptor(mDescriptor);
236
}
237
238
Maybe<StereoMode> BufferTextureData::GetStereoMode() const {
239
return ImageDataSerializer::StereoModeFromBufferDescriptor(mDescriptor);
240
}
241
242
gfx::SurfaceFormat BufferTextureData::GetFormat() const {
243
return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor);
244
}
245
246
already_AddRefed<gfx::DrawTarget> BufferTextureData::BorrowDrawTarget() {
247
if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) {
248
return nullptr;
249
}
250
251
const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
252
253
uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
254
RefPtr<gfx::DrawTarget> dt;
255
if (gfx::Factory::DoesBackendSupportDataDrawtarget(mMoz2DBackend)) {
256
dt = gfx::Factory::CreateDrawTargetForData(
257
mMoz2DBackend, GetBuffer(), rgb.size(), stride, rgb.format(), true);
258
}
259
if (!dt) {
260
// Fall back to supported platform backend. Note that mMoz2DBackend
261
// does not match the draw target type.
262
dt = gfxPlatform::CreateDrawTargetForData(GetBuffer(), rgb.size(), stride,
263
rgb.format(), true);
264
}
265
266
if (!dt) {
267
gfxCriticalNote << "BorrowDrawTarget failure, original backend "
268
<< (int)mMoz2DBackend;
269
}
270
271
return dt.forget();
272
}
273
274
bool BufferTextureData::BorrowMappedData(MappedTextureData& aData) {
275
if (GetFormat() == gfx::SurfaceFormat::YUV) {
276
return false;
277
}
278
279
gfx::IntSize size = GetSize();
280
281
aData.data = GetBuffer();
282
aData.size = size;
283
aData.format = GetFormat();
284
aData.stride =
285
ImageDataSerializer::ComputeRGBStride(aData.format, size.width);
286
287
return true;
288
}
289
290
bool BufferTextureData::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) {
291
if (mDescriptor.type() != BufferDescriptor::TYCbCrDescriptor) {
292
return false;
293
}
294
295
const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
296
297
uint8_t* data = GetBuffer();
298
auto ySize = desc.ySize();
299
auto cbCrSize = desc.cbCrSize();
300
301
aMap.stereoMode = desc.stereoMode();
302
aMap.metadata = nullptr;
303
uint32_t bytesPerPixel =
304
BytesPerPixel(SurfaceFormatForColorDepth(desc.colorDepth()));
305
306
aMap.y.data = data + desc.yOffset();
307
aMap.y.size = ySize;
308
aMap.y.stride = desc.yStride();
309
aMap.y.skip = 0;
310
aMap.y.bytesPerPixel = bytesPerPixel;
311
312
aMap.cb.data = data + desc.cbOffset();
313
aMap.cb.size = cbCrSize;
314
aMap.cb.stride = desc.cbCrStride();
315
aMap.cb.skip = 0;
316
aMap.cb.bytesPerPixel = bytesPerPixel;
317
318
aMap.cr.data = data + desc.crOffset();
319
aMap.cr.size = cbCrSize;
320
aMap.cr.stride = desc.cbCrStride();
321
aMap.cr.skip = 0;
322
aMap.cr.bytesPerPixel = bytesPerPixel;
323
324
return true;
325
}
326
327
bool BufferTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) {
328
if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) {
329
return false;
330
}
331
const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
332
333
uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
334
RefPtr<gfx::DataSourceSurface> surface =
335
gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(), stride,
336
rgb.size(), rgb.format());
337
338
if (!surface) {
339
gfxCriticalError() << "Failed to get serializer as surface!";
340
return false;
341
}
342
343
RefPtr<gfx::DataSourceSurface> srcSurf = aSurface->GetDataSurface();
344
345
if (!srcSurf) {
346
gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (BT).";
347
return false;
348
}
349
350
if (surface->GetSize() != srcSurf->GetSize() ||
351
surface->GetFormat() != srcSurf->GetFormat()) {
352
gfxCriticalError() << "Attempt to update texture client from a surface "
353
"with a different size or format (BT)! This: "
354
<< surface->GetSize() << " " << surface->GetFormat()
355
<< " Other: " << aSurface->GetSize() << " "
356
<< aSurface->GetFormat();
357
return false;
358
}
359
360
gfx::DataSourceSurface::MappedSurface sourceMap;
361
gfx::DataSourceSurface::MappedSurface destMap;
362
if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
363
gfxCriticalError()
364
<< "Failed to map source surface for UpdateFromSurface (BT).";
365
return false;
366
}
367
368
if (!surface->Map(gfx::DataSourceSurface::WRITE, &destMap)) {
369
srcSurf->Unmap();
370
gfxCriticalError()
371
<< "Failed to map destination surface for UpdateFromSurface.";
372
return false;
373
}
374
375
for (int y = 0; y < srcSurf->GetSize().height; y++) {
376
memcpy(destMap.mData + destMap.mStride * y,
377
sourceMap.mData + sourceMap.mStride * y,
378
srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
379
}
380
381
srcSurf->Unmap();
382
surface->Unmap();
383
384
return true;
385
}
386
387
void BufferTextureData::SetDescriptor(BufferDescriptor&& aDescriptor) {
388
MOZ_ASSERT(mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor);
389
MOZ_ASSERT(mDescriptor.get_YCbCrDescriptor().ySize() == gfx::IntSize());
390
mDescriptor = std::move(aDescriptor);
391
}
392
393
bool MemoryTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) {
394
MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
395
if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
396
return false;
397
}
398
399
uintptr_t ptr = reinterpret_cast<uintptr_t>(mBuffer);
400
aOutDescriptor = SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(ptr));
401
402
return true;
403
}
404
405
static bool InitBuffer(uint8_t* buf, size_t bufSize, gfx::SurfaceFormat aFormat,
406
TextureAllocationFlags aAllocFlags, bool aAlreadyZero) {
407
if (!buf) {
408
gfxDebug() << "BufferTextureData: Failed to allocate " << bufSize
409
<< " bytes";
410
return false;
411
}
412
413
if ((aAllocFlags & ALLOC_CLEAR_BUFFER) ||
414
(aAllocFlags & ALLOC_CLEAR_BUFFER_BLACK)) {
415
if (aFormat == gfx::SurfaceFormat::B8G8R8X8) {
416
// Even though BGRX was requested, XRGB_UINT32 is what is meant,
417
// so use 0xFF000000 to put alpha in the right place.
418
libyuv::ARGBRect(buf, bufSize, 0, 0, bufSize / sizeof(uint32_t), 1,
419
0xFF000000);
420
} else if (!aAlreadyZero) {
421
memset(buf, 0, bufSize);
422
}
423
}
424
425
if (aAllocFlags & ALLOC_CLEAR_BUFFER_WHITE) {
426
memset(buf, 0xFF, bufSize);
427
}
428
429
return true;
430
}
431
432
MemoryTextureData* MemoryTextureData::Create(gfx::IntSize aSize,
433
gfx::SurfaceFormat aFormat,
434
gfx::BackendType aMoz2DBackend,
435
LayersBackend aLayersBackend,
436
TextureFlags aFlags,
437
TextureAllocationFlags aAllocFlags,
438
IShmemAllocator* aAllocator) {
439
// Should have used CreateForYCbCr.
440
MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
441
442
if (aSize.width <= 0 || aSize.height <= 0) {
443
gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x"
444
<< aSize.height;
445
return nullptr;
446
}
447
448
uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
449
if (!bufSize) {
450
return nullptr;
451
}
452
453
uint8_t* buf = new (fallible) uint8_t[bufSize];
454
if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags, false)) {
455
return nullptr;
456
}
457
458
bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(
459
aFormat, aLayersBackend, aAllocFlags & ALLOC_ALLOW_DIRECT_MAPPING);
460
461
GfxMemoryImageReporter::DidAlloc(buf);
462
463
BufferDescriptor descriptor =
464
RGBDescriptor(aSize, aFormat, hasIntermediateBuffer);
465
466
return new MemoryTextureData(descriptor, aMoz2DBackend, buf, bufSize);
467
}
468
469
void MemoryTextureData::Deallocate(LayersIPCChannel*) {
470
MOZ_ASSERT(mBuffer);
471
GfxMemoryImageReporter::WillFree(mBuffer);
472
delete[] mBuffer;
473
mBuffer = nullptr;
474
}
475
476
TextureData* MemoryTextureData::CreateSimilar(
477
LayersIPCChannel* aAllocator, LayersBackend aLayersBackend,
478
TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const {
479
return MemoryTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend,
480
aLayersBackend, aFlags, aAllocFlags,
481
aAllocator);
482
}
483
484
bool ShmemTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) {
485
MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
486
if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
487
return false;
488
}
489
490
aOutDescriptor =
491
SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(std::move(mShmem)));
492
493
return true;
494
}
495
496
ShmemTextureData* ShmemTextureData::Create(gfx::IntSize aSize,
497
gfx::SurfaceFormat aFormat,
498
gfx::BackendType aMoz2DBackend,
499
LayersBackend aLayersBackend,
500
TextureFlags aFlags,
501
TextureAllocationFlags aAllocFlags,
502
IShmemAllocator* aAllocator) {
503
MOZ_ASSERT(aAllocator);
504
// Should have used CreateForYCbCr.
505
MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
506
507
if (!aAllocator) {
508
return nullptr;
509
}
510
511
if (aSize.width <= 0 || aSize.height <= 0) {
512
gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x"
513
<< aSize.height;
514
return nullptr;
515
}
516
517
uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
518
if (!bufSize) {
519
return nullptr;
520
}
521
522
mozilla::ipc::Shmem shm;
523
if (!aAllocator->AllocUnsafeShmem(bufSize, OptimalShmemType(), &shm)) {
524
return nullptr;
525
}
526
527
uint8_t* buf = shm.get<uint8_t>();
528
if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags, true)) {
529
return nullptr;
530
}
531
532
bool hasIntermediateBuffer = ComputeHasIntermediateBuffer(
533
aFormat, aLayersBackend, aAllocFlags & ALLOC_ALLOW_DIRECT_MAPPING);
534
535
BufferDescriptor descriptor =
536
RGBDescriptor(aSize, aFormat, hasIntermediateBuffer);
537
538
return new ShmemTextureData(descriptor, aMoz2DBackend, shm);
539
}
540
541
TextureData* ShmemTextureData::CreateSimilar(
542
LayersIPCChannel* aAllocator, LayersBackend aLayersBackend,
543
TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const {
544
return ShmemTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend,
545
aLayersBackend, aFlags, aAllocFlags,
546
aAllocator);
547
}
548
549
void ShmemTextureData::Deallocate(LayersIPCChannel* aAllocator) {
550
aAllocator->DeallocShmem(mShmem);
551
}
552
553
} // namespace layers
554
} // namespace mozilla