Android T(13) 源码分析 — BufferQueue 的分析
Android T(13) 源码分析 — BufferQueue 的分析
文章目录
- Android T(13) 源码分析 — BufferQueue 的分析
- 前言
- 摘要
- 一、Java 层的 BufferQueue 分析
- 二、原生层的 BufferQueue 分析
- 1、BLASTBufferQueue 的创建
- 2、BLASTBufferQueue 的更新
- 3、Surface 的创建
- 总结
前言
该系列文章基于 Android T(13) 的源码进行分析,内容会逐渐丰富,欢迎大家点赞关注。
摘要
BufferQueue 是 Android 图形系统中的一个关键组件,它连接了生成图形数据缓冲区的组件(生产者)和接收这些数据以进行显示或进一步处理的组件(消费者)。几乎所有的图形数据缓冲区在系统中的传递都依赖于 BufferQueue。
当生产者需要一个缓冲区时,通过调用 dequeueBuffer() 从 BufferQueue 请求一个空闲缓冲区,指定缓冲区的宽度、高度、像素格式和使用标志。生产者填充缓冲区后,再调用 queueBuffer() 将缓冲区返回队列。消费者通过 acquireBuffer() 获取缓冲区,并在使用完毕后通过 releaseBuffer() 将缓冲区返回队列,本篇重点介绍一下 BufferQueue 的创建。
一、Java 层的 BufferQueue 分析
BufferQueue 是生产者和消费者的链接纽带,其依托于 SurfaceControl,与 Surface 联系紧密,之前我们分析过 Java 层 SurfaceControl 的创建,当 SurfaceControl 创建成功之后,接下来就进行 BufferQueue 创建。我们还是从 relayoutWindow 开始,看一下生产者端的 BufferQueue 是如何创建的:
//frameworks/base/core/java/android/view/ViewRootImpl.javaprivate int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,boolean insetsPending) throws RemoteException {//...if (mSurfaceControl.isValid()) {if (!useBLAST()) {mSurface.copyFrom(mSurfaceControl);} else {updateBlastSurfaceIfNeeded();}//...
BLAST特性是 Android 12 新推出的,默认开启,具体看一下 updateBlastSurfaceIfNeeded 实现:
//frameworks/base/core/java/android/view/ViewRootImpl.javavoid updateBlastSurfaceIfNeeded() {if (!mSurfaceControl.isValid()) {return;}if (mBlastBufferQueue != null && mBlastBufferQueue.isSameSurfaceControl(mSurfaceControl)) {mBlastBufferQueue.update(mSurfaceControl,mSurfaceSize.x, mSurfaceSize.y,mWindowAttributes.format);return;}// If the SurfaceControl has been updated, destroy and recreate the BBQ to reset the BQ and// BBQ states.if (mBlastBufferQueue != null) {mBlastBufferQueue.destroy();}mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);Surface blastSurface = mBlastBufferQueue.createSurface();// Only call transferFrom if the surface has changed to prevent inc the generation ID and// causing EGL resources to be recreated.mSurface.transferFrom(blastSurface);}
初始 mBlastBufferQueue 是空,会新建一个 BLASTBufferQueue 对象,我们看一下 BLASTBufferQueue 是如何定义的:
//frameworks/base/graphics/java/android/graphics/BLASTBufferQueue.java/** Create a new connection with the surface flinger. */public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height,@PixelFormat.Format int format) {this(name, true /* updateDestinationFrame */);update(sc, width, height, format);}public BLASTBufferQueue(String name, boolean updateDestinationFrame) {mNativeObject = nativeCreate(name, updateDestinationFrame);}
BLASTBufferQueue 会新建一个 mNativeObject 对象,继续看一下 nativeCreate 实现:
//frameworks/base/core/jni/android_graphics_BLASTBufferQueue.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName,jboolean updateDestinationFrame) {ScopedUtfChars name(env, jName);sp<BLASTBufferQueue> queue = new BLASTBufferQueue(name.c_str(), updateDestinationFrame);queue->incStrong((void*)nativeCreate);return reinterpret_cast<jlong>(queue.get());
}
这里通过JNI,新建了一个原生层的 BLASTBufferQueue 对象,我们稍后分析原生层的代码。在此之前,我们再看一下 BLASTBufferQueue 函数中 update 的实现:
//frameworks/base/graphics/java/android/graphics/BLASTBufferQueue.javapublic void update(SurfaceControl sc, int width, int height, @PixelFormat.Format int format) {nativeUpdate(mNativeObject, sc.mNativeObject, width, height, format);}
这里 nativeUpdate 同样会转入 JNI,具体实现:
//frameworks/base/core/jni/android_graphics_BLASTBufferQueue.cpp
static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,jlong height, jint format) {sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, format);
}
nativeUpdate 函数中的 update 最后也会通过 JNI 链接到原生层,把 Buffer 的宽度,高度以及像素格式传递给原生层,进一步丰富 BLASTBufferQueue。最后,我们回到 updateBlastSurfaceIfNeeded 函数,看一下 blastSurface 的创建,具体 createSurface 实现 :
//frameworks/base/graphics/java/android/graphics/BLASTBufferQueue.java/*** @return a new Surface instance from the IGraphicsBufferProducer of the adapter.*/public Surface createSurface() {return nativeGetSurface(mNativeObject, false /* includeSurfaceControlHandle */);}
可以看到,createSurface 同样调到了 JNI,函数 nativeGetSurface 实现:
//frameworks/base/core/jni/android_graphics_BLASTBufferQueue.cpp
static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr,jboolean includeSurfaceControlHandle) {sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);return android_view_Surface_createFromSurface(env,queue->getSurface(includeSurfaceControlHandle));
}
queue 是一个 BLASTBufferQueue 对象,通过 android_view_Surface_createFromSurface 进行复制:
//frameworks/base/core/jni/android_view_Surface.cpp
jobject android_view_Surface_createFromSurface(JNIEnv* env, const sp<Surface>& surface) {jobject surfaceObj = env->NewObject(gSurfaceClassInfo.clazz,gSurfaceClassInfo.ctor, (jlong)surface.get());if (surfaceObj == NULL) {if (env->ExceptionCheck()) {ALOGE("Could not create instance of Surface from IGraphicBufferProducer.");LOGE_EX(env);env->ExceptionClear();}return NULL;}surface->incStrong(&sRefBaseOwner);return surfaceObj;
}
总结下来,relayoutWindow 会在 Java 层创建一个 BLASTBufferQueue 对象,通过 JNI 同步在原生层同步创建一个 BLASTBufferQueue 对象;并且把具体的宽度,高度和像素格式也通过 JNI 传递给原生层,并且从原生层返回一个 blastSurface(Surface) 对象,函数时序图:
二、原生层的 BufferQueue 分析
上一章节我们分析过,Java 层通过调用 JNI 的 nativeCreate 在原生层创建 BufferQueue 对象,然后通过 nativeUpdate 更新 Buffer 的宽度,高度以及像素格式,最后通过 nativeGetSurface 来获取创建的 blastSurface,本节我们按照这个顺序逐一分析原生层的 BufferQueue 创建过程。
1、BLASTBufferQueue 的创建
先看一下,原生层 BLASTBufferQueue 的定义:
//frameworks/native/libs/gui/BLASTBufferQueue.cpp
BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinationFrame): mSurfaceControl(nullptr),mSize(1, 1),mRequestedSize(mSize),mFormat(PIXEL_FORMAT_RGBA_8888),mTransactionReadyCallback(nullptr),mSyncTransaction(nullptr),mUpdateDestinationFrame(updateDestinationFrame) {createBufferQueue(&mProducer, &mConsumer);//...mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,GraphicBuffer::USAGE_HW_COMPOSER |GraphicBuffer::USAGE_HW_TEXTURE,1, false, this);//...
}
在 BLASTBufferQueue 函数中,createBufferQueue 创建了 BufferQueue 对象,并且定义了当前 Buffer 的生产者和消费者:
//frameworks/native/libs/gui/BLASTBufferQueue.cpp
void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,sp<IGraphicBufferConsumer>* outConsumer) {//...sp<BufferQueueCore> core(new BufferQueueCore());//...sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core));//...sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core));consumer->setAllowExtraAcquire(true);//...*outProducer = producer;*outConsumer = consumer;
}
在 createBufferQueue 函数中,新建了一个 BufferQueueCore 对象,IGraphicBufferProducer 对象和 BufferQueueConsumer 对象。其中 core(BufferQueueCore) 是 BufferQueue 的核心部分,是生产者和消费者的纽带,主要负责 BufferQueue 的状态管理, 从定义上也能看出,它涵盖了 BufferQueue 的诸多状态:
//frameworks/native/libs/gui/BufferQueueCore.cpp
BufferQueueCore::BufferQueueCore(): mMutex(),mIsAbandoned(false),mConsumerControlledByApp(false),mConsumerName(getUniqueName()),mConsumerListener(),mConsumerUsageBits(0),mConsumerIsProtected(false),mConnectedApi(NO_CONNECTED_API),mLinkedToDeath(),mConnectedProducerListener(),mBufferReleasedCbEnabled(false),mSlots(),mQueue(),mFreeSlots(),mFreeBuffers(),mUnusedSlots(),mActiveBuffers(),mDequeueCondition(),mDequeueBufferCannotBlock(false),mQueueBufferCanDrop(false),mLegacyBufferDrop(true),mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),mDefaultWidth(1),mDefaultHeight(1),mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),mMaxAcquiredBufferCount(1),mMaxDequeuedBufferCount(1),mBufferHasBeenQueued(false),mFrameCounter(0),mTransformHint(0),mIsAllocating(false),mIsAllocatingCondition(),mAllowAllocation(true),mBufferAge(0),mGenerationNumber(0),mAsyncMode(false),mSharedBufferMode(false),mAutoRefresh(false),mSharedBufferSlot(INVALID_BUFFER_SLOT),mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,HAL_DATASPACE_UNKNOWN),mLastQueuedSlot(INVALID_BUFFER_SLOT),mUniqueId(getUniqueId()),mAutoPrerotation(false),mTransformHintInUse(0) {
//...
}
producer(IGraphicBufferProducer)是 BufferQueue 的生产者,主要负责 Buffer 的申请,调用 dequeueBuffer() 获取一个空闲缓冲区,填充数据后通过 queueBuffer() 将其返回队列,producer 是一个 BBQBufferQueueProducer 对象:
//frameworks/native/libs/gui/BLASTBufferQueue.cppBBQBufferQueueProducer(const sp<BufferQueueCore>& core): BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/) {}
而 BBQBufferQueueProducer 又指向了一个 BufferQueueProducer 对象:
//frameworks/native/libs/gui/BufferQueueProducer.cpp
BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,bool consumerIsSurfaceFlinger) :mCore(core),mSlots(core->mSlots),mConsumerName(),mStickyTransform(0),mConsumerIsSurfaceFlinger(consumerIsSurfaceFlinger),mLastQueueBufferFence(Fence::NO_FENCE),mLastQueuedTransform(0),mCallbackMutex(),mNextCallbackTicket(0),mCurrentCallbackTicket(0),mCallbackCondition(),mDequeueTimeout(-1),mDequeueWaitingForAllocation(false) {}
consumer(BufferQueueConsumer) 是 BufferQueue 的消费者,主要负责 Buffer 的使用,调用 acquireBuffer() 获取缓冲区,并在使用完毕后通过 releaseBuffer() 将其返回队列,BufferQueueConsumer 的初始化:
//frameworks/native/libs/gui/BufferQueueConsumer.cpp
BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :mCore(core),mSlots(core->mSlots),mConsumerName() {}
总结一下,core 是 BufferQueue 的核心,主要负责 Buffer 的状态管理,是生产者和消费者之间的重要纽带。producer 是生产者,主要负责 Buffer 空间的申请,consumer 是消费者,主要负责 Buffer 的使用。
2、BLASTBufferQueue 的更新
JNI 通过 nativeGetSurface 更新 Buffer 的宽度,高度以及像素格式,这一小节我们看一下,原生层的 update 函数实现:
//frameworks/native/libs/gui/BLASTBufferQueue.cpp
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height,int32_t format) {//...if (mFormat != format) {mFormat = format;mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));}const bool surfaceControlChanged = !SurfaceControl::isSameSurface(mSurfaceControl, surface);if (surfaceControlChanged && mSurfaceControl != nullptr) {BQA_LOGD("Updating SurfaceControl without recreating BBQ");}bool applyTransaction = false;// Always update the native object even though they might have the same layer handle, so we can// get the updated transform hint from WM.mSurfaceControl = surface;SurfaceComposerClient::Transaction t;if (surfaceControlChanged) {t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure,layer_state_t::eEnableBackpressure);applyTransaction = true;}mTransformHint = mSurfaceControl->getTransformHint();mBufferItemConsumer->setTransformHint(mTransformHint);BQA_LOGV("update width=%d height=%d format=%d mTransformHint=%d", width, height, format,mTransformHint);ui::Size newSize(width, height);if (mRequestedSize != newSize) {mRequestedSize.set(newSize);mBufferItemConsumer->setDefaultBufferSize(mRequestedSize.width, mRequestedSize.height);if (mLastBufferInfo.scalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) {// If the buffer supports scaling, update the frame immediately since the client may// want to scale the existing buffer to the new size.mSize = mRequestedSize;if (mUpdateDestinationFrame) {t.setDestinationFrame(mSurfaceControl, Rect(newSize));applyTransaction = true;}}}if (applyTransaction) {// All transactions on our apply token are one-way. See comment on mAppliedLastTransactiont.setApplyToken(mApplyToken).apply(false, true);}
}
当高度,宽度等有变化的时候,会更新 BufferQueue。注意,这里 mBufferItemConsumer 对应的是上一小节中创建的 consumer。
3、Surface 的创建
JNI 通过 nativeGetSurface 来获取一块 blastSurface,在 nativeGetSurface 函数中,Surface 是由 BLASTBufferQueue 类中的 getSurface 函数返回的:
//frameworks/native/libs/gui/BLASTBufferQueue.cpp
sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) {std::unique_lock _lock{mMutex};sp<IBinder> scHandle = nullptr;if (includeSurfaceControlHandle && mSurfaceControl) {scHandle = mSurfaceControl->getHandle();}return new BBQSurface(mProducer, true, scHandle, this);
}
在 Java 层,传入的 includeSurfaceControlHandle 是 false,所以这里直接新建一个 BBQSurface 对象:
//frameworks/native/libs/gui/BLASTBufferQueue.cpp
BBQSurface(const sp<IGraphicBufferProducer>& igbp, bool controlledByApp,const sp<IBinder>& scHandle, const sp<BLASTBufferQueue>& bbq): Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {}
BBQSurface 初始化了一个 Surface 对象,并将指针返回给 getSurface。这里 Surface 初始化并没有分配空间,还是一块空的 Buffer:
//frameworks/native/libs/gui/Surface.cpp
Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp,const sp<IBinder>& surfaceControlHandle): mGraphicBufferProducer(bufferProducer),mCrop(Rect::EMPTY_RECT),mBufferAge(0),mGenerationNumber(0),mSharedBufferMode(false),mAutoRefresh(false),mAutoPrerotation(false),mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),mSharedBufferHasBeenQueued(false),mQueriedSupportedTimestamps(false),mFrameTimestampsSupportsPresent(false),mEnableFrameTimestamps(false),mFrameEventHistory(std::make_unique<ProducerFrameEventHistory>()) {// Initialize the ANativeWindow function pointers.ANativeWindow::setSwapInterval = hook_setSwapInterval;ANativeWindow::dequeueBuffer = hook_dequeueBuffer;ANativeWindow::cancelBuffer = hook_cancelBuffer;ANativeWindow::queueBuffer = hook_queueBuffer;ANativeWindow::query = hook_query;ANativeWindow::perform = hook_perform;ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;const_cast<int&>(ANativeWindow::minSwapInterval) = 0;const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;mReqWidth = 0;mReqHeight = 0;mReqFormat = 0;mReqUsage = 0;mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO;mDataSpace = Dataspace::UNKNOWN;mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;mTransform = 0;mStickyTransform = 0;mDefaultWidth = 0;mDefaultHeight = 0;mUserWidth = 0;mUserHeight = 0;mTransformHint = 0;mConsumerRunningBehind = false;mConnectedToCpu = false;mProducerControlledByApp = controlledByApp;mSwapIntervalZero = false;mMaxBufferCount = NUM_BUFFER_SLOTS;mSurfaceControlHandle = surfaceControlHandle;
}
在文章第一部分,我们分析了 blastSurface 是一个 Surface 对象,这一小节通过原生层的代码分析,最后新建了一个原生层的 Surface 对象。函数时序图:
总结
至此,BufferQueue 的创建过程已经分析完成,后续我们会继续分析 BufferQueue 如何通过 dequeueBuffer(),queueBuffer(),acquireBuffer() ,releaseBuffer() 进行 Buffer 管理;以及如何通过 Gralloc 申请具体的 Buffer 空间。
相关文章:

