/* * Copyright (C) 2008 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. */ #define LOG_TAG "SurfaceFlinger" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "VRamHeap.h" #include "GPUHardware.h" #if HAVE_ANDROID_OS #include #endif #include "GPUHardware/GPUHardware.h" /* * This file manages the GPU if there is one. The intent is that this code * needs to be different for every devce. Currently there is no abstraction, * but in the long term, this code needs to be refactored so that API and * implementation are separated. * * In this particular implementation, the GPU, its memory and register are * managed here. Clients (such as OpenGL ES) request the GPU when then need * it and are given a revokable heap containing the registers on memory. * */ namespace android { // --------------------------------------------------------------------------- // size reserved for GPU surfaces // 1200 KB fits exactly: // - two 320*480 16-bits double-buffered surfaces // - one 320*480 32-bits double-buffered surface // - one 320*240 16-bits double-bufferd, 4x anti-aliased surface static const int GPU_RESERVED_SIZE = 1200 * 1024; static const int GPUR_SIZE = 1 * 1024 * 1024; // --------------------------------------------------------------------------- /* * GPUHandle is a special IMemory given to the client. It represents their * handle to the GPU. Once they give it up, they loose GPU access, or if * they explicitely revoke their acces through the binder code 1000. * In both cases, this triggers a callback to revoke() * first, and then actually powers down the chip. * * In the case of a misbehaving app, GPUHardware can ask for an immediate * release of the GPU to the target process which should answer by calling * code 1000 on GPUHandle. If it doesn't in a timely manner, the GPU will * be revoked from under their feet. * * We should never hold a strong reference on GPUHandle. In practice this * shouldn't be a big issue though because clients should use code 1000 and * not rely on the dtor being called. * */ class GPUHandle : public BnMemory { public: GPUHandle(const sp& gpu, const sp& heap) : mGPU(gpu), mClientHeap(heap) { } virtual ~GPUHandle(); virtual sp getMemory(ssize_t* offset, size_t* size) const; virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); void setOwner(int owner) { mOwner = owner; } private: void revokeNotification(); wp mGPU; sp mClientHeap; int mOwner; }; GPUHandle::~GPUHandle() { //LOGD("GPUHandle %p released, revoking GPU", this); revokeNotification(); } void GPUHandle::revokeNotification() { sp hw(mGPU.promote()); if (hw != 0) { hw->revoke(mOwner); } } sp GPUHandle::getMemory(ssize_t* offset, size_t* size) const { if (offset) *offset = 0; if (size) *size = mClientHeap !=0 ? mClientHeap->virtualSize() : 0; return mClientHeap; } status_t GPUHandle::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { status_t err = BnMemory::onTransact(code, data, reply, flags); if (err == UNKNOWN_TRANSACTION && code == 1000) { int callingPid = IPCThreadState::self()->getCallingPid(); //LOGD("pid %d voluntarily revoking gpu", callingPid); if (callingPid == mOwner) { revokeNotification(); // we've revoked the GPU, don't do it again later when we // are destroyed. mGPU.clear(); } else { LOGW("%d revoking someone else's gpu? (owner=%d)", callingPid, mOwner); } err = NO_ERROR; } return err; } // --------------------------------------------------------------------------- class MemoryHeapRegs : public MemoryHeapPmem { public: MemoryHeapRegs(const wp& gpu, const sp& heap); virtual ~MemoryHeapRegs(); sp mapMemory(size_t offset, size_t size); virtual void revoke(); private: wp mGPU; }; MemoryHeapRegs::MemoryHeapRegs(const wp& gpu, const sp& heap) : MemoryHeapPmem(heap), mGPU(gpu) { #if HAVE_ANDROID_OS if (heapID()>0) { /* this is where the GPU is powered on and the registers are mapped * in the client */ //LOGD("ioctl(HW3D_GRANT_GPU)"); int err = ioctl(heapID(), HW3D_GRANT_GPU, base()); if (err) { // it can happen if the master heap has been closed already // in which case the GPU already is revoked (app crash for // instance). //LOGW("HW3D_GRANT_GPU failed (%s), mFD=%d, base=%p", // strerror(errno), heapID(), base()); } } #endif } MemoryHeapRegs::~MemoryHeapRegs() { } sp MemoryHeapRegs::mapMemory(size_t offset, size_t size) { sp memory; sp gpu = mGPU.promote(); if (heapID()>0 && gpu!=0) memory = new GPUHandle(gpu, this); return memory; } void MemoryHeapRegs::revoke() { MemoryHeapPmem::revoke(); #if HAVE_ANDROID_OS if (heapID() > 0) { //LOGD("ioctl(HW3D_REVOKE_GPU)"); int err = ioctl(heapID(), HW3D_REVOKE_GPU, base()); LOGE_IF(err, "HW3D_REVOKE_GPU failed (%s), mFD=%d, base=%p", strerror(errno), heapID(), base()); } #endif } // --------------------------------------------------------------------------- class GPURegisterHeap : public PMemHeapInterface { public: GPURegisterHeap(const sp& gpu) : PMemHeapInterface("/dev/hw3d", GPUR_SIZE), mGPU(gpu) { } virtual ~GPURegisterHeap() { } virtual sp createClientHeap() { sp parentHeap(this); return new MemoryHeapRegs(mGPU, parentHeap); } private: wp mGPU; }; /*****************************************************************************/ GPUHardware::GPUHardware() : mOwner(NO_OWNER) { } GPUHardware::~GPUHardware() { } sp GPUHardware::request(int pid) { sp dealer; LOGD("pid %d requesting gpu surface (current owner = %d)", pid, mOwner); const int self_pid = getpid(); if (pid == self_pid) { // can't use GPU from surfaceflinger's process return dealer; } Mutex::Autolock _l(mLock); if (mOwner != pid) { // someone already has the gpu. takeBackGPULocked(); // releaseLocked() should be a no-op most of the time releaseLocked(); requestLocked(); } dealer = mAllocator; mOwner = pid; if (dealer == 0) { mOwner = SURFACE_FAILED; } LOGD_IF(dealer!=0, "gpu surface granted to pid %d", mOwner); return dealer; } status_t GPUHardware::request(const sp& callback, ISurfaceComposer::gpu_info_t* gpu) { sp gpuHandle; IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int self_pid = getpid(); LOGD("pid %d requesting gpu core (owner = %d)", pid, mOwner); if (pid == self_pid) { // can't use GPU from surfaceflinger's process return PERMISSION_DENIED; } Mutex::Autolock _l(mLock); if (mOwner != pid) { // someone already has the gpu. takeBackGPULocked(); // releaseLocked() should be a no-op most of the time releaseLocked(); requestLocked(); } if (mHeapR.isValid()) { gpu->count = 2; gpu->regions[0].region = mHeap0.map(true); gpu->regions[0].reserved = mHeap0.reserved; gpu->regions[1].region = mHeap1.map(true); gpu->regions[1].reserved = mHeap1.reserved; gpu->regs = mHeapR.map(); if (gpu->regs != 0) { static_cast< GPUHandle* >(gpu->regs.get())->setOwner(pid); } mCallback = callback; mOwner = pid; //LOGD("gpu core granted to pid %d, handle base=%p", // mOwner, gpu->regs->pointer()); } else { LOGW("couldn't grant gpu core to pid %d", pid); } return NO_ERROR; } void GPUHardware::revoke(int pid) { Mutex::Autolock _l(mLock); if (mOwner > 0) { if (pid != mOwner) { LOGW("GPU owned by %d, revoke from %d", mOwner, pid); return; } //LOGD("revoke pid=%d, owner=%d", pid, mOwner); // mOwner could be <0 if the same process acquired the GPU // several times without releasing it first. mCondition.signal(); releaseLocked(true); } } status_t GPUHardware::friendlyRevoke() { Mutex::Autolock _l(mLock); takeBackGPULocked(); //LOGD("friendlyRevoke owner=%d", mOwner); releaseLocked(true); return NO_ERROR; } void GPUHardware::takeBackGPULocked() { sp callback = mCallback; mCallback.clear(); if (callback != 0) { callback->gpuLost(); // one-way mCondition.waitRelative(mLock, ms2ns(250)); } } void GPUHardware::requestLocked() { if (mAllocator == 0) { GPUPart* part = 0; sp surfaceHeap; if (mHeap1.promote() == false) { //LOGD("requestLocked: (1) creating new heap"); mHeap1.set(new PMemHeap("/dev/pmem_gpu1", 0, GPU_RESERVED_SIZE)); } if (mHeap1.isValid()) { //LOGD("requestLocked: (1) heap is valid"); // NOTE: if GPU1 is available we use it for our surfaces // this could be device specific, so we should do something more // generic surfaceHeap = static_cast< PMemHeap* >( mHeap1.getHeap().get() ); part = &mHeap1; if (mHeap0.promote() == false) { //LOGD("requestLocked: (0) creating new heap"); mHeap0.set(new PMemHeap("/dev/pmem_gpu0")); } } else { //LOGD("requestLocked: (1) heap is not valid"); // No GPU1, use GPU0 only if (mHeap0.promote() == false) { //LOGD("requestLocked: (0) creating new heap"); mHeap0.set(new PMemHeap("/dev/pmem_gpu0", 0, GPU_RESERVED_SIZE)); } if (mHeap0.isValid()) { //LOGD("requestLocked: (0) heap is valid"); surfaceHeap = static_cast< PMemHeap* >( mHeap0.getHeap().get() ); part = &mHeap0; } } if (mHeap0.isValid() || mHeap1.isValid()) { if (mHeapR.promote() == false) { //LOGD("requestLocked: (R) creating new register heap"); mHeapR.set(new GPURegisterHeap(this)); } } else { // we got nothing... mHeap0.clear(); mHeap1.clear(); } if (mHeapR.isValid() == false) { //LOGD("requestLocked: (R) register heap not valid!!!"); // damn, couldn't get the gpu registers! mHeap0.clear(); mHeap1.clear(); surfaceHeap.clear(); part = NULL; } if (surfaceHeap != 0 && part && part->getClientHeap()!=0) { part->reserved = GPU_RESERVED_SIZE; part->surface = true; mAllocatorDebug = static_cast( surfaceHeap->getAllocator().get()); mAllocator = new MemoryDealer( part->getClientHeap(), surfaceHeap->getAllocator()); } } } void GPUHardware::releaseLocked(bool dispose) { /* * if dispose is set, we will force the destruction of the heap, * so it is given back to other systems, such as camera. * Otherwise, we'll keep a weak pointer to it, this way we might be able * to reuse it later if it's still around. */ //LOGD("revoking gpu from pid %d", mOwner); mOwner = NO_OWNER; mAllocator.clear(); mCallback.clear(); /* if we're asked for a full revoke, dispose only of the heap * we're not using for surface (as we might need it while drawing) */ mHeap0.release(mHeap0.surface ? false : dispose); mHeap1.release(mHeap1.surface ? false : dispose); mHeapR.release(false); } // ---------------------------------------------------------------------------- // for debugging / testing ... sp GPUHardware::getAllocator() const { Mutex::Autolock _l(mLock); sp allocator = mAllocatorDebug.promote(); return allocator; } void GPUHardware::unconditionalRevoke() { Mutex::Autolock _l(mLock); releaseLocked(); } // --------------------------------------------------------------------------- GPUHardware::GPUPart::GPUPart() : surface(false), reserved(0) { } GPUHardware::GPUPart::~GPUPart() { } const sp& GPUHardware::GPUPart::getHeap() const { return mHeap; } const sp& GPUHardware::GPUPart::getClientHeap() const { return mClientHeap; } bool GPUHardware::GPUPart::isValid() const { return ((mHeap!=0) && (mHeap->base() != MAP_FAILED)); } void GPUHardware::GPUPart::clear() { mHeap.clear(); mHeapWeak.clear(); mClientHeap.clear(); surface = false; } void GPUHardware::GPUPart::set(const sp& heap) { mHeapWeak.clear(); if (heap!=0 && heap->base() == MAP_FAILED) { mHeap.clear(); mClientHeap.clear(); } else { mHeap = heap; mClientHeap = mHeap->createClientHeap(); } } bool GPUHardware::GPUPart::promote() { //LOGD("mHeapWeak=%p, mHeap=%p", mHeapWeak.unsafe_get(), mHeap.get()); if (mHeap == 0) { mHeap = mHeapWeak.promote(); } if (mHeap != 0) { if (mClientHeap != 0) { mClientHeap->revoke(); } mClientHeap = mHeap->createClientHeap(); } else { surface = false; } return mHeap != 0; } sp GPUHardware::GPUPart::map(bool clear) { sp memory; if (mClientHeap != NULL) { memory = mClientHeap->mapMemory(0, mHeap->virtualSize()); if (clear && memory!=0) { //StopWatch sw("memset"); memset(memory->pointer(), 0, memory->size()); } } return memory; } void GPUHardware::GPUPart::release(bool dispose) { if (mClientHeap != 0) { mClientHeap->revoke(); mClientHeap.clear(); } if (dispose) { if (mHeapWeak!=0 && mHeap==0) { mHeap = mHeapWeak.promote(); } if (mHeap != 0) { mHeap->dispose(); mHeapWeak.clear(); mHeap.clear(); } else { surface = false; } } else { if (mHeap != 0) { mHeapWeak = mHeap; mHeap.clear(); } } } // --------------------------------------------------------------------------- }; // namespace android