??????Android dump渲染和合成圖層GraphicBuffer指南
引言
??博客停更很久了,提起筆來漸感生疏啊!看來,還是得抽出時間來更新更新啊!好了,感慨也發完了,是時候切入正題了,本篇博客將主要詳細介紹如何dump Android渲染和合成圖層GraphicBuffer,并通過YUV軟體查看流程!通過本篇博客,讀者將會至少學會如下兩點:
- 通過dump Android渲染圖層GraphicBuffer,查看Android渲染結果是否正確
- 通過dump Android合成圖層GraphicBuffer,查看Android合成結果是否正確
注意這里的合成指的是GPU(Client)合成
好了不多說了,直接開干!
能搜尋到這篇博客的,肯定是對Android graphci有一定掌握的同仁嗎,所以這里就不會過多解釋一些名詞和代碼邏輯了,這是一篇專業性比較強的文章!
注意:本篇的介紹是基于Android 11?平臺為基礎的(其中Q的版本差異也不是很大),其中涉及的代碼路徑如下:
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
frameworks/native/libs/renderengine/gl/GLESRenderEngine.cpp
frameworks/native/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
frameworks/base/cmds/screencap
frameworks/av/cmds/screenrecord
一.通過Android內置命令dump Android合成圖層GraphicBuffer
??在正式開始分析我們如何自行添加dump相關邏輯代碼,dump渲染和合成圖層GraphicBuffer之前,這里我簡單介紹下如何使用Android內置的cmd命令,進行相關的dump邏輯,這里的dump邏輯僅僅只能dump GPU合成圖層GraphicBuffer.
1.1 Screencap dump一幀GPU合成圖層GraphicBuffer
??screencap的命令格式如下,它將當前當前Android顯示的圖層以GPU合成的模式,并通過png格式保存下來,該命令的使用方法如下:
130|XXX:/ # screencap --help
screencap: invalid option -- -
usage: screencap [-hp] [-d display-id] [FILENAME]
-h: this message
-p: save the file as a png.
-d: specify the physical display ID to capture (default: 0)
see "dumpsys SurfaceFlinger --display-id" for valid display IDs.
If FILENAME ends with .png it will be saved as a png.
If FILENAME is not given, the results will be printed to stdout.
130|XXX:/ #screencap -p /sdcard/screencap.png
關于screencap的具體實作邏輯就不過多介紹,感興趣的可以frameworks/base/cmds/screencap查看相關的原始碼邏輯!
1.2 Screenrecord dump一幀或多幀GPU合成圖層GraphicBuffer
??screenrecord的命令格式如下,它將當前當前Android顯示的圖層以GPU合成的模式,并通過多種格式保存下來,該命令的使用方法如下:
1|XXX:/ # screenrecord --help
Usage: screenrecord [options] <filename>
Android screenrecord v1.3. Records the device's display to a .mp4 file.
Options:
--size WIDTHxHEIGHT
Set the video size, e.g. "1280x720". Default is the device's main
display resolution (if supported), 1280x720 if not. For best results,
use a size supported by the AVC encoder.
--bit-rate RATE
Set the video bit rate, in bits per second. Value may be specified as
bits or megabits, e.g. '4000000' is equivalent to '4M'. Default 20Mbps.
--bugreport
Add additional information, such as a timestamp overlay, that is helpful
in videos captured to illustrate bugs.
--time-limit TIME
Set the maximum recording time, in seconds. Default / maximum is 180.
--display-id ID
specify the physical display ID to record. Default is the primary display.
see "dumpsys SurfaceFlinger --display-id" for valid display IDs.
--verbose
Display interesting information on stdout.
--help
Show this message.
Recording continues until Ctrl-C is hit or the time limit is reached.
1|XXX:/ #screenrecord --verbose --time-limit 10 --output-format raw-frames /sdcard/raw-frames.frames
//說明:錄制螢屏,錄制時間為10s,格式為裸BGR(FORMAT_FRAMES),不添加任何資訊
1|XXX:/ #screenrecord --verbose --time-limit 10 --output-format frames /sdcard/frames.frames
//說明:錄制螢屏,錄制時間為10s,格式為裸BGR(FORMAT_FRAMES),只是每幀前面會加上用于描述幀資訊的20位元組頭,
1|XXX:/ #screenrecord --verbose --time-limit 30 --output-format h264 /sdcard/demo.h264
//說明:錄制螢屏,錄制時間為30s,格式為h264(FORMAT_H264),
1|XXX:/ #screenrecord /sdcard/demo.mp4
//說明:錄制螢屏,錄制時間為默認的180s,格式為MP4(FORMAT_MP4),
關于screenrecord的具體實作邏輯就不過多介紹,感興趣的可以frameworks/base/cmds/screencap查看相關的原始碼邏輯!
二.自定義邏輯dump Android渲染和合成圖層GraphicBuffer指南
??通過前面的章節,我們簡單介紹了如何使用Android內置的cmds命令dump GPU合成圖層的GraphicBuffer,本章節我們重點介紹如何自定義dump Android渲染圖層和GPU合成圖層,
2.1dump Android渲染圖層GraphicBuffer
??這塊我們可以在GLESRenderEngine.cpp的如下方法中添加相關的邏輯,如下:
//frameworks/native/libs/renderengine/gl/GLESRenderEngine.cpp
static void dump_content_of_layers_to_file(const sp<GraphicBuffer>& target)
{
ALOGE("dump_content_of_layers_to_file");
// ALOGE("dump_content_of_layers_to_file");
// ALOGE("dump_content_of_layers_to_file");
int result = -1;
void *addr = NULL;
static int DumpSurfaceCount = 0;
int32_t bufStride;
FILE * pfile = NULL;
char layername[100] ;
memset(layername,0,sizeof(layername));
uint32_t w, s, h, f;
w = target->getWidth();
h = target->getHeight();
s = target->getStride();
f = target->getPixelFormat();
android_dataspace d;
uint32_t buffer_size = 0;
d = HAL_DATASPACE_UNKNOWN;
buffer_size = s * h * bytesPerPixel(f);
bufStride = bytesPerPixel(f);
ALOGE("buffer_layer info w:%d h:%d s:%d f:%d d:%d size:%d", w, h, s, f, d, buffer_size);
sprintf(layername,
"/data/dump/buffer_layer_%d_frame_%d_%d_%d.bin",
DumpSurfaceCount,
w,
h,
bufStride);
ALOGD("The dump file info : %s", layername);
DumpSurfaceCount ++;
pfile = fopen(layername,"w+");
if(pfile)
{
//獲取FrameBufferSurface對應的ion地址
result = target->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &addr);
if(addr != NULL){
ALOGE("The addr : %p", addr);
int result = -1;
// system("mkdir /data/dump && chmod 777 /data/dump");
result = fwrite( (const void *)addr,
(size_t)( (buffer_size)),
1,
pfile);
if(result >0){
ALOGD("fwrite success!");
}else{
ALOGE("fwrite failed error %d", result);
}
}else{
ALOGE("lock buffer error!");
}
fclose(pfile);
target->unlock();
}
}
status_t GLESRenderEngine::drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
ANativeWindowBuffer* const buffer,
const bool useFramebufferCache, base::unique_fd&& bufferFence,
base::unique_fd* drawFence) {
ATRACE_CALL();
if (layers.empty()) {
ALOGV("Drawing empty layer stack");
return NO_ERROR;
}
if (bufferFence.get() >= 0) {
// Duplicate the fence for passing to waitFence.
base::unique_fd bufferFenceDup(dup(bufferFence.get()));
if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) {
ATRACE_NAME("Waiting before draw");
sync_wait(bufferFence.get(), -1);
}
}
if (buffer == nullptr) {
ALOGE("No output buffer provided. Aborting GPU composition.");
return BAD_VALUE;
}
//dump layers
char pro_value[PROPERTY_VALUE_MAX];
property_get("buffer.dump",pro_value,0);
if(!strcmp(pro_value,"true"))
{
ALOGD("dump_content_of_layers_to_file!");
//dump_content_of_layers_to_file(mCurrentBuffer);
//int layer_order = 0;
for (auto const layer : layers) {
if (layer->source.buffer.buffer != nullptr) {
sp<GraphicBuffer> gBuf = layer->source.buffer.buffer;
dump_content_of_layers_to_file(gBuf);
//layer_order++;
}
}
}
...
}
當然上述僅僅是提供了一種思路,具體的上述原始碼邏輯用在什么地方,讀者可以根據自己的需要自行調整,這里我們簡單測驗一下,看看生成的dump檔案如下:
XXX:/data/dump # setprop buffer.dump true
XXX:/data/dump # ls
buffer_layer_0_frame_2880_2560_4.bin buffer_layer_3_frame_1920_56_4.bin buffer_layer_6_frame_1920_24_4.bin
buffer_layer_1_frame_1920_1080_4.bin buffer_layer_4_frame_2880_2560_4.bin buffer_layer_7_frame_1920_56_4.bin
buffer_layer_2_frame_1920_24_4.bin buffer_layer_5_frame_1920_1080_4.bin buffer_layer_8_frame_22_28_4.bin
2.2 dump Android GPU合成圖層GraphicBuffer
??這塊我們可以在FramebufferSurface.cpp的如下方法中添加相關的邏輯,如下:
//frameworks/native/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
void dump_content_of_layers_to_file(const sp<GraphicBuffer>& target)
{
ALOGE("dump_content_of_layers_to_file");
ALOGE("dump_content_of_layers_to_file");
ALOGE("dump_content_of_layers_to_file");
int result = -1;
void *addr = NULL;
static int DumpSurfaceCount = 0;
int32_t bufStride;
FILE * pfile = NULL;
char layername[100] ;
memset(layername,0,sizeof(layername));
uint32_t w, s, h, f;
w = target->getWidth();
h = target->getHeight();
s = target->getStride();
f = target->getPixelFormat();
android_dataspace d;
uint32_t buffer_size = 0;
d = HAL_DATASPACE_UNKNOWN;
buffer_size = s * h * bytesPerPixel(f);
bufStride = bytesPerPixel(f);
ALOGE("FrameBufferSurface info w:%d h:%d s:%d f:%d d:%d size:%d", w, h, s, f, d, buffer_size);
sprintf(layername,
"/data/dump/hwc_layer_%d_frame_%d_%d_%d.bin",
DumpSurfaceCount,
w,
h,
bufStride);
ALOGD("The dump file info : %s", layername);
DumpSurfaceCount ++;
pfile = fopen(layername,"w+");
if(pfile)
{
//獲取FrameBufferSurface對應的ion地址
result = target->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &addr);
if(addr != NULL){
ALOGE("The addr : %p", addr);
int result = -1;
system("mkdir /data/dump && chmod 777 /data/dump");
result = fwrite( (const void *)addr,
(size_t)( (buffer_size)),
1,
pfile);
if(result >0){
ALOGD("fwrite success!");
}else{
ALOGE("fwrite failed error %d", result);
}
}else{
ALOGE("lock buffer error!");
}
fclose(pfile);
target->unlock();
usleep(1000 * 5);//延時5毫秒
}
}
#define HWC_DUMP_LAYER 1
status_t FramebufferSurface::nextBuffer(uint32_t& outSlot,
sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence,
Dataspace& outDataspace) {
...
#if HWC_DUMP_LAYER
char pro_value[PROPERTY_VALUE_MAX];
property_get("hwc.dump",pro_value,0);
if(!strcmp(pro_value,"true"))
{
dump_content_of_layers_to_file(mCurrentBuffer);
}
#endif
status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace);
...
}
當然上述僅僅是提供了一種思路,具體的上述原始碼邏輯用在什么地方,讀者可以根據自己的需要自行調整,這里我們簡單測驗一下,看看生成的dump檔案如下:
XXX:/data/dump #setprop hwc.dump true
XXX:/data/dump # ls
hwc_layer_0_frame_1920_1080_4.bin hwc_layer_2_frame_1920_1080_4.bin hwc_layer_4_frame_1920_1080_4.bin hwc_layer_6_frame_1920_1080_4.bin
hwc_layer_1_frame_1920_1080_4.bin hwc_layer_3_frame_1920_1080_4.bin hwc_layer_5_frame_1920_1080_4.bin hwc_layer_7_frame_1920_1080_4.bin
三.Android dump渲染和合成圖層GraphicBuffer指南階段總結
??至此Android dump渲染和合成圖層GraphicBuffer階段就完成了,讀者是感到意猶未盡呢,還是想說一句尼瑪,瞎扯淡呢!
好了,Android dump渲染和合成圖層GraphicBuffer分析就告一段落了,各位青山不改綠水長流,各位江湖見!當然各位讀者的點贊和關注是我寫作路上前進的最大動力了,如果有啥不對或者不爽的也可以踩一踩也無妨!你們的鼓勵和批評是博主前進路上最大的動力,
各位讀友,千萬不要噴我,因為我這也是第一次深入到Android顯示這塊的原始碼邏輯,為啥我深入到了這塊,因為入職了一家原廠,所以我現在是菜鳥一杯,如果有對Android graphic剛興趣的朋友,也可以聯系我,一起學習進步!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/353443.html
標籤:其他
