[Switch] OpenGL uses PBO to copy screen images to memory or textures at high speed OpenGL uses PBO to copy screen images to memory or textures at high speed

If you want to do a screenshot function for the game, or want to make the screen image into a texture, you need a PBO very much

Usually, you want to read the pixel data of the screen image into memory, you need to use glReadPixels and then pass the pixels parameter into a memory address

This is very, very bad, because glReadPixels will copy the pixel data of the screen image from the video memory of the graphics card to the memory stick. This process is very, very slow, especially when the amount of data is large.

Then if you want to use glTexImage2D to transfer the pixel data to the texture, the data has to be copied from the memory stick to the video memory. This process is also very, very slow, especially when the amount of data is large.

So is there a way to directly access the data in the video memory through a memory pointer? Of course there is, that is OpenGL's Array Buffer

This thing is called an array buffer in Chinese and can also be omitted as a buffer directly because it is a piece of memory in the video memory, so we will call it a buffer below.

You can use glMapBuffer to get its memory pointer, and then you can do whatever you want. In addition, many OpenGL functions that return data can write data to the buffer instead of copying it to the memory stick.

For example, in glReadPixels, you originally wanted to pass a memory pointer, but with a buffer, it can copy the data to the buffer instead of the memory stick.

Because the pixel data of the screen is in the video memory, and the buffer is also in the video memory, so the speed of video memory -> copy data -> video memory is much faster than that of video memory -> copy data -> memory stick

Then we directly use glMapBuffer to get the memory address of the buffer, we can access the copied screen pixel data, and then what to do.

Moreover, some functions of OpenGL can write data into the buffer, and some functions can also read data from the buffer. For example, glTexImage2D or something, if you are smart, you already know what to do next span

If we read the pixel data of the screen into the buffer in the previous step, we can directly use glTexImage2D, glTexSubImage2D and other functions to transfer the data in the buffer to the texture

In this way, we store the screen image in the texture, and then do what, and this process has nothing to do with the memory stick, so the speed is very, very fast

 

Of course we need functions for manipulating Buffers. You can also be lazy with the GLEW library

#include "glext.h"

PFNGLBINDBUFFERPROC glBindBuffer = NULL;
PFNGLBUFFERDATAPROC glBufferData = NULL;
PFNGLGENBUFFERSPROC glGenBuffers = NULL;
PFNGLMAPBUFFERPROC glMapBuffer = NULL;
PFNGLUNMAPBUFFERPROC glUnmapBuffer = NULL;

void gl_init()
{
    glBindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");
    glBufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData");
    glGenBuffers = (PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffers");
    glMapBuffer = (PFNGLMAPBUFFERPROC)wglGetProcAddress("glMapBuffer");
    glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)wglGetProcAddress("glUnmapBuffer");
}

So we have already talked a lot above, and then we will start writing the code.

GLuint Buffer;
GLuint Texture;