Android T(13) 源码分析 — BufferQueue 的分析
Android T(13) 源码分析 — BufferQueue 的分析 文章目录 Android T(13) 源码分析 — BufferQueue 的分析前言摘要一、Java 层的 BufferQueue 分析二、原生层的 BufferQueue 分析1、BLASTBufferQueue 的创建2、BLASTBufferQueue 的更新3、Surface 的创建 总结 前言 该系列文章…...

Vite+TS项目中配置路径别名
在使用 Vite 和 TypeScript 的项目中配置路径别名,可以简化模块导入路径,提高代码的可读性和维护性。以下是详细的步骤和示例代码: 1. 配置 Vite 别名 前置条件 下载types/node 下面引入的path会用到 npm install types/node --save-dev原…...

看盘细节系列 篇二:集合竞价的9点18分大单打到3%以下或以上,9点19分撤单
文章目录 系列文章现象原因分析时间点含义正常情况测试市场反应诱导跟风操纵股价意图系列文章 看盘细节系列 篇一:集合竞价尾盘突变 现象 集合竞价中 9 点 18 分通过一笔大单或连续几笔大单将股价打到 3% 以下或以上,9 点 19 分又迅速撤单。从而在分时图上留下一根长长的上…...

Java继承简介
继承的本质:是代码的复用,重复使用已经定义好的方法和域(即全局变量) 要掌握继承首先要了解Java方法的重载和重写 方法的重载和重写 方法的重载 当前方法名相同,但是参数类型不同,发生重载 类比数学函…...

