You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
7.8 KiB
243 lines
7.8 KiB
// Copyright (C) 2015 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "GpuFrameBridge.h"
|
|
|
|
#include <stdio.h> // for printf
|
|
#include <stdlib.h> // for NULL, free, malloc
|
|
#include <string.h> // for memcpy
|
|
|
|
#include <atomic> // for atomic_bool, memory_o...
|
|
#include <memory> // for unique_ptr
|
|
|
|
#include "base/Lock.h" // for Lock, AutoLock
|
|
#include "base/MessageChannel.h"
|
|
#include "..//opengles.h" // for android_getFlushReadP...
|
|
|
|
#ifdef _WIN32
|
|
#undef ERROR
|
|
#endif
|
|
|
|
namespace android {
|
|
namespace opengl {
|
|
|
|
using android::base::AutoLock;
|
|
using android::base::Lock;
|
|
using android::base::MessageChannel;
|
|
|
|
namespace {
|
|
|
|
// A small structure to model a single frame of the GPU display,
|
|
// as passed between the EmuGL and main loop thread.
|
|
struct Frame {
|
|
int width;
|
|
int height;
|
|
void* pixels;
|
|
bool isValid;
|
|
|
|
Frame(int w, int h, const void* pixels)
|
|
: width(w), height(h), pixels(NULL), isValid(true) {
|
|
this->pixels = ::malloc(w * 4 * h);
|
|
}
|
|
|
|
~Frame() { ::free(pixels); }
|
|
};
|
|
|
|
// Real implementation of GpuFrameBridge interface.
|
|
class Bridge : public GpuFrameBridge {
|
|
public:
|
|
// Constructor.
|
|
Bridge()
|
|
: GpuFrameBridge(),
|
|
mRecFrame(NULL),
|
|
mRecTmpFrame(NULL),
|
|
mRecFrameUpdated(false),
|
|
mReadPixelsFunc(android_getReadPixelsFunc()),
|
|
mFlushPixelPipeline(android_getFlushReadPixelPipeline()) {}
|
|
|
|
// The gpu bridge receives frames from a buffer that can contain multiple
|
|
// frames. usually the bridge is one frame behind. This is usually not a
|
|
// problem when we have a high enough framerate. However it is possible that
|
|
// the framerate drops really low (even <1). This can result in the bridge
|
|
// never delivering this "stuck frame".
|
|
//
|
|
// As a work around we will flush the reader pipeline if no frames are
|
|
// delivered within at most 2x kFrameDelayms
|
|
const long kMinFPS = 24;
|
|
const long kFrameDelayMs = 1000 / kMinFPS;
|
|
|
|
// Destructor
|
|
virtual ~Bridge() {
|
|
if (mRecFrame) {
|
|
delete mRecFrame;
|
|
}
|
|
if (mRecTmpFrame) {
|
|
delete mRecTmpFrame;
|
|
}
|
|
}
|
|
|
|
// virtual void setLooper(android::base::Looper* aLooper) override {
|
|
// mTimer = std::unique_ptr<android::base::Looper::Timer>(
|
|
// aLooper->createTimer(_on_frame_notify, this));
|
|
// }
|
|
|
|
void notify() {
|
|
AutoLock delay(mDelayLock);
|
|
switch (mDelayCallback) {
|
|
case FrameDelay::Reschedule:
|
|
// mTimer->startRelative(kFrameDelayMs);
|
|
mDelayCallback = FrameDelay::Scheduled;
|
|
break;
|
|
case FrameDelay::Scheduled:
|
|
// mTimer->stop();
|
|
mDelayCallback = FrameDelay::Firing;
|
|
delay.unlock();
|
|
mFlushPixelPipeline(mDisplayId);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
// static void _on_frame_notify(void* opaque,
|
|
// android::base::Looper::Timer* timer) {
|
|
// Bridge* worker = static_cast<Bridge*>(opaque);
|
|
// worker->notify();
|
|
// }
|
|
|
|
// Implementation of the GpuFrameBridge::postRecordFrame() method, must be
|
|
// called from the EmuGL thread.
|
|
virtual void postRecordFrame(int width,
|
|
int height,
|
|
const void* pixels) override {
|
|
postFrame(width, height, pixels, true);
|
|
}
|
|
|
|
virtual void postRecordFrameAsync(int width,
|
|
int height,
|
|
const void* pixels) override {
|
|
postFrame(width, height, pixels, false);
|
|
}
|
|
|
|
virtual void* getRecordFrame() override {
|
|
if (mRecFrameUpdated.exchange(false)) {
|
|
AutoLock lock(mRecLock);
|
|
memcpy(mRecFrame->pixels, mRecTmpFrame->pixels,
|
|
mRecFrame->width * mRecFrame->height * 4);
|
|
mRecFrame->isValid = true;
|
|
}
|
|
return mRecFrame && mRecFrame->isValid ? mRecFrame->pixels : nullptr;
|
|
}
|
|
|
|
virtual void* getRecordFrameAsync() override {
|
|
if (mRecFrameUpdated.exchange(false)) {
|
|
AutoLock lock(mRecLock);
|
|
mReadPixelsFunc(mRecFrame->pixels,
|
|
mRecFrame->width * mRecFrame->height * 4,
|
|
mDisplayId);
|
|
mRecFrame->isValid = true;
|
|
}
|
|
return mRecFrame && mRecFrame->isValid ? mRecFrame->pixels : nullptr;
|
|
}
|
|
|
|
virtual void invalidateRecordingBuffers() override {
|
|
{
|
|
AutoLock lock(mRecLock);
|
|
// Release the buffers because new recording in the furture may have
|
|
// different resolution if multi display changes its resolution.
|
|
if (mRecFrame) {
|
|
delete mRecFrame;
|
|
mRecFrame = nullptr;
|
|
}
|
|
if (mRecTmpFrame) {
|
|
delete mRecTmpFrame;
|
|
mRecTmpFrame = nullptr;
|
|
}
|
|
}
|
|
mRecFrameUpdated.store(false, std::memory_order_release);
|
|
}
|
|
|
|
void setFrameReceiver(FrameAvailableCallback receiver,
|
|
void* opaque) override {
|
|
mReceiver = receiver;
|
|
mReceiverOpaque = opaque;
|
|
}
|
|
|
|
void postFrame(int width, int height, const void* pixels, bool copy) {
|
|
{
|
|
AutoLock lock(mRecLock);
|
|
if (!mRecFrame) {
|
|
mRecFrame = new Frame(width, height, pixels);
|
|
}
|
|
if (!mRecTmpFrame) {
|
|
mRecTmpFrame = new Frame(width, height, pixels);
|
|
}
|
|
if (copy) {
|
|
memcpy(mRecTmpFrame->pixels, pixels, width * height * 4);
|
|
}
|
|
}
|
|
mRecFrameUpdated.store(true, std::memory_order_release);
|
|
if (mReceiver) {
|
|
mReceiver(mReceiverOpaque);
|
|
AutoLock delay(mDelayLock);
|
|
switch (mDelayCallback) {
|
|
case FrameDelay::NotScheduled:
|
|
// mTimer->startRelative(kFrameDelayMs);
|
|
mDelayCallback = FrameDelay::Scheduled;
|
|
break;
|
|
case FrameDelay::Firing:
|
|
mDelayCallback = FrameDelay::NotScheduled;
|
|
break;
|
|
default:
|
|
mDelayCallback = FrameDelay::Reschedule;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void setDisplayId(uint32_t displayId) override {
|
|
mDisplayId = displayId;
|
|
}
|
|
enum class FrameDelay {
|
|
NotScheduled = 0, // No delay timer is scheduled
|
|
Scheduled, // A delay timer has been scheduled and will flush the
|
|
// pipeline on expiration
|
|
Reschedule, // Do not flush the pipeline, but reschedule
|
|
Firing, // A callback has been scheduled, nothing needs to happen
|
|
};
|
|
|
|
private:
|
|
FrameAvailableCallback mReceiver = nullptr;
|
|
void* mReceiverOpaque = nullptr;
|
|
Lock mRecLock;
|
|
Frame* mRecFrame;
|
|
Frame* mRecTmpFrame;
|
|
std::atomic_bool mRecFrameUpdated;
|
|
ReadPixelsFunc mReadPixelsFunc = 0;
|
|
uint32_t mDisplayId = 0;
|
|
FlushReadPixelPipeline mFlushPixelPipeline = 0;
|
|
|
|
// std::unique_ptr<android::base::Looper::Timer> mTimer;
|
|
Lock mDelayLock;
|
|
FrameDelay mDelayCallback{FrameDelay::NotScheduled};
|
|
};
|
|
} // namespace
|
|
// static
|
|
GpuFrameBridge* GpuFrameBridge::create() {
|
|
return new Bridge();
|
|
}
|
|
|
|
} // namespace opengl
|
|
} // namespace android
|