void init()
{
    // Create 1 buffer 
    glGenBuffers( 1 , & Buffer);
    
    // When the buffer is just created, the memory has not been allocated, so we have to initialize it
     . // Bind first.. 
    glBindBuffer(GL_ARRAY_BUFFER, Buffer);
    
    // This step is very important. The second parameter specifies the size of the buffer in bytes. Be sure to pay attention
     . //   Then the third parameter is the data for initialization. If you pass a memory pointer in, this function It will copy your
     //   data into the buffer, we don't need any data at the beginning, so just pass NULL
     //   GL will allocate memory for the buffer internally, and then do nothing, the fourth The parameter can optimize the memory efficiency, specify
     //   the frequency of reading and writing data in the buffer, if the data in the buffer is not often read and written, you can pass in GL_STATIC_****
     //   This way GL will put the buffer in the memory data For areas that change infrequently, if you want to read and write the data in the buffer frequently, you can pass
     // other values , please
       refer to @ https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/ / Note the BUFFER_SIZE here We assume that we want to copy the pixel data of the entire screen, the format is RGB, then
     // the   size is screen width × screen height × 3, 1 byte per pixel     glBufferData(GL_ARRAY_BUFFER, BUFFER_SIZE, NULL, GL_STREAM_COPY) ;

    
    // This way our buffer has been initialized, and it now has a piece of memory
       available.//It can be accessed at any time with glMapBuffer
     // After initialization, unbind it 
    glBindBuffer(GL_ARRAY_BUFFER, 0 );
    
    
    // Create a texture, and then copy the screen to this texture 
    glGenTextures( 1 , & Texture);
     // Initialize the texture, not much explanation 
    glBindTexture(GL_TEXTURE_2D, Texture);
     // The data parameter here passes NULL and the above buffer In the same way, GL only allocates memory for textures
     //   ScreenWide and ScreenTall are the width and height of the screen.
     // The format uses RGB, because the screen does not need a transparent channel, so the pixel data size of the texture is the same as the buffer size above 
    glTexImage2D( GL_TEXTURE_2D, 0 , GL_RGB, ScreenWide, ScreenTall, 0 , GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Unbind after initialization 
    glBindTexture(GL_TEXTURE_2D, 0 );
}

void draw()
{
    // Pretend to draw the game scene here
    
    
    // First we need to bind the buffer to GL_PIXEL_PACK_BUFFER 
    here glBindBuffer(GL_PIXEL_PACK_BUFFER, Buffer);
     // This function will judge whether GL_PIXEL_PACK_BUFFER is bound to a buffer, if so, write the data to this buffer In the area
     // The first 4 parameters are the screen area to be read, not much explanation
     // The   format is RGB, the type is BYTE, 1 byte per pixel
     // If GL_PIXEL_PACK_BUFFER has a binding buffer, the last parameter is used as The offset value is used, and there is no useless thing here.
    //   Just pass NULL 
    glReadPixels( 0 , 0 , ScreenWide, ScreenTall, GL_RGB, GL_UNSIGNED_BYTE, NULL);
     // Well, we have successfully copied the pixel data of the screen to the buffer
    
    // At this time, you can use glMapBuffer to get the memory pointer of the buffer to read the pixel data in it and save it to the picture file
     // Complete the screenshot 
    /* *****
    
    // Note that the first parameter of glMapBuffer does not have to be GL_PIXEL_PACK_BUFFER, you can bind the buffer to GL_ARRAY_BUFFER of the init function above
    // Then GL_ARRAY_BUFFER is also passed here. Since I am too lazy to bind it again, I will use the GL_PIXEL_PACK_BUFFER bound above.
    void *data = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE);
    if (data)
    {
        WriteTGA("screenshot.tga", ScreenWide, ScreenTall, data);
        glUnmapBuffer(GL_PIXEL_PACK_BUFFER); // Don't forget to unmap the Map
    }
    
    ******/
    
    // When you are done, unbind the buffer in GL_PIXEL_PACK_BUFFER to prevent other functions from misoperating 
    qglBindBuffer(GL_PIXEL_PACK_BUFFER, 0 );
    

    
    // Then we demonstrate passing the pixel data in the buffer to the texture
     //   First we bind the buffer to GL_PIXEL_UNPACK_BUFFER. Pay attention here! GL_PIXEL_PACK_BUFFER and GL_PIXEL_UNPACK_BUFFER are different! 
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Buffer);
     // Bind texture 
    glBindTexture(GL_TEXTURE_2D, Texture);
     // This function will judge whether GL_PIXEL_UNPACK_BUFFER is bound to a buffer
     //    If there is, read data from this buffer, and It is not the memory specified by the data parameter
     // The previous parameters are very simple and will not be explained. The last parameter is the same as the above glReadPixels. Just pass NULL
     // In this way glTexSubImage2D will read data from our buffer
     // Why here What about using glTexSubImage2D, because if you use glTexImage2D, glTexImage2D will destroy the texture memory and re-apply, glTexSubImage2D just updates the data in the texture
     //   This improves the speed and optimizes the utilization of video memory 
    glTexSubImage2D(GL_TEXTURE_2D,0 , 0 , 0 , ScreenWide, ScreenTall, GL_RGB, GL_UNSIGNED_BYTE, NULL);
     // After finishing, unbind the buffer in GL_PIXEL_UNPACK_BUFFER to prevent other functions from misoperating 
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0 );
    
    // At this point we have updated the texture, we can draw the texture to see
    
    // pretend there is code to draw textures here 
}

That's it for the code. If you want more details on the functions used above, be sure to check out https://www.khronos.org/registry/OpenGL-Refpages/gl2.1

Supplement: GL_PIXEL_PACK_BUFFER and GL_PIXEL_UNPACK_BUFFER are collectively referred to as PBO (Pixel Buffer Object) because these two are used for pixels, so they are called Pixel buffer.

Anything can be stored in the buffer, but not just pixels. Please search for other information for details. 

This is what the texture looks like:

I don't see the HUD in the texture because the HUD hasn't been drawn when I read the screen pixels

 

Original link:

OpenGL uses PBO to quickly copy screen images to memory or textures

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325117222&siteId=291194637