redis之哨兵集群搭建
一:哨兵集群工作概览图 1.监控:sentinel通过心跳监控redis的master和slave实例是否正常工作 2.故障转移:假如master出现故障,sentinel会选举一个slave作为新的master,当故障实例恢复后身份会变成slave,会以…...

保姆级AI开发环境搭建
目录 windows下环境搭建1. Python环境搭建2. 下载vLLM2.1 安装CUDA2.2 安装Pytorch2.3 安装vllm 3. 部署Deepseek(huggingface)3.1 DeepSeek的优化建议 4. ollama快速部署Deepseek4.1 下载Ollama4.2 配置Ollma4.2 运行模型4.3 其他Ollama命令 linux下环境…...

Arduino 型号的对比
常见 Arduino 型号的对比表格 涵盖了不同型号的关键参数和特点,方便你根据项目需求进行选择: 型号Arduino UnoArduino Mega 2560Arduino LeonardoArduino NanoArduino Due微控制器ATmega328PATmega2560ATmega32U4ATmega328P 或 ATmega168SAM3X8E&#…...

Kafka系列之:定位topic只能保存最新数据的原因
Kafka系列之:定位topic只能保存最新数据的原因 一、背景二、定位排查方向三、深入排查一、背景 kafka topic保存的数据少,topic只能保存最新的数据二、定位排查方向 能想到的定位排查方向:topic能存储的数据量、topic数据保存的时间、topic数据大小./bin/kafka-configs.sh -…...

