What's new

Reading Video Memory?

nameuser

New member
Hello, I have a question about emulating consoles with 3D and was hoping someone could help:

Can DirectX or OpenGL be used to read from what is rendered so as to emulate reading from the console's video memory (which seems to be supported on all consoles)? How?
If not, how do various emulator of 3D consoles achieve good non-software emulation when the game cannot read and modify the data it renders?

Any help and advice would be appreciated.
 

Slougi

New member
At least one technique that comes to mind is to render to a texture that is then drawn to the screen. It depends on what the performance constraints are.
 

Exophase

Emulator Developer
In OpenGL you can use Pixel Buffer Objects to accomplish this. Unfortunately I can't give a lot of implementation details.
 
H

h4tred

Guest
Or you can use framebuffer objects too. Much faster than using PBOs.

In DirectX, using render targets is what most devs use to do render to texture.
 

Lunaboy

New member
I don't know how to do it using DirectX, but in OpenGL there are several ways to re-use the rendered data. In most cases you're using the rendered data as a texture to draw other models. Beware that some older videocards can only use texture-dimensions that are a power of two. In those cases you can adjust the projectionmatrix to render to a square region.

The slowest, but most supported way is using the copy-to-texture method:
1. Create an empty texture (use glTexImage2D and pass a NULL pointer as image data)
2. Render your scene to the screen as usual
3. Make the empty texture the current one with glBindTexture
4. Call glCopyTexSubImage2D to copy a region from the screen to the texture
5. Use the texture to draw models

You should use glCopyTexSubImage2D instead of glCopyTexImage2D, because the last one will re-allocate the memory every time before copy. This is expensive and not necessary, because the texture is always the same size. One thing to keep in mind is that everything outside of the screen will not be drawn to the screen (viewport-clipping). So copying the whole rendered image to a texture when the renderwindow is partly outside the screen will result in a black part at the side of the texture.


Another way is using framebuffer-objects. These are virtual renderbuffers you can allocate inside the videomemory.
It works like this:
1. Create an empty framebuffer-object
2. Allocate a colorbuffer and a depthbuffer inside the videomemory
3. Attach the colorbuffers to the framebuffer-object
4. Set the framebuffer object as current render-target
5. Draw your scene (everything is rendered inside the allocated color- and depthbuffers)
6. Set the screen as current render-target
7. Select the allocated colorbuffer as texture
8. Use the texture to draw models

This method is slightly faster than using the copy-to-texture method, because you don't need to make a copy the imagedata (wich is realy expensive). You also won't get black parts when the renderwindow is partly outside the screen, because you're using a virtual framebuffer and not the screen. This is a new technique that's only supported by newers videocards. Use the function glGetString(GL_EXTENSIONS) to find out if your videocard supports framebuffer-objects. Get the addreses of the functions from OpenGL by calling wglGetProcAddress to use the OpenGL-extension. Don't forget to de-allocate the colorbuffers and the framebuffer-object when you exit your application.


It's also possible to copy the rendered data from the videomemory to your application. You can do this by calling glReadPixels(). You can specify a pointer to a buffer that will receive the imagedata, and the region of the screen you want to copy. Be careful to allocate enough space in your buffer to hold the image.

Good luck
 
H

h4tred

Guest
Nice to see another OGL dev around.

BTW, do you know of a easy way to capture the main depth buffer info into a FBO or FBO depth attachment? Any source code examples would be great :p
 

Lunaboy

New member
To copy the main depthbuffer contents to the FBO depthbuffer, make sure both depthbuffers are the same type. So if your render window uses 16 bit depthbuffers, you have to make the depthbuffer of the FBO also 16 bit. After that you can copy the contents of the main depthbuffer to the FBO by using the GL_EXT_framebuffer_blit extension.

First call glBindFramebufferEXT to select the source and the target FBO's (0 = main-buffers). After that, use glBlitFramebufferEXT(GL_DEPTH_BUFFER_BIT) to copy the contents of the main depthbuffer to the FBO. Call glBindFramebufferEXT afterwards to select the current bound framebuffer for reading and writing.

Here is a code example for 16-bit depthbuffers:
Code:
GLuint Fbo, DepthRenderBuffer, FboTexture;

void OnInit(){
  //** Create FBO and depth-renderbuffer **
  glGenFramebuffersEXT(1, &Fbo);
  glGenRenderbuffersEXT(1, &DepthRenderBuffer);
  glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, DepthRenderBuffer);
  glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, RenderWidth, RenderHeight);

  //** Check for errors **
  if(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) DoError("Error creating FBO");

  //** Create rendertexture **
  glGenTextures(1, &FboTexture);
  glBindTexture(GL_TEXTURE_2D, FboTexture);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, RenderWidth, RenderHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

  //** Attach to FBO **
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, Fbo);
  glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, FboTexture, 0);
  glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, DepthRenderBuffer);
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

void OnRender(){
  //** Render to main depthbuffer **
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
  glViewport(0, 0, ScreenWidth, ScreenHeight);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  //** Draw to main buffers **
  DrawScene(0);

  //** Switch to FBO and clear it **
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, Fbo);
  glViewport(0, 0, RenderWidth, RenderHeight);
  glClear(GL_COLOR_BUFFER_BIT);

    //** Copy data from main depthbuffer to FBO depthbuffer **
    glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
    glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, Fbo);
    glBlitFramebufferEXT(0, 0, ScreenWidth, ScreenHeight, 0, 0, RenderWidth, RenderHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
    glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, Fbo);
    glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, Fbo);

    //** Draw to FBO buffers (with depthinfo copied from main buffers) **
    DrawScene(1);

  //** Switch back to main buffers **
  glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
  glViewport(0, 0, ScreenWidth, ScreenHeight);
}

void OnDestroy(){
  //** Cleanup **
  glDeleteTextures(1, &FboTexture);
  glDeleteFramebuffersEXT(1, &Fbo);
  glDeleteRenderbuffersEXT(1, &DepthRenderBuffer);
}
 

Top