Repository: CatOnly/CrashNote
Branch: master
Commit: abf19e36725c
Files: 25
Total size: 313.6 KB
Directory structure:
gitextract_ek_1s3xw/
├── .gitignore
├── ComputerGraphics(OpenGL)/
│ ├── EXT0_GLBuffers&MultiSample.md
│ ├── EXT1_FileFormat.md
│ ├── EXT2_HardwareSupport.md
│ ├── EXT3_Platform.md
│ ├── Part0_Context&Pipeline.md
│ ├── Part1_Light&ShadowInGame.md
│ ├── Part2_PhysicalLight.md
│ ├── Part3_Texture.md
│ ├── Part4_Animation.md
│ ├── Part5_Trick.md
│ └── README.md
├── DigitalImageProcessing/
│ ├── Part0_Signals&Systems.md
│ ├── Part1_Filtering.md
│ ├── Part2_Colors.md
│ ├── Part3_PhotoShop.md
│ └── README.md
├── LinearAlgebra/
│ ├── Part0_Base.md
│ ├── Part1_Matrix.md
│ ├── Part2_Quaternion.md
│ ├── Part3_Triangles.md
│ └── README.md
├── README.md
└── UnrealEngine4/
├── Part0_Base.md
└── README.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.DS_Store
*.key
*/images/*.log
UnrealEngine4/Part3_AnimationSystem.md
UnrealEngine4/Part4_LandscapeSystem.md
UnrealEngine4/Part5_ParticleSystem.md
================================================
FILE: ComputerGraphics(OpenGL)/EXT0_GLBuffers&MultiSample.md
================================================
# 一、顶点信息传输
## 1. 立即模式 glBegin()/glEnd()
方式:立即绘制
优点:功能适配范围广,写法直观
缺点:频繁调用 OpenGL 函数,效率低,共享点使用次数多
例子:
1. 直接提交 OpenGL 命令到 GPU
```c
// Note that not all of OpenGL commands can be placed in between glBegin() and glEnd()
// Only a subset of commands can be used
// glVertex*(), glColor*(), glNormal*(), glTexCoord*(), glMaterial*(), glCallList(), etc.
glBegin(GL_TRIANGLES);
glColor3f(1, 0, 0); // set vertex color to red
glVertex3fv(v1); // draw a triangle with v1, v2, v3
glVertex3fv(v2);
glVertex3fv(v3);
glEnd();
```
2. 将命令放到 DisplayList 后,批量一次传入到 GPU
DisplayList 会将其中命令的所有资源存储到自己的内存中
DisplayList 是服务端的状态,本身存储在 GPU 缓存中,只能存储与服务端有关的部分命令
DisplayList 的命令和数据一旦上传便不可修改
```c
// create one display list
GLuint index = glGenLists(1);
// compile the display list, store a triangle in it
// Option: GL_COMPILE or GL_COMPILE_AND_EXECUTE(render)
glNewList(index, GL_COMPILE);
glBegin(GL_TRIANGLES);
glVertex3fv(v0);
glVertex3fv(v1);
glVertex3fv(v2);
glEnd();
glEndList();
...
// draw the display list
glCallList(index);
...
// delete it if it is not used any more
glDeleteLists(index, 1);
```
## 2. VertexArray
方式:批量数据传入绘制
优点:数据以数组的形式**存储在应用缓存**,减少了 OpenGL 函数的频繁调用
缺点:每次绘制都要占用带宽上传到显存
例子:
```c
GLfloat vertices[] = {...}; // 36 of vertex coords
glUseProgram(progId);
// activate and specify pointer to vertex array
// 因为 vertices 存储在应用程序上,所以这里 enable client state
glEnableClientState(GL_VERTEX_ARRAY);
// 也可以用
// glNormalPointer、glColorPointer、glIndexPointer、glTexCoordPointer、glEdgeFlagPointer
glVertexPointer(3, GL_FLOAT, 0, vertices);
// draw a cube
// 也可以用 glDrawElements、glDrawRangeElements
glDrawArrays(GL_TRIANGLES, 0, 36);
// deactivate vertex arrays after drawing
glDisableClientState(GL_VERTEX_ARRAY);
```
## 3. VertexBuffer
方式:批量数据传入绘制
优点:
1. 数据以数组的形式**存储在显卡高速缓存**,每次使用时不用重新上传,只需要在显卡绑定即可
2. 数据由于存储在显存,可以被应用程序在不同线程访问和修改
例子:
1. 创建和销毁
```c
GLuint vboId; // ID of VBO
GLfloat* vertices = new GLfloat[vCount*3]; // create vertex array
// generate a new VBO and get the associated ID
glGenBuffers(1, &vboId);
// bind VBO in order to use
// Option: GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER
// This Option assists VBO to decide the most efficient locations of buffer objects
// For example, some systems may prefer indices in AGP or system memory, and vertices in video memory
// Once glBindBuffer() is first called, VBO initializes the buffer with a zero-sized memory buffer and set the initial VBO states, such as usage and access properties.
glBindBuffer(GL_ARRAY_BUFFER, vboId);
// upload data to VBO
// Option: glBufferSubData, GL_[STATIC/DYNAMIC/STREAM]_[DRAW/READ/COPY]
// GL_STATIC_DRAW 决定了数据的存储位置
// Static: 更新一次,使用多次
// Dynamic: 不断更新,使用多次
// Stream: 更新一次,最多使用几次
// Draw: application upload to GPU
// Read: GPU copy to application
// Copy: Draw and Read
glBufferData(GL_ARRAY_BUFFER, dataSize, vertices, GL_STATIC_DRAW);
// it is safe to delete after copying data to VBO
delete [] vertices;
// delete VBO when program terminated
glDeleteBuffers(1, &vboId);
```
2. 过去的使用方式:不同的 API 开启/关闭 不同的顶点属性
```c
glUseProgram(progId);
// bind VBOs for vertex array and index array
glBindBuffer(GL_ARRAY_BUFFER, vboId1); // for vertex attributes
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId2); // for indices
glEnableClientState(GL_VERTEX_ARRAY); // activate vertex position array
glEnableClientState(GL_NORMAL_ARRAY); // activate vertex normal array
glEnableClientState(GL_TEXTURE_COORD_ARRAY); // activate texture coord array
// do same as vertex array except pointer
glVertexPointer(3, GL_FLOAT, stride, offset1); // last param is offset, not ptr
glNormalPointer(GL_FLOAT, stride, offset2);
glTexCoordPointer(2, GL_FLOAT, stride, offset3);
// draw 6 faces using offset of index array
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0);
glDisableClientState(GL_VERTEX_ARRAY); // deactivate vertex position array
glDisableClientState(GL_NORMAL_ARRAY); // deactivate vertex normal array
glDisableClientState(GL_TEXTURE_COORD_ARRAY); // deactivate vertex tex coord array
// bind with 0, so, switch back to normal pointer operation
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
```
3. OpenGL 2.0 + 使用方式:同一个 API 开启/关闭 不同的顶点属性
```c
glUseProgram(progId);
// bind VBOs for vertex array and index array
glBindBuffer(GL_ARRAY_BUFFER, vboId1); // for vertex coordinates
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId2); // for indices
glEnableVertexAttribArray(attribVertex); // activate vertex position array
glEnableVertexAttribArray(attribNormal); // activate vertex normal array
glEnableVertexAttribArray(attribTexCoord); // activate texture coords array
// set vertex arrays with generic API
glVertexAttribPointer(attribVertex, 3, GL_FLOAT, false, stride, offset1);
glVertexAttribPointer(attribNormal, 3, GL_FLOAT, false, stride, offset2);
glVertexAttribPointer(attribTexCoord, 2, GL_FLOAT, false, stride, offset3);
// draw 6 faces using offset of index array
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, 0);
glDisableVertexAttribArray(attribVertex); // deactivate vertex position
glDisableVertexAttribArray(attribNormal); // deactivate vertex normal
glDisableVertexAttribArray(attribTexCoord); // deactivate texture coords
// bind with 0, so, switch back to normal pointer operation
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
```
4. 更新 VAO
```c
// 方法 1:重新向 GPU 上传数据(缺点:应用程序和 GPU 要存储 2 份数据,每次更新都要占用带宽)
glBufferData(GL_ARRAY_BUFFER, dataSize, vertices, GL_STATIC_DRAW);
// 方法 2:通过映射 GPU 上缓存数据地址到应用程序的缓存地址
// 将应用程序对地址的操作用于对 GPU 缓存操作,达到在应用程序控制 GPU 缓存的效果
// bind then map the VBO
glBindBuffer(GL_ARRAY_BUFFER, vboId);
// Option: GL_READ_ONLY, GL_WRITE_ONLY, GL_READ_WRITE
// 如果 GPU 正在使用这个 buffer,将会返回 NULL
float* ptr = (float*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
// if the pointer is valid(mapped), update VBO
if(ptr)
{
updateMyVBO(ptr, ...); // custom function modify buffer data
glUnmapBuffer(GL_ARRAY_BUFFER); // unmap it after use it's return GLboolean
}
```
# 二、Pixel Buffer
## 1. 创建和使用
Pixel Buffer Object 由 Vertex Buffer Object 扩展而来,因此对 Pixel Buffer 操作的许多细节和接口都与 Vertex Buffer 保持一致,这里不在赘述
例子:
```c
GLuint pboIds[2];
// Create
glGenBuffers(2, pboIds);
// Bind
// Option: GL_PIXEL_UNPACK_BUFFER / GL_PIXEL_PACK_BUFFER
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboIds[0]);
// load data rgba
glBufferData(GL_PIXEL_UNPACK_BUFFER, 720 * 1280 * 4, NULL, GL_STREAM_DRAW);
// unbind
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
...
// Note that glMapBuffer() causes sync issue
// If GPU is working with this buffer, glMapBuffer() will wait(stall)
// until GPU to finish its job. To avoid waiting (idle), you can call
// first glBufferData() with NULL pointer before glMapBuffer()
// If you do that, the previous data in PBO will be discarded and
// glMapBuffer() returns a new allocated pointer immediately
// even if GPU is still working with the previous data
glBufferData(GL_PIXEL_UNPACK_BUFFER, 720 * 1280 * 4, NULL, GL_STREAM_DRAW);
// Mapping PBO
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboIds[0]);
GLubyte* ptr = (GLubyte*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
if(ptr)
{
// Custom function: update data directly on the mapped buffer
updatePixels(ptr, DATA_SIZE);
// release pointer to mapping buffer
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
}
// Delete PBO
glDeleteBuffers(1, &pboIds);
```
## 2. PBO、FBO、Texture Object

**Pack(OpenGL to Application)**
**glReadPixels**
1. 从 frame buffer 中读取数据
2. 将 frame buffer 数据写入 Pixel buffer
**Unpack(Application to OpenGL)**
**glDrawPixels**
1. 从 Pixel buffer 读取数据
2. 将 Pixel buffer 数据写入 frame buffer
## 3. Direct Memory Access
将数据转换到 Pixel Buffer Object 很快是由于:转到 PBO 的数据将会直接进入显卡缓存中,不通过 CPU 的调度
例如在加载纹理时:
1. 不使用 PBO
在 CPU 的调度下,将图片资源加载到系统缓存,然后从系统缓存拷贝到 OpenGL 的纹理对象
2. 使用 PBO
直接加载到 OpenGL 里的 PBO 下,然后拷贝到纹理对象
整个过程由 GPU 完成,可以和 CPU 异步执行,效率提高

例子:
普通加载纹理的方式
```c
// Data is from CPU memory
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, data);
```
通过 PBO 加载纹理(PBO 创建时已经加载纹理数据)
```c
// bind the texture and PBO
glBindTexture(GL_TEXTURE_2D, textureId);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pboIds[index]);
// copy pixels from PBO to texture object so the last param is 0 not data
// Use offset instead of pointer
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, 0);
// it is good idea to release PBOs with ID 0 after use
// Once bound with 0, all pixel operations are back to normal ways
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
```
通过 PBO 加载/读取 Frame Buffer
```c
// set the target framebuffer to read
glReadBuffer(GL_FRONT);
// read pixels from framebuffer to PBO
// glReadPixels() should return immediately.
glBindBuffer(GL_PIXEL_PACK_BUFFER, pboIds[index]);
glReadPixels(0, 0, WIDTH, HEIGHT, GL_BGRA, GL_UNSIGNED_BYTE, 0);
```
# 三、Frame Buffer
> 定义:framebuffer 是 OpenGL 一系列数据存储的集合
**浮点帧缓冲 (Floating Point Framebuffer)**
当一个帧缓冲的颜色缓冲的内部格式被设定成了 `GL_RGB16F`, `GL_RGBA16F`, `GL_RGB32F` 或者 `GL_RGBA32F` 时,这些帧缓冲被叫做,浮点帧缓冲可以存储超过 0.0 到 1.0 范围的浮点值
当帧缓冲使用了一个标准化的定点格式(像 `GL_RGB` )为其颜色缓冲的内部格式,OpenGL 会在将这些值存入帧缓冲前自动将其约束到 0.0 到 1.0 之间
## 1. 不同种类的 framebuffer
1. **Default framebuffer**
本地窗口系统创建和使用的 framebuffer,一定会显示到屏幕上,是本地系统创建 Context 的一部分,当 `glBindFramebuffer(GL_FRAMEBUFFER, 0);` 时,绑定的就是当前窗口系统提供的 default framebuffer
这种 framebuffer 有本地窗口系统 API 创建提供,由 OpenGL 将其作为自己的输出给本地窗口系统来使用
包含:多个(至少一个)色彩缓冲、一个深度缓冲、一个模板缓冲、一个累积缓冲
2. **Frame Buffer Object**
OpenGL 创建和使用的 framebuffer,可以不显示到屏幕上
提供一个 FBO 对象来供 OpenGL 操作,FBO 对象可以有**多个(至少一个)色彩缓冲**,一个深度缓冲,一个模板缓冲(没有累积缓冲)
## 2. 内部数据对象
> framebuffer 提供的内部数据都以 attach 方式来赋予,并非内部创建
> 因此 framebuffer 内部数据的切换都要比单独切换 framebuffer 本身要快
切换 texture:**glFramebufferTexture2D**
1. attach 的 textureid 为 0,之前的纹理将会从 frame buffer 上解绑
2. 删除纹理时会自动从当前绑定的 framebuffer 上解绑,但如果当前纹理并不会从其他已 attach 的**非当前绑定的** framebuffer 解绑
切换 renderbuffer:**glFramebufferRenderbuffer**
1. renderbuffer 主要用来存储一些逻辑数据,而非图像数据
2. 可以通过 glGetRenderbufferParameteriv 来获取 renderbuffer 里数据的一些属性
```c
int width;
// Option:
// GL_RENDERBUFFER_WIDTH
// GL_RENDERBUFFER_HEIGHT
// GL_RENDERBUFFER_INTERNAL_FORMAT
// GL_RENDERBUFFER_RED_SIZE
// GL_RENDERBUFFER_GREEN_SIZE
// GL_RENDERBUFFER_BLUE_SIZE
// GL_RENDERBUFFER_ALPHA_SIZE
// GL_RENDERBUFFER_DEPTH_SIZE
// GL_RENDERBUFFER_STENCIL_SIZE
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
```
## 3. 创建和使用
例子:
```c
GLuint fboId;
// create a framebuffer object, you need to delete them when program exits.
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
// create a renderbuffer object to store depth info
// NOTE: A depth renderable image should be attached the FBO for depth test.
// If we don't attach a depth renderable image to the FBO, then
// the rendering output will be corrupted because of missing depth test.
// If you also need stencil test for your rendering, then you must
// attach additional image to the stencil attachement point, too.
glGenRenderbuffers(1, &rboDepthId);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepthId);
// allocat memory for renderbuffer
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, TEXTURE_WIDTH, TEXTURE_HEIGHT);
//glRenderbufferStorageMultisample(GL_RENDERBUFFER, fboSampleCount, GL_DEPTH_COMPONENT, TEXTURE_WIDTH, TEXTURE_HEIGHT);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// attach a texture to FBO color attachement point
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);
//glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);
// attach a renderbuffer to depth attachment point
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepthId);
//@@ disable color buffer if you don't attach any color buffer image,
//@@ for example, rendering the depth buffer only to a texture.
//@@ Otherwise, glCheckFramebufferStatus will not be complete.
//glDrawBuffer(GL_NONE);
//glReadBuffer(GL_NONE);
// trigger mipmaps generation explicitly
// NOTE: If GL_GENERATE_MIPMAP is set to GL_TRUE, then glCopyTexSubImage2D()
// triggers mipmap generation automatically. However, the texture attached
// onto a FBO should generate mipmaps manually via glGenerateMipmap().
glBindTexture(GL_TEXTURE_2D, textureId);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
```
## 4. Multi Sample Anti Aliasing
多重采样抗锯齿功能**不会自动打开**
例子
```c
// Open MSAA
glEnable(GL_MULTISAMPLE); // default is enable
// create a 4x MSAA renderbuffer object for colorbuffer
int msaa = 4;
GLuint rboColorId;
glGenRenderbuffers(1, &rboColorId);
glBindRenderbuffer(GL_RENDERBUFFER, rboColorId);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, GL_RGB8, width, height);
// create a 4x MSAA renderbuffer object for depthbuffer
GLuint rboDepthId;
glGenRenderbuffers(1, &rboDepthId);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepthId);
// msaa: samples count
// get max count by use glGetIntegerv(GL_MAX_SAMPLES, &max_samples_count);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, GL_DEPTH_COMPONENT, width, height);
// create a 4x MSAA framebuffer object
GLuint fboMsaaId;
glGenFramebuffers(1, &fboMsaaId);
glBindFramebuffer(GL_FRAMEBUFFER, fboMsaaId);
// attach colorbuffer image to FBO
glFramebufferRenderbuffer(GL_FRAMEBUFFER, // 1. fbo target: GL_FRAMEBUFFER
GL_COLOR_ATTACHMENT0, // 2. color attachment point
GL_RENDERBUFFER, // 3. rbo target: GL_RENDERBUFFER
rboColorId); // 4. rbo ID
// attach depthbuffer image to FBO
glFramebufferRenderbuffer(GL_FRAMEBUFFER, // 1. fbo target: GL_FRAMEBUFFER
GL_DEPTH_ATTACHMENT, // 2. depth attachment point
GL_RENDERBUFFER, // 3. rbo target: GL_RENDERBUFFER
rboDepthId); // 4. rbo ID
```
### 4.1 多重采样 转 单采样
**多重采样后 framebuffer 的渲染结果不能直接使用,需要转换成 single-sample image 才能使用**
转换例子
```c
// copy rendered image from MSAA (multi-sample) to normal (single-sample)
// NOTE: The multi samples at a pixel in read buffer will be converted
// to a single sample at the target pixel in draw buffer.
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMsaaId); // src FBO (multi-sample)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId); // dst FBO (single-sample)
glBlitFramebuffer(0, 0, width, height, // src rect
0, 0, width, height, // dst rect
GL_COLOR_BUFFER_BIT, // buffer mask(which buffers are copied)
GL_LINEAR); // scale filter
```
### 4.2 从 shader 里获取多重采样结果
```c
// shader 里自定义多重纹理采样
// 1. 使用 sampler2DMS 而不是 sampler2D
uniform sampler2DMS screenTextureMS;
// 2. 使用 texelFetch
vec4 colorSample = texelFetch(screenTextureMS, TexCoords, 3); // 取第4个子样本
```
## 5. 检查 framebuffer 的状态
例子
```c
bool checkFramebufferStatus(GLuint fbo)
{
// check FBO status
glBindFramebuffer(GL_FRAMEBUFFER, fbo); // bind
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
switch(status)
{
case GL_FRAMEBUFFER_COMPLETE:
std::cout << "Framebuffer complete." << std::endl;
return true;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
std::cout << "[ERROR] Framebuffer incomplete: Attachment is NOT complete." << std::endl;
return false;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
std::cout << "[ERROR] Framebuffer incomplete: No image is attached to FBO." << std::endl;
return false;
/*
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
std::cout << "[ERROR] Framebuffer incomplete: Attached images have different dimensions." << std::endl;
return false;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS:
std::cout << "[ERROR] Framebuffer incomplete: Color attached images have different internal formats." << std::endl;
return false;
*/
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
std::cout << "[ERROR] Framebuffer incomplete: Draw buffer." << std::endl;
return false;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
std::cout << "[ERROR] Framebuffer incomplete: Read buffer." << std::endl;
return false;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
std::cout << "[ERROR] Framebuffer incomplete: Multisample." << std::endl;
return false;
case GL_FRAMEBUFFER_UNSUPPORTED:
std::cout << "[ERROR] Framebuffer incomplete: Unsupported by FBO implementation." << std::endl;
return false;
default:
std::cout << "[ERROR] Framebuffer incomplete: Unknown error." << std::endl;
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind
}
```
# Reference
1. [OpenGL Vertex Buffer Object (VBO)](http://www.songho.ca/opengl/gl_vbo.html)
2. [How to choose between GL_STREAM_DRAW or GL_DYNAMIC_DRAW?](https://stackoverflow.com/questions/8281653/how-to-choose-between-gl-stream-draw-or-gl-dynamic-draw)
3. [OpenGL Pixel Buffer Object](http://www.songho.ca/opengl/gl_pbo.html)
4. [OpenGL Frame Buffer Object](http://www.songho.ca/opengl/gl_fbo.html)
================================================
FILE: ComputerGraphics(OpenGL)/EXT1_FileFormat.md
================================================
# 一、图片存储格式
## 1. BMP 文件
无损的图片格式,全称 Bitmap(无压缩,体积大)
可以直接存储图片数据,也可以采用索引表的存储方式。但即便采用了索引表的存储方式,也不能使体积缩小太多。图片的内存格式
适合:logos 等有明确边界的图片,程序的缓存文件(无压缩,可直接存储的特点)
格式:ARGB,BMP 文件的第一行数据是显示器的最后一行数据
内存排列:
- 位图文件头(bitmap-file header)
- 位图信息头(bitmap-informationheader)
- 颜色表(color table)
- 颜色点阵数据(bits data)
## 2. GIF 文件
无损的图片压缩格式,只能采用索引表的存储方式存储,因此最多能表示 256 种颜色
GIF 的图片善于做动画,并且支持 alpha 透明通道
适合:logos 等有明确边界的简单图片
## 3. PNG 文件
Portable Network Graphics
无损的图片压缩格式,不能做动画,支持 alpha 透明通道(透明效果优于 GIF)
- PNG-8:采用索引表的方式存储图片,最多能表示 256 种颜色(压缩后体积小于 GIF)
适合:logos 等有明确边界的简单图片
- PNG-24:采用直接存储的方式存储图片,能存储上千种颜色(24 位存储一个像素)
适合:兼顾好的压缩和效果的照片存储
## 4. JPEG 文件
Joint Photographic Experts Group
简称 jpg,有损的图片压缩格式,压缩后体积小(虽然有损,但不易被人眼察觉,24 位存储一个像素 RGB,**不支持透明**)
适合:只考虑体积照片的压缩
JPEG 格式图片是分为一个一个的段来存储的:
段的多少和长度并不是一定的。只要包含了足够的信息,该 JPEG 文件就能够被打开,呈现给人们
段的结构:
```c++
名称 字节数 数据 说明
------------------------------------------------------
段标识 1 FF 每个新段的开始标识
段类型 1 类型编码(称作“标记码”)
段长度 2 包括段内容和段长度本身,不包括段标识和段类型
段内容 ≤65533字节
```
JPEG 图片存储的段文件按顺序依次如下:
1. **SOI(文件头)**Start Of Image
段标识:FF(标志新段的开始)
段类型:D8(SOI 的段类型为 D8,表示文件头)
2. APP0(图像识别信息)Application data marker, type 0
段标识:FF(标志新段的开始)
段类型:E0(APP0 的段类型为 E0,定义交换格式和图像识别信息)
3. DQT(定义量化表)
4. SOF0(图像基本信息)
5. DHT(定义 Huffman 表)
6. DRI(定义重新开始间隔)
7. SOS(扫描行开始)
8. **EOI(文件尾)**End Of Image
段标识:FF(标志新段的开始)
段类型:D9(EOI 的段类型为 D9 表示文件尾)
## 5. SVG 文件
矢量的图片存储方案,内部存储的不是像素,而是曲线和线条
这使得即便是看起来很大的 SVG 图,存储起来会很小(前提是画面图像足够简单)
SVG 使用 XML 语法编写,并且可以在文本工具里修改(可以使用 JavaScript 快速修改 SVG 图片的颜色)
适合:logos 或 icons 等简单且需要适配不同尺寸的网站图片
## 6. TGA 文件
游戏中常用的图像格式
### 6.1 非压缩文件格式

### 6.2 压缩文件格式
# 二、三维文件格式
三维软件之间互相导入导出一般会涉及到一些格式不兼容的问题,不同的格式有着不同的定位及用处,有开源的也有商业的
| 格式 | 功能 | 详情 |
| ---------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| **.abc**
Alembic | 动画、粒子、模型烘焙、流体 | 通用格式,有效地储存, 共享动画与特效场景
[官网](http://www.alembic.io/)
[为什么 CG 行业需要 Alembic(.abc) 通用格式](http://www.bgteach.com/article/131) |
| **.glTF**
GL Transmission Format | 动画、场景、相机、网格、材质、纹理、着色器、着色程序 | json 格式描述
较少的冗余信息
[官网](https://www.khronos.org/gltf/)
[Github](https://github.com/KhronosGroup/glTF/blob/master/README.md) |
| **.fbx**
FilmBoX | 骨骼动画、材质、网格 | FilmBoX 这套软件所使用的格式,后改称 Motionbuilder
Autodesk 家族格式,在 3D Max、Maya、Softimage 等软件间进行**模型**、材质、**动作**和摄影机信息的互导,这样就可以发挥 3D Max 和 Maya 等软件的优势 |
| **.bvh**
BioVision | 骨骼动画 | 对人体运动进行捕获后产生文件格式的文件扩展名,捕捉后的文件可以重复利用,应用在不同的角色骨骼驱动上制作动画 |
| **.obj** | 主要支持多边形(Polygons)模型
不包含动画、材质特性、贴图路径、动力学、粒子等信息 | 几乎所有知名的 3D 软件都支持 OBJ 文件的读写 |
| **.ply**
Polygon File Format | 静态多边形模型,OBJ 格式的升级版
颜色、透明度、表面法向量、材质座标与资料可信度 | 改进了 Obj 格式所缺少的对任意属性及群组的扩充性
因此PLY格式发明了 "property" 及 "element" 这两个关键词,来概括 "顶点、面、相关资讯、群组" 的概念 |
| **.dae**
Data Acquisition Equipment | 骨骼动画、材质、网格 | xml 格式描述,3D Max、Maya,通过安装插件可导出
相比 FBX,对 dae 格式模型的载入有非常高的自由控制,是 FBX 格式代替品 |
| **.x3d** | 多纹理、多遍绘制、支持 Shader 着色、支持多渲染目标、支持几何实例 | xml 格式描述,专为万维网而设计的三维图像标记语言 |
| **.stl** | 三角面静态模型
只能描述三维物体的几何信息,不支持颜色材质等信息 | 计算机图形学处理 CG、数字几何处理如 CAD、 数字几何工业应用, 如三维打印机支持的最常见文件格式 |
| **.dxf**
Drawing Exchange File | 三角面静态模型 | CAD 通用格式 |
| **.3ds** | 三角面静态模型 | 比较早的一种三维格式,三角面,最早游戏模型应用比较广泛
由于后期导入软件的不可编辑性、难以二次编辑现在逐渐的远离了我们的视线 |
# 三、三维软件
三维软件根据工作的功能分类为:
- 主体三维软件
指能独立完成整个三维动画创作的平台性三维软件,具备建模、材质、灯光、渲染、动画、角色等一系列创作的需求,同时允许开发者对软件进行开发第三方插件以扩充软件主体的三维软件
| 软件 | 简介 | 功能 |
| --------- | ---------------------------------- | ------------------------------------------------------------ |
| Blender | 免费开源的三维软件 | 建模、材质、灯光、渲染、雕刻、角色动画、纹理绘制、插件、摄影机跟踪、扣像、合成、游戏引擎 |
| Maya | 售价高昂,易学易用,制作效率高 | 建模、材质、灯光、渲染、角色动画、插件 |
| Cinema 4D | 许多一流艺术家和电影公司的首选 | 建模、材质、灯光、渲染、雕刻、角色动画、纹理绘制、插件 |
| Houdini | 完全是为电影而生 | 建模、材质、灯光、渲染、特效、角色动画 |
| LightWave | 生物建模和角色动画方面功能异常强大 | 建模、材质、灯光、渲染、特效、角色动画 |
| Softimage | 2014 年三月,发布停产声明 | 建模、材质、灯光、渲染、特效、角色动画 |
- 协助三维软件
指能依赖三维主体软件存在,以强大的辅助完成高质量、高效率的流程,这种软件常常也属于单功能三维软件
| 软件 | 简介 | 功能 |
| ------------ | -------------------------------------------------------- | --------------------------------------------------------- |
| Clarisse iFX | 以图像为核心
减少机器开始渲染最终图像所需的交互时间 | 导入模型、材质、灯光、即时渲染、特效、合成 |
| Twinmotion | 可以将项目导出为 .exe 可执行文件 | 导入模型、材质、灯光、即时渲染、动画、展示、交互、VR、360 |
| Lumion | 实时3D可视化工具
用来制作电影和静帧作品 | 导入模型、材质、灯光、即时渲染、动画、展示、360 |
| ZBrush | 数字雕刻和绘画软件 | 雕刻、纹理 |
- 单功能三维软件
一般在某个模块异常强大,由于着重解决流程中的一个环节,在效率上有着得天独厚的优势。 缺点也显而易见,需要主体三维软件的导入导出
| 软件 | 简介 | 功能 |
| -------------------- | ------------------------------------ | ------------------------------------------------------------ |
| Marmoset Toolbag | 实时材质编辑,渲染,动画编辑预览软件 | GPU、CPU 即时渲染器 |
| Silo | 视频游戏及电影创建角色或建筑 | 建模、UV Mapping |
| Cycles Render Engine | blender 中的一种渲染引擎 | 基于光线追踪的渲染引擎,支持交互式渲染,内置一个新的光影节点系统、新的纹理工作流程和GPU加速,用户通过切换GPU渲染可以使渲染过程变得较为便捷 |
# Reference
- [What are the different usecases of PNG vs. GIF vs. JPEG vs. SVG?](https://stackoverflow.com/questions/2336522/what-are-the-different-usecases-of-png-vs-gif-vs-jpeg-vs-svg)
- [libpng](http://www.libpng.org/pub/png/libpng.html)
- [www.w3.org/Graphics](https://www.w3.org/Graphics/JPEG/itu-t81.pdf)
- [JPEG 解码器](https://zhuanlan.zhihu.com/p/27296876)
- [使用 libjpeg 进行图片压缩](https://zhuanlan.zhihu.com/p/126728039)
- [影像算法解析——JPEG 压缩算法](https://zhuanlan.zhihu.com/p/40356456)
- [TGA 文件格式解析](http://www.twinklingstar.cn/2013/471/tga-file-format/)
- [三维文件格式知多少 ](http://www.bgteach.com/article/132)
- [三维软件知多少](http://www.bgteach.com/article/40)
- [图片文件格式知多少 | jpeg、png、pdf、tga、tif、svg、esp、exr、hdr...](https://www.bgteach.com/article/133)
- [游戏制作行业为什么使用TGA格式的贴图而不使用PNG格式? - 韦易笑的回答 - 知乎 ](https://www.zhihu.com/question/340196227/answer/789538293)
================================================
FILE: ComputerGraphics(OpenGL)/EXT2_HardwareSupport.md
================================================
# 一、 GPU 的硬件架构
GPU 主要由 **显存 Device Memory** 和 **流多处理器 Stream Multiprocessors ** 组成(Stream Processors,SP 是 SM 中的一个 Core)
## 1. CPU vs GPU
相对于全面功能考虑的 CPU,GPU 有更多的 ALU(Arithmetic Logic Unit,逻辑运算单元),更少的逻辑控制单元和寄存器
GPU 的并行运算:与 CPU 上十几个线程的并行计算不同,GPU 的线程数可以达到上百万或更多

**缓存行 Cache-line**:缓存存储数据的最小单位
CPU 主要采用**三层**缓存(当今主流的 CPU 架构)
1. L1、L2 硬件缓存成为本地核心内缓存,即一个核一个
如果是 4 核,那就是有 4 个 L1+4 个 L2
2. L3 硬件缓存是所有核共享的。即不管你的 CPU 是几核,这个 CPU 中只有一个 L3
3. L1 硬件缓存的大小是 64K,即 32K 指令缓存 +32K 数据缓存,L2 是 256K,L3 是 2M
这不是绝对的,目前 Intel CPU 基本是这样的设计
GPU 主要采用**二层**缓存(缓存容量小,Cache 命中率低,延时较高)
1. L1 硬件缓存,速度最快,存储共享内存
每个 SM 独立包含,一部分在 SM 内共用,一部分在每个 SP 内单独使用(用来交换共享内存)
2. L2 硬件缓存,速度低于 L1,存储只读常量、纹理
多个 SM 共享使用
3. GPU 的全局内存最大为 12GB
GPU 内存**不支持并发读取和并发写入**

## 2. Stream Multiprocessor
GPU 在 shader 中进行的向量运算采用 SIMD 或 MIMD 计算方式
- **SISD**(Single Instruction Single Data Stream,单指令单数据流):传统顺序执行计算机使用
- **MIMD**(Multiple Instruction Stream Multiple Data Stream,多指令多数据流):现代大多数计算机使用,使用多个控制器来异步地控制多个处理器,从而实现空间上的并行性。从硬件角度看,MIMD 需要消耗大量的晶体管数
- **SIMD**(Single Instruction Stream Multiple Data Stream,单指令多数据流):GPU 内置计算方式,CPU 里也有相应的实现方法
- **MISD**(Multiple Instruction Stream Single Data Stream,多指令单数据流)
早期的 GPU,顶点着色器和像素着色器的硬件结构是独立的(顶点和像素线程资源不能共享)
使用统一着色器架构 **Unified shader Architecture**,VS、PS、GS、CS 都可以使用相同的硬件资源(线程共享)Stream Multiprocessor
Stream Multiprocessor,SM 为 Shader 执行的地方,如右图一个多流处理器包含
1. **多边形引擎 PolyMorph Engine**:负责 attribute Setup、VertexFetch、曲面细分、栅格化
2. **多 ALU 逻辑运算 Core**(每个核代表一个线程,由 Warp 统一管理一组线程)
Warp 中的多个线程是**单指令多线程**(相同的逻辑,不同的数据)
当执行 if-else 语句、 for 循环次数 n 不是常量,或被 break 提前终止了但其他批次循环的内容还在执行时
一个 Warp 线程组只会有部分满足逻辑条件的线程在执行,其他线程当前执行阶段会什么都不执行(被遮掩,但是仍然活跃)
这相当于浪费了一部分线程资源,导致像 if-else 这样的语句是 false 的情况也占用了线程资源(占用了,但不使用)
3. **指令缓存 Instruction Cache**
4. **LD/ST(load/store)**:加载和存储数据
5. **SFU(Special function units)**:执行特殊数学运算(sin、cos、log 等)
6. **寄存器** 128KB
7. **L1 Cache**
配合共享的 L2 Cache 做到 vertex-shader 和 pixel-shader 的数据通信
8. **Uniform Buffer** 的**部分**缓存
9. **Texture Cache 和 纹理读取单元**
## 3. GPU 处理 Shader
**Shader 处理流程**
1. CPU 端编译 Shader 源码为二进制(现代 GPU Shader 为二进制)
2. CPU 端将 shader 二进制指令经由 PCI-e 推送到 GPU 端
3. GPU 在执行代码时,会用 Context 将指令分成若干 Channel 推送到各个 SM 的存储空间
**Shader 的流水线多线程式处理**
多个 SP 里的多个运算单元同时运行同一个 Shader,但每个运算单元支撑的 1 个线程处理的数据又各不相同

# 二、CPU、GPU、显示器
## 1. CPU-GPU 异构系统

**分离式**结构(左图)
- 结构:CPU 和 GPU 拥有各自的存储系统,两者通过 PCI-e 总线进行连接,可以共享一套虚拟地址空间,必要时会进行内存拷贝
- 缺点:PCI-e 相对于两者具有低带宽和高延迟,数据的**传输带宽**成了其中的性能瓶颈
- 应用的硬件设备:**PC**(CPU、GPU 存储**各自有存储系统**)、**移动端**(CPU、GPU **继承到了一个芯片,且共享物理内存**)
很多 SoC (比如:移动端)都是集成了CPU 和 GPU,事实上这仅仅是在物理上进行了集成,并不意味着它们使用的(运行时)就是耦合式结构
**耦合式**结构(右图)
- 结构:CPU 和 GPU 集成到了一个芯片,GPU 没有独立的内存,与 GPU 共享系统内存,由 MMU 进行存储管理
- 应用的硬件设备:PS4
## 2. CPU-GPU Workflow
下图所示为 CPU-GPU 异构系统的工作流,当 CPU 遇到图像处理的需求时,会调用 GPU 进行处理,主要流程可以分为以下四步:
1. 将主存的处理数据 CPU 复制到 显存 GPU 中(可以通过 [DMA](./EXT0_GLBuffers&MultiSample.md) 跳过此步骤)
2. CPU 指令驱动 GPU
3. GPU 中的每个运算单元并行处理
4. GPU 将显存结果传回主存

## 3. CPU-GPU Data transmission

1. **MMIO(Memory-Mapped I/O)**
CPU 通过 MMIO 访问 GPU 的寄存器状态
通过 MMIO 传送数据块传输命令,支持 DMA 的硬件可以实现块数据传输
2. **GPU Context**
上下文表示 GPU 的计算状态,在 GPU 中占据部分虚拟地址空间
多个活跃态下的上下文可以在 GPU 中并存(一个 Context 对应一个 SP)
3. **CPU Channel**
来自 CPU 操作 GPU 的命令存储在内存中,并提交至 GPU channel 硬件单元
每个 GPU 上下文可拥有多个 GPU Channel
每个 GPU 上下文都包含 GPU channel 描述符(GPU 内存中的内存对象)
每个 GPU Channel 描述符存储了channel 的配置,如:其所在的页表
每个 GPU Channel 都有一个专用的命令缓冲区,该缓冲区分配在 GPU 内存中,通过 MMIO 对 CPU 可见
4. **GPU 页表**
GPU 上下文使用 GPU 页表进行分配,该表将**虚拟地址**空间与**其他地址空间**隔离开来
GPU 页表与 CPU 页表分离,其驻留在 GPU 内存中,物理地址位于 GPU 通道描述符中
通过 GPU channel 提交的所有命令和程序都在对应的 GPU 虚拟地址空间中执行
GPU 页表将 GPU 虚拟地址不仅转换为 GPU 设备物理地址,还转换为主机物理地址
这使得 GPU 页面表能够将 GPU 存储器和主存储器统一到统一的 GPU 虚拟地址空间中,从而构成一个完成的虚拟地址空间
5. **PFIFO Engine**
一个提交 GPU 命令的特殊引擎
维护多个独立的命令队列,即 channel(带有 put 和 get 指针的环形缓冲器)
会拦截多有对通道控制区域的访问以供执行
GPU 驱动使用一个通道描述符来存储关联通道的设置
6. **Buffer Object** 缓冲对象
一块内存,可以用来存储纹理,渲染对象,着色器代码等等
## 4. 显示器的显示
从 CPU 通过 GPU 到显示的流程如下:
1. CPU 计算好显示内容提交至 GPU
2. GPU 渲染完成后将渲染结果存入帧缓冲区
3. 视频控制器会按照 `VSync` 信号逐帧读取帧缓冲区的数据
4. 帧缓冲区数据 经过转换后 由显示器进行显示

**显示器显示工作流程**
1. 显示器逐行刷新,每一行刷新完后会发出一个水平同步信号(horizonal synchronization),简称 **HSync**
2. 绘制下一行
3. 显示器一帧画面绘制完成后会发出一个垂直同步信号(vertical synchronization),简称 **VSync**
**双缓冲机制**
- 使用场景:防止在快速运动场景下,由于**显卡运算速率 > 显示器运算速率**导致快速运动的动作割裂情况(画面撕裂)
- 方法:使用两个帧缓冲,一个负责 GPU 写入,一个负责 显示器 读取,用垂直同步确保写入完成后将写入和读取帧缓冲互换角色
- 缺点:开启垂直同步,画面会有延迟(无法达到显卡的最大运算速率),但并没有卡顿
`显卡绘制一帧时间 > 显示器刷新一帧时间 ? 显示器刷新(显卡等待) : 显示器显示上一帧,等待显卡绘制完成(屏幕卡顿);`
- 解决方法:用**三重缓冲**代替垂直同步
三重缓冲:在双缓冲的基础上加了一个缓冲
在等待垂直同步时,来回交替渲染两个离屏的缓冲区
垂直同步发生时,屏幕缓冲区和最近渲染完成的离屏缓冲区交换,实现充分利用硬件性能的目的

# 三、测试 GPU 硬件信息
## 1. NV Shader 扩展功能
[NV shader thread group](https://www.opengl.org/registry/specs/NV/shader_thread_group.txt) 提供了 OpenGL 的扩展,可以查询 GPU 线程、Core、SM、Warp 等硬件相关的属性(需要支持 GLVersion 4.3+ 的硬件)
```c
// 开启扩展
#extension GL_NV_shader_thread_group : require (or enable)
WARP_SIZE_NV // 单个线程束的线程数量
WARPS_PER_SM_NV // 单个SM的线程束数量
SM_COUNT_NV // SM数量
uniform uint gl_WarpSizeNV; // 单个线程束的线程数量
uniform uint gl_WarpsPerSMNV; // 单个SM的线程束数量
uniform uint gl_SMCountNV; // SM数量
in uint gl_WarpIDNV; // 当前线程束id
in uint gl_SMIDNV; // 当前线程束所在的SM id,取值[0, gl_SMCountNV-1]
in uint gl_ThreadInWarpNV; // 当前线程id,取值[0, gl_WarpSizeNV-1]
in uint gl_ThreadEqMaskNV; // 是否等于当前线程id的位域掩码。
in uint gl_ThreadGeMaskNV; // 是否大于等于当前线程id的位域掩码。
in uint gl_ThreadGtMaskNV; // 是否大于当前线程id的位域掩码。
in uint gl_ThreadLeMaskNV; // 是否小于等于当前线程id的位域掩码。
in uint gl_ThreadLtMaskNV; // 是否小于当前线程id的位域掩码。
in bool gl_HelperThreadNV; // 当前线程是否协助型线程(Draw quad 时,不在当前图元上的像素所在的线程)
/**
The variable gl_HelperThreadNV specifies if the current thread is a helper thread. In implementations supporting this extension, fragment shader invocations may be arranged in SIMD thread groups of 2x2 fragments called "quad". When a fragment shader instruction is executed on a quad, it's possible that some fragments within the quad will execute the instruction even if they are not covered by the primitive. Those threads are called helper threads. Their outputs will be discarded and they will not execute global store functions, but the intermediate values they compute can still be used by thread group sharing functions or by fragment derivative functions like dFdx and dFdy.
*/
```
## 2. Sample Code
```C
// VS
#version 430 core
layout (location = 0) in vec3 aPos;
void main() {
gl_Position = vec4(aPos, 1.0f);
}
// FS
#version 430 core
#extension GL_NV_shader_thread_group : require
uniform uint gl_WarpSizeNV; // 单个线程束的线程数量
uniform uint gl_WarpsPerSMNV; // 单个SM的线程束数量
uniform uint gl_SMCountNV; // SM数量
in uint gl_WarpIDNV; // 当前线程束id
in uint gl_SMIDNV; // 当前线程所在的SM id,取值[0, gl_SMCountNV-1]
in uint gl_ThreadInWarpNV; // 当前线程id,取值[0, gl_WarpSizeNV-1]
out vec4 FragColor;
void main() {
// SM id
float lightness0 = gl_SMIDNV / gl_SMCountNV;
// warp id
float lightness1 = gl_WarpIDNV / gl_WarpsPerSMNV;
// thread id
float lightness2 = gl_ThreadInWarpNV / gl_WarpSizeNV;
FragColor = vec4(lightness0);
}
```
**SM id 的结果 lightness0 分析:**
1. 通过计算画面色阶总数得出 SM 的总数
2. 通过单个三角面内重复的像素块个数推算每个 SM 内的核心数
3. 不同三角形的接缝处出现断层,说明同一个像素块如果分属不同的三角形,就会分配到不同的 SM 进行处理
由此推断,**相同面积的区域,如果所属的三角形越多,就会导致分配给 SM 的次数越多,消耗的渲染性能也越多**

# 四、GPU 硬件渲染模式
## 1. IMR
**Immediate Mode Rendering 立即渲染模式**:PC 端

- 优点:顶点着色器和其它几何体相关着色器的输出数据能存储在 GPU 上(FIFO 缓冲区),直到管道中的下一阶段准备使用数据
- 缺点:由于根据图元划分绘制批次,GPU 对整个帧缓冲进行随机访问,帧缓冲只能存储在外部 DRAM 上,导致高分辨率时内存**带宽负载高**
- 优化:降带宽,将最近访问的帧缓冲存储排布在靠近 GPU 的位置来提高内存命中率
- 方法:**优先根据图元来划分绘制批次**

```python
# 每个顶点几何图形画完后直接做像素颜色,此时像素颜色不确定,需要多次读写 framebuffer(深度值的不同)
for draw in renderPass:
for primitive in draw:
for vertex in primitive:
execute_vertex_shader(vertex)
if primitive not culled:
for fragment in primitive:
execute_fragment_shader(fragment)
```
## 2. TB[D]R
**Tile Based [Deferred] Rendering 基于切片的[延迟]渲染**:移动端

- 优点:
可以将整个颜色、深度和模板的工作集存储在快速的 On-chip RAM 上(GPU 直连,不用带宽)
深度测试和混合透明像素所需帧缓冲数据也存储在 GPU 内部,通过提高缓存命中来降低了带宽消耗
- 缺点:
GPU 必须将每个顶点的变化数据和 Tile 的中间状态存储到主内存中,着色阶段随后读取这些数据
通过**延迟一帧**的方式降低了带宽
- 方法:**优先根据帧缓冲来划分绘制批次**,将帧缓冲切分为几个固定大小的 Tile,分别渲染每个 Tile 上的像素

```python
# Pass 1. 将所有几何图元属性处理后,逐个划分到对应的 Tile 中
for draw in renderPass:
for primitive in draw:
for vertex in primitive:
execute_vertex_shader(vertex)
if primitive not culled:
append_tile_list(primitive)
# Pass 2. 根据当前 Tile 的图元属性绘制当前 Tile 所包含的所有像素
for tile in renderPass:
for primitive in tile:
for fragment in primitive:
execute_fragment_shader(fragment)
```
# 五、GPU 硬件新特性
## 1. Pixel Local Storage
支持平台:OpenGL ES、Metal、Vulkan、D3D
**Pixel Local Storage(PLS)**是一种数据存取方式,用 PLS 声明的数据将保存在 GPU 的 Tile buffer 上
应用:**延迟着色**所需的 **GBuffer** 数据一直处于 PLS 之中,最好解析后返回最终颜色,而**不需要将 GBuffer 写回系统显存**
```glsl
// 1. 光照累积
__pixel_localEXT FragData // 可读写数据
{
layout(rgba8) highp vec4 Color;
layout(rg16f) highp vec2 NormalXY;
layout(rg16f) highp vec2 NormalZ_LightingB;
layout(rg16f) highp vec2 LightingRG;
} gbuf;
void main() {
vec3 Lighting = CalcLighting(gbuf.NormalXY, gbuf.NormalZ_LightingB.x);
gbuf.LightingRG += Lighting.xy;
gbuf.NormalZ_LightingB.y += Lighting.z;
}
// 2. 最终着色
// __pixel_local_outEXT 只读数据
__pixel_local_inEXT FragData // 只读数据
{
layout(rgba8) highp vec4 Color;
layout(rg16f) highp vec2 NormalXY;
layout(rg16f) highp vec2 NormalZ_LightingB;
layout(rg16f) highp vec2 LightingRG;
} gbuf;
out highp vec4 FragColor;
void main() {
FragColor = resolve(gbuf.Color, gbuf.LightingRG, gbuf.NormalZ_LightingB.y);
}
```
## 2. Subpass
支持平台:Metal、Vulkan、D3D
SubPass 与 Pixel Local Storage 类似也是存储数据到 GPU 的 Tile buffer 上,借鉴了 TBDR 的延迟一帧的 1 Pass 拆成 图元处理 Pass 和 着色 Pass 这样两个 Subpass 的想法

限制:
- 所有 Subpass 必须在同一个 Render Pass 中(不能是上一个)
- 无法在超过 GPU Tile buffer 范围外进行采样
```c
// 读
// LOAD_OP_LOAD:从全局内存加载 Attachment 到 Tile
// LOAD_OP_CLEAR:清理Tile缓冲区的数据。
// LOAD_OP_DONT_CARE:不对 Tile 缓冲区的数据做任何操作,通常用于 Tile 内的数据会被全部重新,效率高于 LOAD_OP_CLEAR
// 写
// STORE_OP_STORE:将 Tile 内的数据存储到全局内存
// STORE_OP_DONT_CARE:不对 Tile 缓冲区的数据做任何存储操作
// 1. Attachment
VkAttachmentDescription colorAttachment = {};
colorAttachment.format = VK_FORMAT_B8G8R8A8_SRGB;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
// 标明 loadOp 为 DONT_CARE
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
// 标明 storeOp 为 DONT_CARE
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
// 2. 为了让 Attachment 存储到 Tile 内,必须使用标记 TRANSIENT_ATTACHMENT 和 LAZILY_ALLOCATED
VkImageCreateInfo imageInfo{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
imageInfo.flags = flags;
imageInfo.imageType = type;
imageInfo.format = format;
imageInfo.extent = extent;
imageInfo.samples = sampleCount;
// Image 使用 TRANSIENT_ATTACHMENT 的标记
imageInfo.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
VmaAllocation memory;
VmaAllocationCreateInfo memoryInfo{};
memoryInfo.usage = memoryUsage;
// Image 所在的内存使用 LAZILY_ALLOCATED 的标记
memoryInfo.preferredFlags = VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
// 创建 Image
auto result = vmaCreateImage(device.get_memory_allocator(), &imageInfo, memoryInfo, &handle, &memory, nullptr);
```
# 六、常见问题
## 1. PCIe BindWidth
显存的带宽比内存的大很多(显存的位宽大)
- **内存** 和 **显存** 之间的 PCIe 总线带宽过小是 CPU 和 GPU 交互的瓶颈
- 在移动端,由于其[耦合式的物理架构](#gpu),带宽是一种多设备(CPU、GPU、AUDIO 等)共享的资源,而且**处理器通过带宽对存储的访问很耗电**
- OpenGL 的**显示列表**,将一组绘制指令放到 GPU 上,CPU 只要发一条 "执行这个显示列表" 这些指令就执行,而不必每次渲染都发送大量指令到 GPU,从而节约 PCIe 带宽
移动设备的特点:不同于 PC 端的 CPU 和 GPU 纯粹地追求计算性能,移动端在尺寸、能耗、硬件性能等诸多方面都存在显著的差异
1. 性能(**P**erformance):移动设备的各类元件(CPU、带宽、内存、GPU等)的性能都只是PC设备的数十分之一
2. 能耗(**P**ower):为了满足足够长的续航和散热限制,必须严格控制移动设备的整机功率
PC 设备通常可以安装散热风扇、甚至水冷系统,而移动设备不具备这些主动散热方式,只能靠热传导散热
如果散热不当,CPU 和 GPU 都会主动降频,以非常有限的性能运行,以免设备元器件因过热而损毁
3. 面积(**A**rea):移动端的便携性就要求整机只能限制在非常小的体积之内
## 2. Pipeline Barrier
Vulkan、Metal、DX12 等现代图形 API 可以精确指定渲染管线屏障 Barrier 的等待阶段
避免 TBDR 这种 VS 和 FS 分两个 Pass 等待期间造成的 GPU 流水线并发率低,提高 Shader 在 GPU 运行的并发效果

## 3. GPU 访问内存
由于 TBDR 的 Tile GPU 缓存拆分,GPU 在访问内存时,多组 ALU 计算单元核需要串行访问

# Reference
1. [NV extensions](https://www.khronos.org/registry/OpenGL/extensions/NV/)
2. [GDC Vault](https://gdcvault.com/browse/?categories=PgTaVr)
3. [Siggraph Conference Content](https://www.siggraph.org/learn/conference-content/)
4. [Rendering pipeline: The hardware side](https://slideplayer.com/slide/11059244/)
4. [Introduction to GPU Architecture](http://haifux.org/lectures/267/Introduction-to-GPUs.pdf)
4. [An Introduction to Modern GPU Architecture](http://download.nvidia.com/developer/cuda/seminar/TDCI_Arch.pdf)
4. [Revisting Co-Processing for Hash Joins on the Coupled CPU-GPU Architecture](https://www.slideshare.net/mohamedragabslideshare/p12-29046493)
4. [Understanding GPU caches](https://www.rastergrid.com/blog/gpu-tech/2021/01/understanding-gpu-caches/)
8. [Transitioning from OpenGL to Vulkan](https://developer.nvidia.com/transitioning-opengl-vulkan)
9. [Next Generation OpenGL Becomes Vulkan: Additional Details Released](https://www.anandtech.com/show/9038/next-generation-opengl-becomes-vulkan-additional-details-released)
10. [Bringing Fortnite to Mobile with Vulkan and OpenGL ES](https://www.khronos.org/assets/uploads/developers/library/2019-gdc/Vulkan-Bringing-Fortnite-to-Mobile-Samsung-GDC-Mar19.pdf)
5. [Graphics Processing Unit(GPU) Memory Hierarchy](http://meseec.ce.rit.edu/551-projects/spring2015/3-2.pdf)[Tile-Based Rendering](https://developer.arm.com/solutions/graphics-and-gaming/developer-guides/learn-the-basics/tile-based-rendering)
6. [Understanding Render Passes](https://developer.arm.com/solutions/graphics-and-gaming/developer-guides/learn-the-basics/understanding-render-passes)
6. [Asynchronous Shaders](http://developer.amd.com/wordpress/media/2012/10/Asynchronous-Shaders-White-Paper-FINAL.pdf)
7. [GameDev Best Practices](https://developer.samsung.com/galaxy-gamedev/best-practice.html)
8. [Accelerating Mobile XR](https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph_2D00_2018_2D00_mmg_2D00_2_2D00_rob_2D00_xr.pdf)
9. [Google Developer Contributes Universal Bandwidth Compression To Freedreno Driver](https://www.phoronix.com/scan.php?page=news_item&px=Freedreno-UBWC-A6XX)
10. [Using pipeline barriers efficiently](https://github.com/KhronosGroup/Vulkan-Samples/blob/master/samples/performance/pipeline_barriers/pipeline_barriers_tutorial.md#the-sample)
11. [Graphics Shaders - Theory and Practice 2nd Edition](http://cs.uns.edu.ar/cg/clasespdf/GraphicShaders.pdf)
11. [Unreal Engine 4: Mobile Graphics on ARM CPU and GPU Architecture](https://armkeil.blob.core.windows.net/developer/Files/pdf/graphics-and-multimedia/Unreal Engine 4 Mobile Graphics on ARM CPU and GPU Architecture.pdf)
12. [渲染优化-从GPU的结构谈起](https://zhuanlan.zhihu.com/p/58694744)
12. [Render Graph 与现代图形 API](https://zhuanlan.zhihu.com/p/425830762)
7. [计算机那些事(8)——图形图像渲染原理](http://chuquan.me/2018/08/26/graphics-rending-principle-gpu/)
7. [移动游戏性能优化通用技法](https://www.cnblogs.com/timlly/p/10463467.html)
7. [写实大世界游戏渲染技术详解 GPU 优化](https://mp.weixin.qq.com/s/T1t7dQwmxoUfuz1ik2USVg)
7. [剖析虚幻渲染体系(12)- 移动端专题Part 2(GPU架构和机制)](https://www.cnblogs.com/timlly/p/15546797.html)
================================================
FILE: ComputerGraphics(OpenGL)/EXT3_Platform.md
================================================
# 零、代码 Debug
> 关于性能的衡量 帧率/单帧时间,**建议采用单帧时间**
>
> 由于 帧率 = 60 / 单帧时间,他们之间的关系不是线性的,例如在减少同样时间消耗的情况下:
> 低帧率区间进步缓慢,每秒 10fps 下,帧时间减少 2ms,帧率提高到 10.2fps
> 高帧率区间进步明显,每秒 25fps 下,帧时间减少 2ms,帧率提高到 26.3fps
第三方跨平台框架
- [Flutter外接纹理](https://zhuanlan.zhihu.com/p/42566807)
# 一、Android 平台
>图片有不清晰的地方,可以点开连接看大图
## 1. 数据的封装
### 1.1 Surface
内存中的一段绘图缓冲区,是对 framebuffer 的 Java 封装对象
### 1.2 SurfaceTexture
内存中的一段绘图缓冲区,对 EGL texture 的 Java 封装对象
数据源:android.hardware.camera2, MediaCodec, MediaPlayer 和 Allocation 这些类的目标视频数据输出对象
限制:API 11 存在,Android 3.0 及其后才能使用
特点:可以接收一个 EGL texture 的纹理 ID 来产生,可以做到**离线渲染**
关键方法:
- updateTexImage(从内容流中获取当前帧,使得内容流中的一些帧可以跳过)
- 通过 调用 getTransformMatrix 获取纹理的旋转情况

SurfaceTexture 使用流程

## 2. 数据的展示和刷新
### 2.1 SurfaceView
父类:View
限制:API 1 存在
回调方法运行线程:主线程
优点:有自己的 Surface 来刷新,可以做到**局部刷新,独立线程刷新数据**
缺点:不能进行 Transition,Rotation,Scale 等变换,这导致 SurfaceView 在由于刷新不受主线程控制,**滑动时可能有黑边**
关键方法:getHolder(获取 SurfaceHolder ,SurfaceHolder 持有 Surface 数据对象)
### 2.2 GLSurfaceView
父类:SurfaceView
限制:API 3 存在,Android 1.5 及其后才能使用
关键方法:

### 2.3 TextureView
父类:View
限制:API 14 存在,Android 4.0 及其后才能使用,只能针对用于硬件加速(没有 GPU 就无法使用)
回调方法运行线程:主线程(在Android 5.0 引入渲染线程后,它是在渲染线程中做的)
特点:没有自己的 Surface 来刷新,使用所在的 window 来**全局刷新**,可以进行 Transition,Rotation,Scale 等变换,**滑动时没有黑边**
关键方法:
- getSurfaceTexture(可能返回 null)
- setSurfaceTextureListener
```java
public DrawTextureView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.setSurfaceTextureListener(this);
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mSurface = new Surface(surface);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
```
### 2.4 Android 5.0 引入渲染线程

## 3. EGL 环境配置
> 如果 OpenGL 是打印机,EGL 就是纸。EGL:作为 OpenGL ES 和本地窗口的桥梁,不同平台的 EGL 实现方式不一样
### 3.1 渲染同步问题
本地环境和客户端环境
- 本地窗口环境:Windows、X
- 客户端环境:3D 渲染器 OpenGL、2D 矢量图形渲染器 [OpenVG](https://baike.baidu.com/item/OpenVG/7922699?fr=aladdin)
同一个 surface 上可能 **同时异步** 执行了 本地窗口环境 和 客户端环境 的命令
- 本地环境等待客户端环境渲染完成,效果同 glFinish 或 vgFinish 一致
[EGLBoolean eglWaitClient(void);](https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglWaitClient.xhtml)
- 客户端环境等待本地环境渲染完成
[EGLBoolean eglWaitNative(EGLint engine);](https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglWaitNative.xhtml)
### 3.2 获取 EGL 版本信息
1. [EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);](https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglInitialize.xhtml)
本质是初始化函数,但也能通过返回 major,minor 来获取当前 display 硬件设备支持的 OpenGL ES 版本号
2. [const char* eglQueryString(EGLDisplay dpy, EGLint name);](https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglQueryString.xhtml)
查询当前 display 硬件设备有哪些 EGL 的扩展支持,**调用前必须先通过 eglInitialize 初始化 EGL**
### 3.3 EGL 的 Context 和 Surface
EGL 主要作用是将渲染绘制到本地窗口上
EGL 可以销毁本地资源(各种 surface 类型)
只有一个方法,[EGLBoolean eglDestroySurface(EGLDisplay display, EGLSurface surface);](https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglDestroySurface.xhtml)
EGL 可以创建本地环境的资源(各种 surface 类型)格式有
1. pixel buffer(存储在显存上)
OpenGL API 方面,[EGLSurface eglCreatePbufferSurface(...)](https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglCreatePbufferSurface.xhtml)
OpenVG API 方面,[EGLSurface eglCreatePbufferFromClientBuffer(...)](https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglCreatePbufferFromClientBuffer.xhtml)
2. frame buffer(存储在显存上)
本地环境 API 创建的 window 内的 surface,[EGLSurface eglCreateWindowSurface(...)](https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglCreateWindowSurface.xhtml)
3. 本地环境 API 创建的 [pixmap(常用做本地图像的内部存储)](https://franz.com/support/documentation/current/doc/cg/cg-pixmaps.htm),[EGLSurface eglCreatePixmapSurface(...);](https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglCreatePixmapSurface.xhtml)
Pixmap:将图像以像素颜色数组的结构来存储的对象
Bitmap:用 1 bit 来存储 1 个像素颜色的 Pixmap
**EGLContext 上下文创建步骤**
```c
#include
#include
#include
#include
typedef ... NativeWindowType;
extern NativeWindowType createNativeWindow(void);
// 虽然是一维数组,但还是要采用 id, value, id, value ... 的存储方式
const EGLint attribute_list[] =
{
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
//EGL_WINDOW_BIT EGL_PBUFFER_BIT we will create a pixelbuffer surface
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8, // if you need the alpha channel
EGL_DEPTH_SIZE, 8, // if you need the depth buffer
EGL_STENCIL_SIZE, 8,
EGL_NONE
};
// EGL context attributes
const EGLint ctxAttr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
void createGLESEnv()
{
EGLint num_config;
EGLint numConfigs;
EGLint eglMajVers;
EGLint eglMinVers;
EGLConfig config;
EGLDisplay m_eglDisplay; // 关联系统物理屏幕,表示显示设备句柄
EGLContext m_eglContext;
EGLSurface m_eglSurface; // EGLSurface 和 Java 的 Surface 没有关系,是两个独立的对象
NativeWindowType native_window;
// 1. get an EGL display connection
m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if(EGL_NO_DISPLAY == m_eglDisplay) {
Log.e("ERROR: get an EGL display connection");
}
// 2. initialize the EGL display connection
if (!eglInitialize(m_eglDisplay, &eglMajVers, &eglMinVers)) {
Log.e("ERROR: initialize the EGL display connection");
}
// 3. get an appropriate EGL frame buffer configuration
if(!eglChooseConfig(m_eglDisplay, attribute_list, &config, 1, &num_config)) {
Log.e("ERROR: get an appropriate EGL frame buffer configuration");
}
// 4. create an EGL rendering context
m_eglContext = eglCreateContext(m_eglDisplay, config, EGL_NO_CONTEXT, ctxAttr);
if (EGL_NO_CONTEXT == m_eglContext) {
EGLint error = eglGetError();
if(error == EGL_BAD_CONFIG) {
Log.e("ERROR: create an EGL rendering context");
}
}
// 5. create a native window
native_window = createNativeWindow();
// 6. create an EGL window surface
m_eglSurface = eglCreateWindowSurface(m_eglDisplay, config, native_window, NULL);
// 7. connect the context to the surface
if (!eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)) {
Log.e("ERROR: connect the context to the surface");
}
}
void destroyGlESEnv()
{
if (m_eglDisplay != EGL_NO_DISPLAY) {
eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(m_eglDisplay, m_eglContext);
eglDestroySurface(m_eglDisplay, m_eglSurface);
eglReleaseThread();
eglTerminate(m_eglDisplay);
}
m_eglDisplay = EGL_NO_DISPLAY;
m_eglSurface = EGL_NO_SURFACE;
m_eglContext = EGL_NO_CONTEXT;
}
int main(int argc, char ** argv)
{
createGLESEnv();
glClearColor(1.0, 1.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glFlush();
// 所有的绘制步骤在后台绘制,当绘制完成时切换前后台缓冲,确保显示的一直是绘制完成的画面
eglSwapBuffers(m_eglDisplay, m_eglSurface);
destroyGlESEnv();
return EXIT_SUCCESS;
}
```
## 4. 平台问题
### 4.1 TEXTURE_EXTERNAL_OES
[TEXTURE_EXTERNAL_OES](https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt) 是 OpenGL ES 在 Android 上的扩展,在获取相机纹理时只有 TEXTURE_EXTERNAL_OES 类型的纹理
使用扩展纹理 TEXTURE_EXTERNAL_OES 步骤
1. 创建纹理时
```java
// 注意 GLES11Ext
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP_TO_EDGE);
```
2. 在 fragment shader 里要提前声明使用的扩展
```c
#extension GL_OES_EGL_image_external : require
uniform samplerExternalOES u_texture; // 代替 sampler2D
void main() {}
```
### 4.2 Java 成员变量和 C++ 指针的 JNI 层绑定
绑定指针
- Java 类中,声明一个为 long 的长整型类型的成员变量代表 C++ 指针的句柄
- C / C++ 文件中,每次获取 jlong 的成员变量时**强制转换为指针来使用**
```c
#include
jfieldID inline getHandleField(JNIEnv *env, jobject obj)
{
jclass c = env->GetObjectClass(obj);
// J is the type signature for long:
return env->GetFieldID(c, "nativeHandle", "J");
}
template
T *getHandle(JNIEnv *env, jobject obj)
{
jlong handle = env->GetLongField(obj, getHandleField(env, obj));
return reinterpret_cast(handle);
}
template
void setHandle(JNIEnv *env, jobject obj, T *t)
{
jlong handle = reinterpret_cast(t);
env->SetLongField(obj, getHandleField(env, obj), handle);
}
// Use Example
MyObject* ptr = new MyObject();
setHandle(env, object, ptr);
ptr = getHandle(env, object);
```
绑定智能指针
- Java 类中,声明一个为 long 的长整型类型的成员变量代表 C++ 指针的句柄
- C / C++ 文件中,每次获取 jlong 的成员变量时**强制转换为包含智能指针的对象的指针来使用**
```c
template
class SmartPointerWrapper {
std::shared_ptr mObject;
public:
template
explicit SmartPointerWrapper(ARGS... a) {
mObject = std::make_shared(a...);
}
explicit SmartPointerWrapper (std::shared_ptr obj) {
mObject = obj;
}
virtual ~SmartPointerWrapper() noexcept = default;
void instantiate (JNIEnv *env, jobject instance) {
setHandle(env, instance, this);
}
jlong instance() const {
return reinterpret_cast(this);
}
std::shared_ptr get() const {
return mObject;
}
static std::shared_ptr object(JNIEnv *env, jobject instance) {
return get(env, instance)->get();
}
static SmartPointerWrapper *get(JNIEnv *env, jobject instance) {
return getHandle>(env, instance);
}
static void dispose(JNIEnv *env, jobject instance) {
auto obj = get(env,instance);
delete obj;
setHandle(env, instance, nullptr);
}
};
// Use Example
SmartPointerWrapper