AtCoder Beginner Contest 391(A~E题题解)
A - Lucky Direction 思路:纯模拟的一个水题 #include <bits/stdc.h> using namespace std; #define int long long string s; signed main() { cin>>s;for(int i0;i<s.size();i){char cs[i];if(cN){cout<<"S";}else if(c…...

mysql mvcc 锁 关系
多版本并发控制(MVCC)是一种用于数据库并发控制的机制,它可以在保证数据一致性的同时,提高数据库的并发性能。下面结合 MVCC 机制,详细阐述常见的四种事务隔离级别(读未提交、读已提交、可重复读、串行化&a…...

安卓手机基于 Termux 安装 AList 并设置开机自启的详细教程
安装 AList 安装 Termux: 点击下载 更新软件包:打开 Termux,运行以下命令以更新软件包列表并升级已安装的软件包: bash复制 pkg update && pkg upgrade安装 AList:运行以下命令安装 AList: bash复…...

LeetCode:503.下一个更大元素II
跟着carl学算法,本系列博客仅做个人记录,建议大家都去看carl本人的博客,写的真的很好的! 代码随想录 LeetCode:503.下一个更大元素II 给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[…...

实验5 配置OSPFv2验证
实验5 配置OSPFv2验证 1.实验目的 (1)OSPFv2 验证的类型和意义。 (2)配置基于区域的 OSPFv2 简单口令验证和 MD5 验证的方法。 (3)配置基于链路的 OSPFv2 简单口令验证和 MD5 验证的方法。 2.实验准备 配置…...

第二节 docker基础之---镜像构建及挂载
查看当前镜像: [rootdocker ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE [rootdocker ~]#很明显docker是咱们新搭建的所以目前还没有镜像 1,搜索镜像: [rootdocker ~]# docker search centos 搜索镜像并过滤是官…...

论文阅读:MGMAE : Motion Guided Masking for Video Masked Autoencoding
MGMAE:Motion Guided Masking for Video Masked Autoencoding Abstract 掩蔽自编码(Masked Autoencoding)在自监督视频表示学习中展现了出色的表现。时间冗余导致了VideoMAE中高掩蔽比率和定制的掩蔽策略。本文旨在通过引入运动引导掩蔽策略࿰…...

记录一下 在Mac下用pyinstallter 打包 Django项目
安装: pip install pyinstaller 在urls.py from SheepMasterOneToOne import settings from django.conf.urls.static import staticurlpatterns [path("admin/", admin.site.urls),path(generate_report/export/, ReportAdmin(models.Report, admin.site).generat…...
【漫话机器学习系列】084.偏差和方差的权衡(Bias-Variance Tradeoff)
偏差和方差的权衡(Bias-Variance Tradeoff) 1. 引言 在机器学习模型的训练过程中,我们常常面临一个重要的挑战:如何平衡 偏差(Bias) 和 方差(Variance),以提升模型的泛…...

deepseek本地部署-linux
1、官网推荐安装方法(使用脚本,我绕不过github,未采用) 登录ollama下载网站https://ollama.com/download/linux,linux下有下载脚本。 正常来说,在OS系统下直接执行脚本即可。 2、手动安装方法 2.1获取ol…...

解决使用python提取word文档中所有的图片时图片丢失的问题
python解析word文档,提取文档中所有的图片并保存,并将原图位置用占位符替换。 问题描述 利用python-dox库解析word文档,并提取里面的所有图片时发现会出现一摸一样的图片只解析一次,导致图片丢失,数量不对的情况。 …...

【Spring相关知识】Spring应用如何优雅使用消息队列
文章目录 概述**核心概念****使用场景****快速入门**1. 添加依赖2. 配置 Binder3. 定义消息通道4. 发送和接收消息5. 运行应用 **高级特性****优点****适用场景** 概述 Spring Cloud Stream 是一个用于构建消息驱动微服务的框架,它基于 Spring Boot 和 Spring Inte…...

人工智能:从概念到未来
人工智能:从概念到未来 一、引言 在当今数字化时代,人工智能(Artificial Intelligence,AI)已从科幻小说和电影中的幻想逐渐走进现实,成为推动社会进步和经济发展的关键力量。它正在深刻地改变着我们的生活…...

CUDA Graph
cudaGraphLaunch 是 NVIDIA CUDA API 中的一个函数,用于在 CUDA Graphs 中启动一个已实例化的图。 CUDA Graphs 简介 CUDA Graphs 是 NVIDIA CUDA 编程模型中的一种技术,旨在优化 GPU 程序的性能。它允许将一系列连续的 GPU 操作(如计算和数…...

1343. 大小为 K 且平均值大于等于阈值的子数组数目
目录 一、题目二、思路2.1 解题思路2.2 代码尝试2.3 疑难问题 三、解法四、收获4.1 心得4.2 举一反三 一、题目 二、思路 2.1 解题思路 在遍历时维护一个统计的变量,用来统计满足条件的子数组个数 2.2 代码尝试 class Solution { public:int numOfSubarrays(vec…...

IDEA+DeepSeek让Java开发起飞
1.获取DeepSeek秘钥 登录DeepSeek官网 : https://www.deepseek.com/ 进入API开放平台,第一次需要注册一个账号 进去之后需要创建一个API KEY,然后把APIkey记录保存下来 接着我们获取DeepSeek的API对话接口地址,点击左边的:接口…...

C# winforms 使用菜单和右键菜单
初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 源码指引:github源…...

IDEA编写SpringBoot项目时使用Lombok报错“找不到符号”的原因和解决
目录 概述|背景 报错解析 解决方法 IDEA配置解决 Pom配置插件解决 概述|背景 报错发生背景:在SpringBoot项目中引入Lombok依赖并使用后出现"找不到符号"的问题。 本文讨论在上述背景下发生的报错原因和解决办法,如果仅为了解决BUG不论原…...

C基础寒假练习(6)
一、终端输入行数,打印倒金字塔 #include <stdio.h> int main() {int rows;printf("请输入倒金字塔的行数: ");scanf("%d", &rows);for (int i rows; i > 0; i--) {// 打印空格for (int j 0; j < rows - i; j) {printf(&qu…...

【论文翻译】DeepSeek-V3论文翻译——DeepSeek-V3 Technical Report——第一部分:引言与模型架构
论文原文链接:DeepSeek-V3/DeepSeek_V3.pdf at main deepseek-ai/DeepSeek-V3 GitHub 特别声明,本文不做任何商业用途,仅作为个人学习相关论文的翻译记录。本文对原文内容直译,一切以论文原文内容为准,对原文作者表示…...

【docker】Failed to allocate manager object, freezing:兼容兼容 cgroup v1 和 v2
参考大神让系统同时兼容 cgroup v1 和 v2 要解决你系统中只挂载了 cgroup v2 但需要兼容 cgroup v1 的问题,可以通过以下几步来使系统同时兼容 cgroup v1 和 cgroup v2。这样 Docker 和其他服务就可以正常工作了。步骤 1:更新 Grub 配置,启用兼容模式 编辑 GRUB 配置来启用同…...

我使用deepseek高效学习-分析外文网站Cron定时执行任务
最近在spring框架中 设置定时任务,有的末尾是星号有的是问号,有的是6位,有的是7位。就这个机会总结下cron表达式的使用,综合源代码中的crontab地址翻译分析,结合最近超爆的deepseek 提高学习效率,归纳总结出…...