bool LayerScreenshot::isFrozen() { Mutex::Autolock _l(gLock); int count = gLayerStatus.size(); if (count <= 0) return false; LayerStatus top = gLayerStatus.valueAt(0); int identity = gLayerStatus.keyAt(0); for (int i = 1; i < count; i++) { LayerStatus state = gLayerStatus.valueAt(i); if (state.z > top.z) { top = state; identity = gLayerStatus.keyAt(i); } } XLOGI("[%s] LayerScreenshot top:%d (orient:%d, alpha:%d)", __func__, identity, top.orient, top.alpha); if ((top.orient & Transform::ROT_INVALID) || (0xff > top.alpha)) { XLOGI(" No need to freeze screen..."); return false; } XLOGD(" Freeze screen..."); return true; }
/** * Activate/deactivate a given sensor * * @param sensorType - Type of the sensor to activate/deactivate * @param activate - 1 to activate and 0 to deactivate the sensor * @param rate - sampling rate in microseconds */ int SensorsCommand::sensors_activate(int sensorType, int activate, int rate) { // Get sensor device handle given the sensor type int count; for (count = 0; count < mSensorListCount; count++) { if (mSensorsList[count].type == sensorType) { break; } } LOG_ERROR((count >= mSensorListCount), "No such h/w sensor available %d", sensorType); int err; Value jsonMsg; if (activate) { // Only activate if not already active if (!mActiveSensors.valueFor(mSensorsList[count].handle)) { err = mDevice->activate( reinterpret_cast<struct sensors_poll_device_t *>(mDevice), mSensorsList[count].handle, 1); LOG_ERROR((err != 0), "Failed to activate the sensor"); if (mDevice->common.version >= SENSORS_DEVICE_API_VERSION_1_1) { mDevice->batch(mDevice, mSensorsList[count].handle, 0, ms2ns(rate), ms2ns(rate)); } else { mDevice->setDelay( reinterpret_cast<struct sensors_poll_device_t *>(mDevice), mSensorsList[count].handle, ms2ns(rate)); } mActiveSensorCount++; mActiveSensors.add(mSensorsList[count].handle, true); } jsonMsg["eventName"] = "activated"; mSensorsListener->sendEvent(jsonMsg); } else { if (mActiveSensors.valueFor(mSensorsList[count].handle)) { mActiveSensorCount--; mActiveSensors.removeItem(mSensorsList[count].handle); // If no more active sensors then stop polling if (mActiveSensorCount <= 0) { mSensorsPoll->stop(); mSensorsPoll = NULL; } err = mDevice->activate( reinterpret_cast<struct sensors_poll_device_t *>(mDevice), mSensorsList[count].handle, 0); LOG_ERROR((err != 0), "Failed to deactivate the sensor"); } jsonMsg["eventName"] = "deactivated"; mSensorsListener->sendEvent(jsonMsg); } return 0; }
void MCameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) { INFO("%s", __func__); Mutex::Autolock _l(mLock); ssize_t i = mNotifyCount.indexOfKey(msgType); if (i < 0) { mNotifyCount.add(msgType, 1); } else { ++mNotifyCount.editValueAt(i); } mCond.signal(); }
void LayerScreenshot::clearStatus() { Mutex::Autolock _l(gLock); for (uint32_t i = 0; i < gLayerStatus.size(); i++) { int identity = gLayerStatus.keyAt(i); if (this->getIdentity() == (uint32_t)identity) { gLayerStatus.removeItem(identity); XLOGI("[%s] del:%d, count:%d", __func__, identity, gLayerStatus.size()); break; } } }
void RemoteCallbackList::finishBroadcast() { Mutex::Autolock _l(_mutex); DefaultKeyedVector< wp<IBinder>, sp<RemoteCallback> >* pActiveCBs =_activeCBsList.valueFor(pthread_self()); if(pActiveCBs == NULL) { ALOGW("%s called outside of a broadcast",__FUNCTION__); return; } pActiveCBs->clear(); _activeCBsList.removeItem(pthread_self()); delete pActiveCBs; pActiveCBs = NULL; }
void GPUHardware::binderDied(const wp<IBinder>& who) { Mutex::Autolock _l(mLock); pid_t pid = mRegisteredClients.valueFor(who); if (pid != 0) { ssize_t index = mClients.indexOfKey(pid); if (index >= 0) { //LOGD("*** removing client at %d", index); Client& client(mClients.editValueAt(index)); client.revokeAllHeaps(); // not really needed in theory mClients.removeItemsAt(index); if (mClients.size() == 0) { //LOGD("*** was last client closing everything"); mCallback.clear(); mAllocator.clear(); mCurrentAllocator.clear(); mSMIHeap.clear(); mREGHeap.clear(); // NOTE: we cannot clear the EBI heap because surfaceflinger // itself may be using it, since this is where surfaces // are allocated. if we're in the middle of compositing // a surface (even if its process just died), we cannot // rip the heap under our feet. mOwner = NO_OWNER; } } } }
GuiExtPoolItem::GuiExtPoolItem(const sp<IBinder>& token, bool isHwcNeeded, uint32_t poolId, uint32_t /*w*/, uint32_t /*h*/, DefaultKeyedVector< uint32_t, sp<DispInfo> >& dispList, sp<IBinder::DeathRecipient> observer) : mConsumerDeathListener(NULL) //, mGPUUsedBq(NULL) , mGPUUsedProducer(NULL) , mGPUUsedConsumer(NULL) #if !SUPPORT_MULTIBQ_FOR_HWC , mHwcUsedBq(NULL) #endif , mId(poolId) , mIsHwcNeeded(isHwcNeeded) , mGpuUsedBufNum(MAX_GLES_DEQUEUED_NUM) , mProducerPid(-1) , mProducerToken(token) , mProducerDeathObserver(observer) { GUIEXT_LOGV("GuiExtPoolItem ctor, poolId=%d, isHwcNeeded=%d, token=%p", poolId, isHwcNeeded, token.get()); //String8 name(szUsageName[0]); //name.appendFormat("_%d", poolId); //mGPUUsedBq = createBufferQueue(w, h, mGpuUsedBufNum, name); //createBufferQueue(w, h, mGpuUsedBufNum, name, &mGPUUsedProducer, &mGPUUsedConsumer); #ifdef CONFIG_FOR_SOURCE_PQ setOverlaySessionMode(true); #endif if (mIsHwcNeeded) { String8 name(szUsageName[1]); name.appendFormat("_%d", poolId); #if SUPPORT_MULTIBQ_FOR_HWC uint32_t disp_size = dispList.size(); for (uint32_t i = 0; i < disp_size; i++) { uint32_t type = dispList[i]->type; String8 extname(name); extname.appendFormat("_%d", type); sp<HwcBqSlot> slot = new HwcBqSlot(); //slot->bq = createBufferQueue(dispList[i]->w, dispList[i]->h, dispList[i]->bufNum, extname); createBufferQueue(dispList[i]->w, dispList[i]->h, dispList[i]->bufNum, extname, &slot->mProducer, &slot->mConsumer); slot->type = type; slot->bufNum = dispList[i]->bufNum; mHwcUsedBqList.add(type, slot); } #else mHwcUsedBq = createBufferQueue(dispList[0]->w, dispList[0]->h, dispList[i]->bufNum, name); #endif } mProducerPid = (NULL != token->localBinder()) ? getpid() : IPCThreadState::self()->getCallingPid(); for (uint32_t i = 0; i < GUI_EXT_USAGE_MAX; i++) mIsDisconnected[i] = true; }
void GPUHardware::registerCallbackLocked(const sp<IGPUCallback>& callback, Client& client) { sp<IBinder> binder = callback->asBinder(); if (mRegisteredClients.add(binder, client.pid) >= 0) { binder->linkToDeath(this); } }
void* RemoteCallbackList::getBroadcastCookie(int index) { Mutex::Autolock _l(_mutex); DefaultKeyedVector< wp<IBinder>, sp<RemoteCallback> >* pActiveCBs =_activeCBsList.valueFor(pthread_self()); if (pActiveCBs == NULL || pActiveCBs->size() <= 0){ ALOGE("%s activeCBs null\n",__FUNCTION__); return NULL; } int size = pActiveCBs->size(); if(index >= 0 && index < size && pActiveCBs->valueAt(index)!= NULL) { return pActiveCBs->valueAt(index)->_cookie; } ALOGW("%s out of border",__FUNCTION__); return NULL; }
void MCameraClient::waitData(int32_t msgType, OP op, int count) { INFO("waitData: %d, %d, %d", msgType, op, count); Mutex::Autolock _l(mLock); while (true) { int v = mDataCount.valueFor(msgType); if (test(op, v, count)) { break; } mCond.wait(mLock); } }
void MCameraClient::dataCallback(int32_t msgType, const sp<IMemory>& data) { INFO("%s", __func__); int dataSize = data->size(); INFO("data type = %d, size = %d", msgType, dataSize); Mutex::Autolock _l(mLock); ssize_t i = mDataCount.indexOfKey(msgType); if (i < 0) { mDataCount.add(msgType, 1); mDataSize.add(msgType, dataSize); } else { ++mDataCount.editValueAt(i); mDataSize.editValueAt(i) = dataSize; } mCond.signal(); if (msgType == CAMERA_MSG_VIDEO_FRAME) { ASSERT(mReleaser != NULL); mReleaser->releaseRecordingFrame(data); } }
void LayerScreenshot::setGeometry(const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface& layer) { LayerBaseClient::setGeometry(hw, layer); Mutex::Autolock _l(gLock); LayerStatus state; const LayerBase::State& s(drawingState()); const Transform tr(hw->getTransform() * s.transform); state.z = s.z; state.alpha = s.alpha; state.orient = tr.getOrientation(); gLayerStatus.add(this->getIdentity(), state); XLOGI("[%s] add:%d, count:%d", __func__, this->getIdentity(), gLayerStatus.size()); }
int RemoteCallbackList::beginBroadcast() { Mutex::Autolock _l(_mutex); DefaultKeyedVector< wp<IBinder>, sp<RemoteCallback> >* pActiveCBs = _activeCBsList.valueFor(pthread_self()); if(pActiveCBs != NULL) { ALOGW("%s called while already in a broadcast\n",__FUNCTION__); return -1; } pActiveCBs = new DefaultKeyedVector< wp<IBinder>, sp<RemoteCallback> >; if(pActiveCBs == NULL) { ALOGW("[%s]out of memory\n",__FUNCTION__); return -2; } int size = _callbacks.size(); for(int i=0;i<size;i++) { pActiveCBs->add(_callbacks.keyAt(i),_callbacks[i]); } _activeCBsList.add(pthread_self(),pActiveCBs); return size; }
__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) { // eglGetProcAddress() could be the very first function called // in which case we must make sure we've initialized ourselves, this // happens the first time egl_get_display() is called. clearError(); if (egl_init_drivers() == EGL_FALSE) { setError(EGL_BAD_PARAMETER, NULL); return NULL; } // The EGL_ANDROID_blob_cache extension should not be exposed to // applications. It is used internally by the Android EGL layer. if (!strcmp(procname, "eglSetBlobCacheFuncsANDROID")) { return NULL; } __eglMustCastToProperFunctionPointerType addr; addr = findProcAddress(procname, sExtentionMap, NELEM(sExtentionMap)); if (addr) return addr; // this protects accesses to sGLExtentionMap and sGLExtentionSlot pthread_mutex_lock(&sExtensionMapMutex); /* * Since eglGetProcAddress() is not associated to anything, it needs * to return a function pointer that "works" regardless of what * the current context is. * * For this reason, we return a "forwarder", a small stub that takes * care of calling the function associated with the context * currently bound. * * We first look for extensions we've already resolved, if we're seeing * this extension for the first time, we go through all our * implementations and call eglGetProcAddress() and record the * result in the appropriate implementation hooks and return the * address of the forwarder corresponding to that hook set. * */ const String8 name(procname); addr = sGLExtentionMap.valueFor(name); const int slot = sGLExtentionSlot; LOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS, "no more slots for eglGetProcAddress(\"%s\")", procname); if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) { bool found = false; for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { egl_connection_t* const cnx = &gEGLImpl[i]; if (cnx->dso && cnx->egl.eglGetProcAddress) { found = true; // Extensions are independent of the bound context cnx->hooks[GLESv1_INDEX]->ext.extensions[slot] = cnx->hooks[GLESv2_INDEX]->ext.extensions[slot] = #if EGL_TRACE gHooksDebug.ext.extensions[slot] = gHooksTrace.ext.extensions[slot] = #endif cnx->egl.eglGetProcAddress(procname); } } if (found) { addr = gExtensionForwarders[slot]; if (!strcmp(procname, "glEGLImageTargetTexture2DOES")) { glEGLImageTargetTexture2DOES_impl = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)addr; addr = (__eglMustCastToProperFunctionPointerType)glEGLImageTargetTexture2DOES_wrapper; } if (!strcmp(procname, "glEGLImageTargetRenderbufferStorageOES")) { glEGLImageTargetRenderbufferStorageOES_impl = (PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC)addr; addr = (__eglMustCastToProperFunctionPointerType)glEGLImageTargetRenderbufferStorageOES_wrapper; } sGLExtentionMap.add(name, addr); sGLExtentionSlot++; } } pthread_mutex_unlock(&sExtensionMapMutex); return addr; }
void StringPool::sortByConfig() { LOG_ALWAYS_FATAL_IF(mOriginalPosToNewPos.size() > 0, "Can't sort string pool after already sorted."); const size_t N = mEntryArray.size(); // This is a vector that starts out with a 1:1 mapping to entries // in the array, which we will sort to come up with the desired order. // At that point it maps from the new position in the array to the // original position the entry appeared. Vector<size_t> newPosToOriginalPos; newPosToOriginalPos.setCapacity(N); for (size_t i=0; i < N; i++) { newPosToOriginalPos.add(i); } // Sort the array. NOISY(printf("SORTING STRINGS BY CONFIGURATION...\n")); // Vector::sort uses insertion sort, which is very slow for this data set. // Use quicksort instead because we don't need a stable sort here. qsort_r_compat(newPosToOriginalPos.editArray(), N, sizeof(size_t), this, config_sort); //newPosToOriginalPos.sort(config_sort, this); NOISY(printf("DONE SORTING STRINGS BY CONFIGURATION.\n")); // Create the reverse mapping from the original position in the array // to the new position where it appears in the sorted array. This is // so that clients can re-map any positions they had previously stored. mOriginalPosToNewPos = newPosToOriginalPos; for (size_t i=0; i<N; i++) { mOriginalPosToNewPos.editItemAt(newPosToOriginalPos[i]) = i; } #if 0 SortedVector<entry> entries; for (size_t i=0; i<N; i++) { printf("#%d was %d: %s\n", i, newPosToOriginalPos[i], mEntries[mEntryArray[newPosToOriginalPos[i]]].makeConfigsString().string()); entries.add(mEntries[mEntryArray[i]]); } for (size_t i=0; i<entries.size(); i++) { printf("Sorted config #%d: %s\n", i, entries[i].makeConfigsString().string()); } #endif // Now we rebuild the arrays. Vector<entry> newEntries; Vector<size_t> newEntryArray; Vector<entry_style> newEntryStyleArray; DefaultKeyedVector<size_t, size_t> origOffsetToNewOffset; for (size_t i=0; i<N; i++) { // We are filling in new offset 'i'; oldI is where we can find it // in the original data structure. size_t oldI = newPosToOriginalPos[i]; // This is the actual entry associated with the old offset. const entry& oldEnt = mEntries[mEntryArray[oldI]]; // This is the same entry the last time we added it to the // new entry array, if any. ssize_t newIndexOfOffset = origOffsetToNewOffset.indexOfKey(oldI); size_t newOffset; if (newIndexOfOffset < 0) { // This is the first time we have seen the entry, so add // it. newOffset = newEntries.add(oldEnt); newEntries.editItemAt(newOffset).indices.clear(); } else { // We have seen this entry before, use the existing one // instead of adding it again. newOffset = origOffsetToNewOffset.valueAt(newIndexOfOffset); } // Update the indices to include this new position. newEntries.editItemAt(newOffset).indices.add(i); // And add the offset of the entry to the new entry array. newEntryArray.add(newOffset); // Add any old style to the new style array. if (mEntryStyleArray.size() > 0) { if (oldI < mEntryStyleArray.size()) { newEntryStyleArray.add(mEntryStyleArray[oldI]); } else { newEntryStyleArray.add(entry_style()); } } } // Now trim any entries at the end of the new style array that are // not needed. for (ssize_t i=newEntryStyleArray.size()-1; i>=0; i--) { const entry_style& style = newEntryStyleArray[i]; if (style.spans.size() > 0) { // That's it. break; } // This one is not needed; remove. newEntryStyleArray.removeAt(i); } // All done, install the new data structures and upate mValues with // the new positions. mEntries = newEntries; mEntryArray = newEntryArray; mEntryStyleArray = newEntryStyleArray; mValues.clear(); for (size_t i=0; i<mEntries.size(); i++) { const entry& ent = mEntries[i]; mValues.add(ent.value, ent.indices[0]); } #if 0 printf("FINAL SORTED STRING CONFIGS:\n"); for (size_t i=0; i<mEntries.size(); i++) { const entry& ent = mEntries[i]; printf("#" ZD " %s: %s\n", (ZD_TYPE)i, ent.makeConfigsString().string(), String8(ent.value).string()); } #endif }
void MCameraClient::assertNotify(int32_t msgType, OP op, int count) { Mutex::Autolock _l(mLock); int v = mNotifyCount.valueFor(msgType); ASSERT(test(op, v, count)); }
void MCameraClient::assertDataSize(int32_t msgType, OP op, int dataSize) { Mutex::Autolock _l(mLock); int v = mDataSize.valueFor(msgType); assertTest(op, v, dataSize); }
void MCameraClient::assertData(int32_t msgType, OP op, int count) { Mutex::Autolock _l(mLock); int v = mDataCount.valueFor(msgType); assertTest(op, v, count); }
void MCameraClient::clearStat() { Mutex::Autolock _l(mLock); mNotifyCount.clear(); mDataCount.clear(); mDataSize.clear(); }
__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) { // eglGetProcAddress() could be the very first function called // in which case we must make sure we've initialized ourselves, this // happens the first time egl_get_display() is called. clearError(); if (egl_init_drivers() == EGL_FALSE) { setError(EGL_BAD_PARAMETER, NULL); return NULL; } // These extensions should not be exposed to applications. They're used // internally by the Android EGL layer. if (!strcmp(procname, "eglSetBlobCacheFuncsANDROID") || !strcmp(procname, "eglDupNativeFenceFDANDROID") || !strcmp(procname, "eglWaitSyncANDROID") || !strcmp(procname, "eglHibernateProcessIMG") || !strcmp(procname, "eglAwakenProcessIMG")) { return NULL; } __eglMustCastToProperFunctionPointerType addr; addr = findProcAddress(procname, sExtentionMap, NELEM(sExtentionMap)); if (addr) return addr; // this protects accesses to sGLExtentionMap and sGLExtentionSlot pthread_mutex_lock(&sExtensionMapMutex); /* * Since eglGetProcAddress() is not associated to anything, it needs * to return a function pointer that "works" regardless of what * the current context is. * * For this reason, we return a "forwarder", a small stub that takes * care of calling the function associated with the context * currently bound. * * We first look for extensions we've already resolved, if we're seeing * this extension for the first time, we go through all our * implementations and call eglGetProcAddress() and record the * result in the appropriate implementation hooks and return the * address of the forwarder corresponding to that hook set. * */ const String8 name(procname); addr = sGLExtentionMap.valueFor(name); const int slot = sGLExtentionSlot; ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS, "no more slots for eglGetProcAddress(\"%s\")", procname); #if EGL_TRACE gl_hooks_t *debugHooks = GLTrace_getGLHooks(); #endif if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) { bool found = false; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglGetProcAddress) { found = true; // Extensions are independent of the bound context cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = #if EGL_TRACE debugHooks->ext.extensions[slot] = gHooksTrace.ext.extensions[slot] = #endif cnx->egl.eglGetProcAddress(procname); } if (found) { addr = gExtensionForwarders[slot]; sGLExtentionMap.add(name, addr); sGLExtentionSlot++; } } pthread_mutex_unlock(&sExtensionMapMutex); return addr; }
int LayerScreenshot::getCount() { Mutex::Autolock _l(gLock); int count = gLayerStatus.size(); return count; }
namespace android { // --------------------------------------------------------------------------- struct LayerStatus { LayerStatus() : z(0), alpha(0xff), orient(Transform::ROT_0) { } uint32_t z; uint8_t alpha; int32_t orient; }; DefaultKeyedVector<int, LayerStatus> gLayerStatus; Mutex gLock; void LayerScreenshot::setGeometry(const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface& layer) { LayerBaseClient::setGeometry(hw, layer); Mutex::Autolock _l(gLock); LayerStatus state; const LayerBase::State& s(drawingState()); const Transform tr(hw->getTransform() * s.transform); state.z = s.z; state.alpha = s.alpha; state.orient = tr.getOrientation(); gLayerStatus.add(this->getIdentity(), state); XLOGI("[%s] add:%d, count:%d", __func__, this->getIdentity(), gLayerStatus.size()); } void LayerScreenshot::clearStatus() { Mutex::Autolock _l(gLock); for (uint32_t i = 0; i < gLayerStatus.size(); i++) { int identity = gLayerStatus.keyAt(i); if (this->getIdentity() == (uint32_t)identity) { gLayerStatus.removeItem(identity); XLOGI("[%s] del:%d, count:%d", __func__, identity, gLayerStatus.size()); break; } } } int LayerScreenshot::getCount() { Mutex::Autolock _l(gLock); int count = gLayerStatus.size(); return count; } bool LayerScreenshot::isFrozen() { Mutex::Autolock _l(gLock); int count = gLayerStatus.size(); if (count <= 0) return false; LayerStatus top = gLayerStatus.valueAt(0); int identity = gLayerStatus.keyAt(0); for (int i = 1; i < count; i++) { LayerStatus state = gLayerStatus.valueAt(i); if (state.z > top.z) { top = state; identity = gLayerStatus.keyAt(i); } } XLOGI("[%s] LayerScreenshot top:%d (orient:%d, alpha:%d)", __func__, identity, top.orient, top.alpha); if ((top.orient & Transform::ROT_INVALID) || (0xff > top.alpha)) { XLOGI(" No need to freeze screen..."); return false; } XLOGD(" Freeze screen..."); return true; } void LayerScreenshot::setPerFrameData(const sp<const DisplayDevice>& hw, HWComposer::HWCLayerInterface& layer) { LayerBaseClient::setPerFrameData(hw, layer); layer.setBuffer(NULL); } void LayerScreenshot::computeGeometryOrient( const sp<const DisplayDevice>& hw, LayerMesh* mesh, int orient) const { const Layer::State& s(drawingState()); int hw_w = hw->getWidth(); int hw_h = hw->getHeight(); uint32_t flags = Transform::ROT_0; switch ((4 - orient) % 4) { case DisplayState::eOrientation90: flags = Transform::ROT_90; break; case DisplayState::eOrientation180: flags = Transform::ROT_180; break; case DisplayState::eOrientation270: flags = Transform::ROT_270; break; default: XLOGE("[%s] invalid orientation: %d", __func__, orient); break; } Transform orientTransform; if (orient & DisplayState::eOrientationSwapMask) { orientTransform.set(flags, hw_h, hw_w); } else { orientTransform.set(flags, hw_w, hw_h); } const Transform tr(hw->getTransform() * s.transform * orientTransform); if (mesh) { tr.transform(mesh->mVertices[0], 0, 0); tr.transform(mesh->mVertices[1], 0, hw_h); tr.transform(mesh->mVertices[2], hw_w, hw_h); tr.transform(mesh->mVertices[3], hw_w, 0); for (size_t i=0 ; i<4 ; i++) { mesh->mVertices[i][1] = hw_h - mesh->mVertices[i][1]; } } } // --------------------------------------------------------------------------- }; // namespace android