mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1006688 - Support capture screen via adb r=mwu
This commit is contained in:
parent
a27f60facb
commit
945e2a4fe7
@ -447,6 +447,8 @@ public:
|
||||
|
||||
nsIWidget* GetWidget() { return mWidget; }
|
||||
|
||||
void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
|
||||
|
||||
protected:
|
||||
// Protected destructor, to discourage deletion outside of Release():
|
||||
virtual ~CompositorParent();
|
||||
@ -461,7 +463,6 @@ protected:
|
||||
virtual bool DeallocPLayerTransactionParent(PLayerTransactionParent* aLayers) override;
|
||||
virtual void ScheduleTask(CancelableTask*, int);
|
||||
void CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
|
||||
void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
|
||||
|
||||
void SetEGLSurfaceSize(int width, int height);
|
||||
|
||||
|
@ -21,12 +21,14 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <cutils/atomic.h>
|
||||
#include <cutils/log.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include <gui/IDisplayEventConnection.h>
|
||||
#include <gui/GraphicBufferAlloc.h>
|
||||
#include <gui/Surface.h>
|
||||
#include <ui/DisplayInfo.h>
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
@ -39,6 +41,8 @@
|
||||
#include "gfxPrefs.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/layers/CompositorParent.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
@ -272,6 +276,127 @@ sp<IDisplayEventConnection> FakeSurfaceComposer::createDisplayEventConnection()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Capture screen into an IGraphiBufferProducer
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class Barrier {
|
||||
public:
|
||||
inline Barrier() : state(CLOSED) { }
|
||||
inline ~Barrier() { }
|
||||
|
||||
// Release any threads waiting at the Barrier.
|
||||
// Provides release semantics: preceding loads and stores will be visible
|
||||
// to other threads before they wake up.
|
||||
void open() {
|
||||
Mutex::Autolock _l(lock);
|
||||
state = OPENED;
|
||||
cv.broadcast();
|
||||
}
|
||||
|
||||
// Reset the Barrier, so wait() will block until open() has been called.
|
||||
void close() {
|
||||
Mutex::Autolock _l(lock);
|
||||
state = CLOSED;
|
||||
}
|
||||
|
||||
// Wait until the Barrier is OPEN.
|
||||
// Provides acquire semantics: no subsequent loads or stores will occur
|
||||
// until wait() returns.
|
||||
void wait() const {
|
||||
Mutex::Autolock _l(lock);
|
||||
while (state == CLOSED) {
|
||||
cv.wait(lock);
|
||||
}
|
||||
}
|
||||
private:
|
||||
enum { OPENED, CLOSED };
|
||||
mutable Mutex lock;
|
||||
mutable Condition cv;
|
||||
volatile int state;
|
||||
};
|
||||
|
||||
/* The code below is here to handle b/8734824
|
||||
*
|
||||
* We create a IGraphicBufferProducer wrapper that forwards all calls
|
||||
* to the calling binder thread, where they are executed. This allows
|
||||
* the calling thread to be reused (on the other side) and not
|
||||
* depend on having "enough" binder threads to handle the requests.
|
||||
*
|
||||
*/
|
||||
|
||||
class GraphicProducerWrapper : public BBinder, public MessageHandler {
|
||||
sp<IGraphicBufferProducer> impl;
|
||||
sp<Looper> looper;
|
||||
status_t result;
|
||||
bool exitPending;
|
||||
bool exitRequested;
|
||||
mutable Barrier barrier;
|
||||
volatile int32_t memoryBarrier;
|
||||
uint32_t code;
|
||||
Parcel const* data;
|
||||
Parcel* reply;
|
||||
|
||||
enum {
|
||||
MSG_API_CALL,
|
||||
MSG_EXIT
|
||||
};
|
||||
|
||||
/*
|
||||
* this is called by our "fake" BpGraphicBufferProducer. We package the
|
||||
* data and reply Parcel and forward them to the calling thread.
|
||||
*/
|
||||
virtual status_t transact(uint32_t code,
|
||||
const Parcel& data, Parcel* reply, uint32_t flags) {
|
||||
this->code = code;
|
||||
this->data = &data;
|
||||
this->reply = reply;
|
||||
android_atomic_acquire_store(0, &memoryBarrier);
|
||||
if (exitPending) {
|
||||
// if we've exited, we run the message synchronously right here
|
||||
handleMessage(Message(MSG_API_CALL));
|
||||
} else {
|
||||
barrier.close();
|
||||
looper->sendMessage(this, Message(MSG_API_CALL));
|
||||
barrier.wait();
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* here we run on the binder calling thread. All we've got to do is
|
||||
* call the real BpGraphicBufferProducer.
|
||||
*/
|
||||
virtual void handleMessage(const Message& message) {
|
||||
android_atomic_release_load(&memoryBarrier);
|
||||
if (message.what == MSG_API_CALL) {
|
||||
impl->asBinder()->transact(code, data[0], reply);
|
||||
barrier.open();
|
||||
} else if (message.what == MSG_EXIT) {
|
||||
exitRequested = true;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
GraphicProducerWrapper(const sp<IGraphicBufferProducer>& impl) :
|
||||
impl(impl), looper(new Looper(true)), result(NO_ERROR),
|
||||
exitPending(false), exitRequested(false) {
|
||||
}
|
||||
|
||||
status_t waitForResponse() {
|
||||
do {
|
||||
looper->pollOnce(-1);
|
||||
} while (!exitRequested);
|
||||
return result;
|
||||
}
|
||||
|
||||
void exit(status_t result) {
|
||||
this->result = result;
|
||||
exitPending = true;
|
||||
looper->sendMessage(this, Message(MSG_EXIT));
|
||||
}
|
||||
};
|
||||
|
||||
status_t
|
||||
FakeSurfaceComposer::captureScreen(const sp<IBinder>& display
|
||||
, const sp<IGraphicBufferProducer>& producer
|
||||
@ -290,7 +415,116 @@ FakeSurfaceComposer::captureScreen(const sp<IBinder>& display
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return INVALID_OPERATION;
|
||||
if (display == 0 || producer == 0) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// Limit only to primary display
|
||||
if (display != mPrimaryDisplay) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// this creates a "fake" BBinder which will serve as a "fake" remote
|
||||
// binder to receive the marshaled calls and forward them to the
|
||||
// real remote (a BpGraphicBufferProducer)
|
||||
sp<GraphicProducerWrapper> wrapper = new GraphicProducerWrapper(producer);
|
||||
// the asInterface() call below creates our "fake" BpGraphicBufferProducer
|
||||
// which does the marshaling work forwards to our "fake remote" above.
|
||||
sp<IGraphicBufferProducer> fakeProducer = IGraphicBufferProducer::asInterface(wrapper);
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableFunction([&]() {
|
||||
captureScreenImp(fakeProducer, reqWidth, reqHeight, wrapper.get());
|
||||
});
|
||||
NS_DispatchToMainThread(runnable);
|
||||
|
||||
status_t result = wrapper->waitForResponse();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
class RunnableCallTask : public Task {
|
||||
public:
|
||||
explicit RunnableCallTask(nsIRunnable* aRunnable)
|
||||
: mRunnable(aRunnable) {}
|
||||
|
||||
void Run() override
|
||||
{
|
||||
mRunnable->Run();
|
||||
}
|
||||
protected:
|
||||
nsCOMPtr<nsIRunnable> mRunnable;
|
||||
};
|
||||
|
||||
void
|
||||
FakeSurfaceComposer::captureScreenImp(const sp<IGraphicBufferProducer>& producer,
|
||||
uint32_t reqWidth,
|
||||
uint32_t reqHeight,
|
||||
const sp<GraphicProducerWrapper>& wrapper)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(wrapper.get());
|
||||
|
||||
RefPtr<nsScreenGonk> screen = nsScreenManagerGonk::GetPrimaryScreen();
|
||||
|
||||
// get screen geometry
|
||||
nsIntRect screenBounds = screen->GetNaturalBounds().ToUnknownRect();
|
||||
const uint32_t hw_w = screenBounds.width;
|
||||
const uint32_t hw_h = screenBounds.height;
|
||||
|
||||
if (reqWidth > hw_w || reqHeight > hw_h) {
|
||||
ALOGE("size mismatch (%d, %d) > (%d, %d)",
|
||||
reqWidth, reqHeight, hw_w, hw_h);
|
||||
static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(BAD_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
reqWidth = (!reqWidth) ? hw_w : reqWidth;
|
||||
reqHeight = (!reqHeight) ? hw_h : reqHeight;
|
||||
|
||||
nsScreenGonk* screenPtr = screen.forget().take();
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableFunction([screenPtr, reqWidth, reqHeight, producer, wrapper]() {
|
||||
// create a surface (because we're a producer, and we need to
|
||||
// dequeue/queue a buffer)
|
||||
sp<Surface> sur = new Surface(producer);
|
||||
ANativeWindow* window = sur.get();
|
||||
|
||||
if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != NO_ERROR) {
|
||||
static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(BAD_VALUE);
|
||||
NS_ReleaseOnMainThread(screenPtr);
|
||||
return;
|
||||
}
|
||||
uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
|
||||
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
|
||||
|
||||
int err = 0;
|
||||
err = native_window_set_buffers_dimensions(window, reqWidth, reqHeight);
|
||||
err |= native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
|
||||
err |= native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888);
|
||||
err |= native_window_set_usage(window, usage);
|
||||
|
||||
status_t result = NO_ERROR;
|
||||
if (err == NO_ERROR) {
|
||||
ANativeWindowBuffer* buffer;
|
||||
result = native_window_dequeue_buffer_and_wait(window, &buffer);
|
||||
if (result == NO_ERROR) {
|
||||
nsresult rv = screenPtr->MakeSnapshot(buffer);
|
||||
if (rv != NS_OK) {
|
||||
result = INVALID_OPERATION;
|
||||
}
|
||||
window->queueBuffer(window, buffer, -1);
|
||||
}
|
||||
} else {
|
||||
result = BAD_VALUE;
|
||||
}
|
||||
native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
|
||||
static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(result);
|
||||
NS_ReleaseOnMainThread(screenPtr);
|
||||
});
|
||||
|
||||
mozilla::layers::CompositorParent::CompositorLoop()->PostTask(
|
||||
FROM_HERE, new RunnableCallTask(runnable));
|
||||
}
|
||||
|
||||
#if ANDROID_VERSION >= 21
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/Looper.h>
|
||||
|
||||
#include <binder/BinderService.h>
|
||||
|
||||
@ -38,6 +39,7 @@ namespace android {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class GraphicProducerWrapper;
|
||||
class IGraphicBufferAlloc;
|
||||
|
||||
enum {
|
||||
@ -128,6 +130,11 @@ private:
|
||||
*/
|
||||
uint32_t setDisplayStateLocked(const DisplayState& s);
|
||||
|
||||
void captureScreenImp(const sp<IGraphicBufferProducer>& producer,
|
||||
uint32_t reqWidth,
|
||||
uint32_t reqHeight,
|
||||
const sp<GraphicProducerWrapper>& wrapper);
|
||||
|
||||
sp<IBinder> mPrimaryDisplay;
|
||||
|
||||
struct DisplayDeviceState {
|
||||
|
@ -92,19 +92,25 @@ if CONFIG['MOZ_B2G_CAMERA'] or CONFIG['MOZ_OMX_DECODER'] or CONFIG['MOZ_WEBRTC']
|
||||
'GonkNativeWindowICS.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_B2G_CAMERA'] or CONFIG['MOZ_OMX_DECODER']:
|
||||
if CONFIG['ANDROID_VERSION'] >= '18':
|
||||
SOURCES += [
|
||||
'FakeSurfaceComposer.cpp',
|
||||
]
|
||||
if CONFIG['ANDROID_VERSION'] >= '18':
|
||||
SOURCES += [
|
||||
'FakeSurfaceComposer.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
if CONFIG['ANDROID_VERSION'] >= '18':
|
||||
LOCAL_INCLUDES += [
|
||||
'%' + '%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
|
||||
'frameworks/native/opengl/include',
|
||||
]
|
||||
]
|
||||
|
||||
# Suppress some GCC warnings being treated as errors:
|
||||
# - about attributes on forward declarations for types that are already
|
||||
# defined, which complains about an important MOZ_EXPORT for android::AString
|
||||
if CONFIG['GNU_CC']:
|
||||
CXXFLAGS += ['-Wno-error=attributes']
|
||||
CXXFLAGS += ['-Wno-error=attributes', '-Wno-overloaded-virtual']
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
|
@ -524,6 +524,63 @@ nsScreenGonk::QueueBuffer(ANativeWindowBuffer* buf)
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsScreenGonk::MakeSnapshot(ANativeWindowBuffer* aBuffer)
|
||||
{
|
||||
MOZ_ASSERT(CompositorParent::IsInCompositorThread());
|
||||
MOZ_ASSERT(aBuffer);
|
||||
|
||||
layers::CompositorParent* compositorParent = mCompositorParent;
|
||||
if (!compositorParent) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int width = aBuffer->width, height = aBuffer->height;
|
||||
uint8_t* mappedBuffer = nullptr;
|
||||
if (gralloc_module()->lock(gralloc_module(), aBuffer->handle,
|
||||
GRALLOC_USAGE_SW_READ_OFTEN |
|
||||
GRALLOC_USAGE_SW_WRITE_OFTEN,
|
||||
0, 0, width, height,
|
||||
reinterpret_cast<void**>(&mappedBuffer))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
SurfaceFormat format = HalFormatToSurfaceFormat(GetSurfaceFormat());
|
||||
RefPtr<DrawTarget> mTarget =
|
||||
Factory::CreateDrawTargetForData(
|
||||
BackendType::CAIRO,
|
||||
mappedBuffer,
|
||||
IntSize(width, height),
|
||||
aBuffer->stride * gfx::BytesPerPixel(format),
|
||||
format);
|
||||
if (!mTarget) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
gfx::IntRect rect = GetRect().ToUnknownRect();
|
||||
compositorParent->ForceComposeToTarget(mTarget, &rect);
|
||||
|
||||
// Convert from BGR to RGB
|
||||
// XXX this is a temporary solution. It consumes extra cpu cycles,
|
||||
if (NeedsRBSwap(GetSurfaceFormat())) {
|
||||
LOGE("Slow path of making Snapshot!!!");
|
||||
SurfaceFormat format = HalFormatToSurfaceFormat(GetSurfaceFormat());
|
||||
gfxUtils::ConvertBGRAtoRGBA(
|
||||
mappedBuffer,
|
||||
aBuffer->stride * aBuffer->height * gfx::BytesPerPixel(format));
|
||||
mappedBuffer = nullptr;
|
||||
}
|
||||
gralloc_module()->unlock(gralloc_module(), aBuffer->handle);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsScreenGonk::SetCompositorParent(layers::CompositorParent* aCompositorParent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mCompositorParent = aCompositorParent;
|
||||
}
|
||||
|
||||
#if ANDROID_VERSION >= 17
|
||||
android::DisplaySurface*
|
||||
nsScreenGonk::GetDisplaySurface()
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "hardware/hwcomposer.h"
|
||||
|
||||
#include "libdisplay/GonkDisplay.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Hal.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsBaseScreen.h"
|
||||
@ -44,6 +45,7 @@ namespace gl {
|
||||
}
|
||||
namespace layers {
|
||||
class CompositorVsyncScheduler;
|
||||
class CompositorParent;
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,6 +59,8 @@ class nsScreenGonk : public nsBaseScreen
|
||||
typedef mozilla::hal::ScreenConfiguration ScreenConfiguration;
|
||||
typedef mozilla::GonkDisplay GonkDisplay;
|
||||
typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect;
|
||||
typedef mozilla::layers::CompositorParent CompositorParent;
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
||||
public:
|
||||
nsScreenGonk(uint32_t aId,
|
||||
@ -85,9 +89,12 @@ public:
|
||||
ScreenConfiguration GetConfiguration();
|
||||
bool IsPrimaryScreen();
|
||||
|
||||
already_AddRefed<mozilla::gfx::DrawTarget> StartRemoteDrawing();
|
||||
already_AddRefed<DrawTarget> StartRemoteDrawing();
|
||||
void EndRemoteDrawing();
|
||||
|
||||
nsresult MakeSnapshot(ANativeWindowBuffer* aBuffer);
|
||||
void SetCompositorParent(CompositorParent* aCompositorParent);
|
||||
|
||||
#if ANDROID_VERSION >= 17
|
||||
android::DisplaySurface* GetDisplaySurface();
|
||||
int GetPrevDispAcquireFd();
|
||||
@ -144,6 +151,7 @@ protected:
|
||||
#endif
|
||||
bool mIsMirroring; // Non-primary screen only
|
||||
RefPtr<nsScreenGonk> mMirroringScreen; // Primary screen only
|
||||
mozilla::Atomic<CompositorParent*> mCompositorParent;
|
||||
|
||||
// Accessed and updated only on compositor thread
|
||||
GonkDisplay::DisplayType mDisplayType;
|
||||
@ -155,7 +163,7 @@ protected:
|
||||
// If we're using a BasicCompositor, these fields are temporarily
|
||||
// set during frame composition. They wrap the hardware
|
||||
// framebuffer.
|
||||
RefPtr<mozilla::gfx::DrawTarget> mFramebufferTarget;
|
||||
RefPtr<DrawTarget> mFramebufferTarget;
|
||||
ANativeWindowBuffer* mFramebuffer;
|
||||
/**
|
||||
* Points to a mapped gralloc buffer between calls to lock and unlock.
|
||||
@ -170,7 +178,7 @@ protected:
|
||||
//
|
||||
// Only accessed on the compositor thread, except during
|
||||
// destruction.
|
||||
RefPtr<mozilla::gfx::DrawTarget> mBackBuffer;
|
||||
RefPtr<DrawTarget> mBackBuffer;
|
||||
};
|
||||
|
||||
class nsScreenManagerGonk final : public nsIScreenManager
|
||||
|
@ -687,8 +687,11 @@ nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
|
||||
}
|
||||
|
||||
CreateCompositor();
|
||||
if (mCompositorParent && mScreen->IsPrimaryScreen()) {
|
||||
mComposer2D->SetCompositorParent(mCompositorParent);
|
||||
if (mCompositorParent) {
|
||||
mScreen->SetCompositorParent(mCompositorParent);
|
||||
if (mScreen->IsPrimaryScreen()) {
|
||||
mComposer2D->SetCompositorParent(mCompositorParent);
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(mLayerManager);
|
||||
return mLayerManager;
|
||||
@ -697,9 +700,12 @@ nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
|
||||
void
|
||||
nsWindow::DestroyCompositor()
|
||||
{
|
||||
if (mCompositorParent && mScreen->IsPrimaryScreen()) {
|
||||
// Unset CompositorParent
|
||||
mComposer2D->SetCompositorParent(nullptr);
|
||||
if (mCompositorParent) {
|
||||
mScreen->SetCompositorParent(nullptr);
|
||||
if (mScreen->IsPrimaryScreen()) {
|
||||
// Unset CompositorParent
|
||||
mComposer2D->SetCompositorParent(nullptr);
|
||||
}
|
||||
}
|
||||
nsBaseWidget::DestroyCompositor();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user