【OpenGL ES】EGL+FBO离屏渲染
1 前言
FBO离屏渲染 中使用 GLSurfaceView 来驱动 Renderer 渲染图片,为了隐藏 GLSurfaceView,将其设置为透明的,并且宽高都设置为1。本文将使用 EGL 代替 GLSurfaceView 生成 OpenGL ES 的渲染环境,实现离屏渲染,将渲染后的图片显示在 ImageView 上。
EGL 为 OpenGL ES 提供了绘制表面(或渲染画布),是 OpenGL ES 与显示设备的桥梁,让 OpenGL ES 绘制的内容能够在呈现当前设备上。
EGL 环境创建分为以下5步:
1)创建EGLDisplay
EGLDisplay mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
int[] versions = new int[2];
EGL14.eglInitialize(mEGLDisplay, versions,0, versions, 1);
2)创建EGLConfig
int[] mEGLConfigAttrs = {
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_DEPTH_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] configNum = new int[1];
EGL14.eglChooseConfig(mEGLDisplay, mEGLConfigAttrs, 0, configs, 0,1, configNum, 0);
EGLConfig mEGLConfig = configs[0];
3)创建EGLContext
int[] mEGLContextAttrs = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};
EGLContext mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mEGLConfig, EGL14.EGL_NO_CONTEXT, mEGLContextAttrs, 0);
4)创建EGLSurface
int[] eglSurfaceAttrs = {EGL14.EGL_WIDTH, mWidth, EGL14.EGL_HEIGHT, mHeight, EGL14.EGL_NONE};
EGLSurface mEGLSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, eglSurfaceAttrs, 0);
5)绑定EGLSurface和EGLContext到显示设备(EGLDisplay)
EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
读者如果对 OpenGL ES 不太熟悉,请回顾以下内容:
本文完整代码资源见→EGL+FBO离屏渲染
项目目录如下:
2 案例
本案例实现了将彩色图片转换为灰色,并且使用 ImageView 显示转换后的图片。
MainActivity.java
package com.zhyan8.egl.activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import com.zhyan8.egl.R;
import com.zhyan8.egl.model.Model;
import com.zhyan8.egl.opengl.MyEGLSurface;
import com.zhyan8.egl.opengl.MyRender;
public class MainActivity extends AppCompatActivity implements Model.Callback {
private ImageView mImageView;
private MyEGLSurface mEGlSurface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = findViewById(R.id.imageView);
initEGLSurface();
mEGlSurface.requestRender();
}
private void initEGLSurface() {
mEGlSurface = new MyEGLSurface(this);
MyRender render = new MyRender(getResources());
render.setCallback(this);
mEGlSurface.init(render);
}
@Override
public void onCall(final Bitmap bitmap) {
runOnUiThread(() -> {
mImageView.setImageBitmap(bitmap);
});
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#556688">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"/>
</FrameLayout>
BaseEGLSurface.java
package com.zhyan8.egl.opengl;
import android.content.Context;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.util.DisplayMetrics;
import android.view.WindowManager;
public class BaseEGLSurface {
protected EGLDisplay mEGLDisplay;
protected EGLConfig mEGLConfig;
protected EGLContext mEGLContext;
protected EGLSurface mEGLSurface;
protected Context mContext;
protected Renderer mRenderer;
protected EglStatus mEglStatus = EglStatus.INVALID;
protected int mWidth;
protected int mHeight;
public BaseEGLSurface(Context context) {
mContext = context;
WindowManager mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getRealMetrics(displayMetrics);
mWidth = displayMetrics.widthPixels;
mHeight = displayMetrics.heightPixels;
}
public BaseEGLSurface(Context context, int width, int height) {
mContext = context;
mWidth = width;
mHeight = height;
}
// 设置渲染器
public void setRenderer(Renderer renderer) {
mRenderer = renderer;
}
// EGLDisplay宽高发生变化
public void onSurfaceChanged(int width, int height) {
mWidth = width;
mHeight = height;
EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
createSurface();
mEglStatus = EglStatus.CREATED;
}
// 请求渲染
public void requestRender() {
if (mEglStatus == mEglStatus.INVALID) {
return;
}
if (mEglStatus == EglStatus.INITIALIZED) {
mRenderer.onSurfaceCreated();
mRenderer.onSurfaceChanged(mWidth, mHeight);
mEglStatus = EglStatus.CREATED;
}
if (mEglStatus == EglStatus.CREATED || mEglStatus == EglStatus.DRAW) {
mRenderer.onDrawFrame();
mEglStatus = EglStatus.DRAW;
}
}
// 创建EGL环境
public void createEGLEnv() {
createDisplay();
createConfig();
createContext();
createSurface();
makeCurrent();
}
// 销毁EGL环境
public void destroyEGLEnv() {
// 与显示设备解绑
EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
// 销毁 EGLSurface
EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
// 销毁EGLContext
EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
// 销毁EGLDisplay(显示设备)
EGL14.eglTerminate(mEGLDisplay);
mEGLContext = null;
mEGLSurface = null;
mEGLDisplay = null;
}
// 1.创建EGLDisplay
private void createDisplay() {
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
int[] versions = new int[2];
EGL14.eglInitialize(mEGLDisplay, versions,0, versions, 1);
}
// 2.创建EGLConfig
private void createConfig() {
EGLConfig[] configs = new EGLConfig[1];
int[] configNum = new int[1];
EGL14.eglChooseConfig(mEGLDisplay, mEGLConfigAttrs, 0, configs, 0,1, configNum, 0);
if (configNum[0] > 0) {
mEGLConfig = configs[0];
}
}
// 3.创建EGLContext
private void createContext() {
if (mEGLConfig != null) {
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mEGLConfig, EGL14.EGL_NO_CONTEXT, mEGLContextAttrs, 0);
}
}
// 4.创建EGLSurface
private void createSurface() {
if (mEGLContext != null && mEGLContext != EGL14.EGL_NO_CONTEXT) {
int[] eglSurfaceAttrs = {EGL14.EGL_WIDTH, mWidth, EGL14.EGL_HEIGHT, mHeight, EGL14.EGL_NONE};
mEGLSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, eglSurfaceAttrs, 0);
}
}
// 5.绑定EGLSurface和EGLContext到显示设备(EGLDisplay)
private void makeCurrent() {
if (mEGLSurface != null && mEGLSurface != EGL14.EGL_NO_SURFACE) {
EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
mEglStatus = EglStatus.INITIALIZED;
}
}
// EGLConfig参数
private int[] mEGLConfigAttrs = {
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_DEPTH_SIZE, 8,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_NONE
};
// EGLContext参数
private int[] mEGLContextAttrs = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};
// EGL状态
enum EglStatus {
INVALID, INITIALIZED, CREATED, DRAW
}
// 渲染器接口
interface Renderer {
void onSurfaceCreated();
void onSurfaceChanged(int width, int height);
void onDrawFrame();
}
}
MyEGLSurface.java
package com.zhyan8.egl.opengl;
import android.content.Context;
public class MyEGLSurface extends BaseEGLSurface {
public MyEGLSurface(Context context) {
super(context);
}
public MyEGLSurface(Context context, int width, int height) {
super(context, width, height);
}
public void init(Renderer renderer) {
setRenderer(renderer);
createEGLEnv();
}
}
MyRender.java
package com.zhyan8.egl.opengl;
import android.content.res.Resources;
import android.opengl.GLES30;
import com.zhyan8.egl.model.Model;
public class MyRender implements BaseEGLSurface.Renderer {
private Model mModel;
public MyRender(Resources resources) {
mModel = new Model(resources);
}
@Override
public void onSurfaceCreated() {
//设置背景颜色
GLES30.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
//启动深度测试
GLES30.glEnable(GLES30.GL_DEPTH_TEST);
//创建程序id
mModel.onModelCreate();
}
@Override
public void onSurfaceChanged(int width, int height) {
mModel.onModelChange(width, height);
}
@Override
public void onDrawFrame() {
GLES30.glClearColor(0.5f, 0.7f, 0.3f, 1.0f);
// 将颜色缓存区设置为预设的颜色
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);
// 启用顶点的数组句柄
GLES30.glEnableVertexAttribArray(0);
GLES30.glEnableVertexAttribArray(1);
// 绘制模型
mModel.onModelDraw();
// 禁止顶点数组句柄
GLES30.glDisableVertexAttribArray(0);
GLES30.glDisableVertexAttribArray(1);
}
public void setCallback(Model.Callback callback) {
mModel.setCallback(callback);
}
}
Model.java
package com.zhyan8.egl.model;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.opengl.GLES30;
import com.zhyan8.egl.R;
import com.zhyan8.egl.utils.ArraysUtils;
import com.zhyan8.egl.utils.ShaderUtils;
import com.zhyan8.egl.utils.TextureUtils;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
public class Model {
private static final int TEXTURE_DIMENSION = 2; // 纹理坐标维度
private static final int VERTEX_DIMENSION = 3; // 顶点坐标维度
private Callback mCallback;
private Resources mResources;
private float mVertex[] = {-1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 0.0f};
private float[] mFboTexture = {0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f};
protected FloatBuffer mVertexBuffer;
protected FloatBuffer mFboTextureBuffer;
// 帧缓冲对象 - 颜色、深度、模板附着点,纹理对象可以连接到帧缓冲区对象的颜色附着点
private int[] mFrameBufferId = new int[1];
private int[] mTextureId = new int[2];
private int mProgramId;
private Point mBitmapSize = new Point();
public Model(Resources resources) {
mResources = resources;
mVertexBuffer = ArraysUtils.getFloatBuffer(mVertex);
mFboTextureBuffer = ArraysUtils.getFloatBuffer(mFboTexture);
}
// 模型创建
public void onModelCreate() {
mProgramId = ShaderUtils.createProgram(mResources, R.raw.vertex_shader, R.raw.fragment_shader);
TextureUtils.loadTexture(mResources, R.raw.xxx, mBitmapSize, mTextureId, mFrameBufferId);
}
// 模型参数变化
public void onModelChange(int width, int height) {
GLES30.glViewport(0, 0, mBitmapSize.x, mBitmapSize.y);
}
// 模型绘制
public void onModelDraw() {
GLES30.glUseProgram(mProgramId);
// 准备顶点坐标和纹理坐标
GLES30.glVertexAttribPointer(0, VERTEX_DIMENSION, GLES30.GL_FLOAT, false, 0, mVertexBuffer);
GLES30.glVertexAttribPointer(1, TEXTURE_DIMENSION, GLES30.GL_FLOAT, false, 0, mFboTextureBuffer);
// 激活纹理
GLES30.glActiveTexture(GLES30.GL_TEXTURE);
// 绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureId[0]);
// 绑定缓存
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFrameBufferId[0]);
// 绘制贴图
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
showBitmap();
}
private void showBitmap() {
// 分配字节缓区大小, 一个像素4个字节
ByteBuffer byteBuffer = ByteBuffer.allocate(mBitmapSize.x * mBitmapSize.y * Integer.BYTES);
GLES30.glReadPixels(0, 0, mBitmapSize.x, mBitmapSize.y, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, byteBuffer);
Bitmap bitmap = Bitmap.createBitmap(mBitmapSize.x, mBitmapSize.y, Bitmap.Config.ARGB_8888);
// 从缓存区读二进制缓冲数据
bitmap.copyPixelsFromBuffer(byteBuffer);
// 回调
mCallback.onCall(bitmap);
}
public void setCallback(Callback callback) {
mCallback = callback;
}
public interface Callback{
void onCall(Bitmap bitmap);
}
}
ShaderUtils.java
package com.zhyan8.egl.utils;
import android.content.res.Resources;
import android.opengl.GLES30;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class ShaderUtils {
//创建程序id
public static int createProgram(Resources resources, int vertexShaderResId, int fragmentShaderResId) {
final int vertexShaderId = compileShader(resources, GLES30.GL_VERTEX_SHADER, vertexShaderResId);
final int fragmentShaderId = compileShader(resources, GLES30.GL_FRAGMENT_SHADER, fragmentShaderResId);
return linkProgram(vertexShaderId, fragmentShaderId);
}
//通过外部资源编译着色器
private static int compileShader(Resources resources, int type, int shaderId){
String shaderCode = readShaderFromResource(resources, shaderId);
return compileShader(type, shaderCode);
}
//通过代码片段编译着色器
private static int compileShader(int type, String shaderCode){
int shader = GLES30.glCreateShader(type);
GLES30.glShaderSource(shader, shaderCode);
GLES30.glCompileShader(shader);
return shader;
}
//链接到着色器
private static int linkProgram(int vertexShaderId, int fragmentShaderId) {
final int programId = GLES30.glCreateProgram();
//将顶点着色器加入到程序
GLES30.glAttachShader(programId, vertexShaderId);
//将片元着色器加入到程序
GLES30.glAttachShader(programId, fragmentShaderId);
//链接着色器程序
GLES30.glLinkProgram(programId);
return programId;
}
//从shader文件读出字符串
private static String readShaderFromResource(Resources resources, int shaderId) {
InputStream is = resources.openRawResource(shaderId);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
StringBuilder sb = new StringBuilder();
try {
while ((line = br.readLine()) != null) {
sb.append(line);
sb.append("\n");
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
}
TextureUtils.java
package com.zhyan8.egl.utils;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.opengl.GLES30;
import android.opengl.GLUtils;
public class TextureUtils {
// 加载纹理贴图
public static void loadTexture(Resources resources, int resourceId, Point bitmapSize, int[] textureId, int[] frameBufferId) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
Bitmap bitmap = BitmapFactory.decodeResource(resources, resourceId, options);
bitmapSize.set(bitmap.getWidth(), bitmap.getHeight());
// 生成纹理id
GLES30.glGenTextures(2, textureId, 0);
for (int i = 0; i < 2; i++) {
// 绑定纹理到OpenGL
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId[i]);
GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST);
GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
if (i == 0) {
// 第一个纹理对象给渲染管线(加载bitmap到纹理中)
GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, bitmap, 0);
} else {
// 第二个纹理对象给帧缓冲区
GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, bitmap.getWidth(), bitmap.getHeight(),
0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null);
}
// 取消绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE);
}
// 创建帧缓存id
GLES30.glGenFramebuffers(1, frameBufferId, 0);
// 绑定帧缓存
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, frameBufferId[0]);
// 将第二个纹理附着在帧缓存的颜色附着点上
GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, textureId[1], 0);
// 取消绑定帧缓存
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE);
}
}
ArraysUtils.java
package com.zhyan8.egl.utils;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
public class ArraysUtils {
public static FloatBuffer getFloatBuffer(float[] floatArr) {
FloatBuffer fb = ByteBuffer.allocateDirect(floatArr.length * Float.BYTES)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
fb.put(floatArr);
fb.position(0);
return fb;
}
}
vertex_shader.glsl
#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec2 aTextureCoord;
out vec2 vTexCoord;
void main() {
gl_Position = vPosition;
vTexCoord = aTextureCoord;
}
fragment_shader.glsl
#version 300 es
precision mediump float;
uniform sampler2D uTextureUnit;
in vec2 vTexCoord;
out vec4 fragColor;
void main() {
vec4 color = texture(uTextureUnit, vTexCoord);
float rgb = color.g;
vec4 c = vec4(rgb, rgb, rgb, color.a);
fragColor = c;
}
3 运行效果
原图:
处理后:
声明:本文转自【OpenGL ES】EGL+FBO离屏渲染
【OpenGL ES】EGL+FBO离屏渲染的更多相关文章
- IOS 中openGL使用教程4(openGL ES 入门篇 | 离屏渲染)
通常情况下,我们使用openGL将渲染好的图片绘制到屏幕上,但有时候我们不想显示处理结果,这时候就需要使用离屏渲染了. 正常情况下,我们将屏幕,也就是一个CAEAGLLayer对象作为渲染目标,离屏渲 ...
- Opengl ES之FBO
FBO介绍 FBO帧缓冲对象,它的主要作用一般就是用作离屏渲染,例如做Camera相机图像采集进行后期处理时就可能会用到FBO.假如相机出图的是OES纹理,为了方便后期处理, 一般先将OES纹理通过F ...
- 一步步实现windows版ijkplayer系列文章之六——SDL2源码分析之OpenGL ES在windows上的渲染过程
一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...
- NDK OpenGLES3.0 开发(五):FBO 离屏渲染
什么是 FBOFBO(Frame Buffer Object)即帧缓冲区对象,实际上是一个可添加缓冲区的容器,可以为其添加纹理或渲染缓冲区对象(RBO). FBO 本身不能用于渲染,只有添加了纹理或者 ...
- OpenGL ES 3.0之Fragment buffer objects(FBO)详解 (转)
http://www.cnblogs.com/salam/p/4957250.html 片段操作图 这篇文章将介绍从写入帧缓冲和读取帧缓冲的方式. Buffers(缓冲) OpenGL ES支持三种缓 ...
- Opengl ES之YUV数据渲染
YUV回顾 记得在音视频基础知识介绍中,笔者专门介绍过YUV的相关知识,可以参考: <音视频基础知识-YUV图像> YUV数据量相比RGB较小,因此YUV适用于传输,但是YUV图不能直接用 ...
- OpenGL ES 3.0之Fragment buffer objects(FBO)详解(一)
片段操作图 这篇文章将介绍从写入帧缓冲和读取帧缓冲的方式. Buffers(缓冲) OpenGL ES支持三种缓冲: OpenGL ES •• Color buffer颜色缓冲 •• Depth bu ...
- 【Android应用开发】 OpenGL ES -- 透视投影 和 正交投影
博客地址 : http://blog.csdn.net/shulianghan/article/details/46680803 源代码下载 : http://download.csdn.net/de ...
- Opengl ES之踩坑记
前因 最近在尝试使用Opengl ES实现一些LUT滤镜效果,在实现这些滤镜效果的时候遇到一些兼容性的坑,踩过这些坑后我希望把这几个坑分享给读者朋友们, 希望同在学习Opengl ES的朋友们能少走弯 ...
- Opengl ES之矩阵变换(上)
前言 说到矩阵变换,我们第一时间想到的就是大学时代的线性代数这些复杂的东西,突然有了一种令人从入门到放弃的念头,不慌,作为了一个应用层的CV工程师, 在实际应用中线性代数哪些复杂的计算根本不用我们自己 ...
随机推荐
- CSS 动画 : 3D翻页动画
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- MyBatis04——使用注解开发
使用注解开发 MyBatis3提供了新的基于注解的配置,但是MyBatis映射并不能用注解来构建. sql类型主要分成: @select @update @insert @delete 注意:利用注解 ...
- blender 3D 汽车模型下载
前言 因为之前需要模拟摄像头拍摄汽车车牌,从各种途径下载了一些blender 3D 汽车模型,现将模型分享出来,如有需要,直接去最后面下载地址下载即可 目前已经车型40多款 车型图 70年代Porsc ...
- [转帖]鲲鹏性能优化十板斧——鲲鹏处理器NUMA简介与性能调优五步法
https://www.cnblogs.com/huaweicloud/p/12166354.html 1.1 鲲鹏处理器NUMA简介 随着现代社会信息化.智能化的飞速发展,越来越多的设备接入互联网. ...
- [转帖]Intel“革命性”X86s架构,带来哪些颠覆及影响?
https://www.eet-china.com/mp/a221822.html 英特尔发布了一份新的白皮书(Intel X86-S扩展架构规格),计划简化其处理器指令集架构(ISA).英特尔提供了 ...
- [转帖]Web性能优化工具WebPageTest(一)——总览与配置
https://www.cnblogs.com/strick/p/6677836.html 网站性能优化工具大致分为两类:综合类和RUM类(实时监控用户类),WebPageTest属于综合类. Web ...
- [转帖]xfs_repair命令详解
https://bbs.qunyingkeji.com/2052/ 1.现状 目前网上出现大量的主机输入输出错误,原因是由于主机文件系统损坏.一线人员大部分采用的是umont 和 mount的方式恢复 ...
- [转帖]使用SkyWalking监控nginx (以openresty为例)
https://www.cnblogs.com/hahaha111122222/p/15829737.html 安装使用SkyWalking先看这篇文章,地址:https://www.cnblogs. ...
- [转帖]「Linux性能调优」磁盘I/O队列调度策略
https://zhuanlan.zhihu.com/p/450329513 傻瓜化说明 简单地说,对于磁盘I/O,Linux提供了cfq, deadline和noop三种调度策略 cfq: 这个名字 ...
- Linux下面rsync 实现 完全一致的同步方法
1. 在某些特殊的linux机器上面, 比如龙芯后者是飞腾服务器,部分工具不太好用, 需要使用x86弄好之后进行同步过去, 这个时候scp 最简单但是网络流量非常大, 不如使用rsync, rsync ...