More work on glGraphicsBuffers (will compile soon)

This commit is contained in:
Josh Yelon 2006-03-22 04:14:46 +00:00
parent 67f79bc9c4
commit f35a7469d0
2 changed files with 285 additions and 99 deletions

View File

@ -24,14 +24,32 @@ TypeHandle CLP(GraphicsBuffer)::_type_handle;
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
CLP(GraphicsBuffer):: CLP(GraphicsBuffer)::
CLP(GraphicsBuffer)(GraphicsPipe *pipe, GraphicsStateGuardian *gsg, CLP(GraphicsBuffer)(GraphicsPipe *pipe,
const string &name, const string &name,
int x_size, int y_size) : int x_size, int y_size, int flags,
GraphicsBuffer(pipe, gsg, name, x_size, y_size) GraphicsStateGuardian *gsg,
GraphicsOutput *host) :
GraphicsBuffer(pipe, name, x_size, y_size, flags, gsg, host)
{ {
// Since the FBO never gets flipped, we get screenshots from the // Since the FBO never gets flipped, we get screenshots from the
// same buffer we draw into. // same buffer we draw into.
_screenshot_buffer_type = _draw_buffer_type; _screenshot_buffer_type = _draw_buffer_type;
// Initialize these.
_fbo = 0;
_rb_size_x = 0;
_rb_size_y = 0;
for (int i=0; i<SLOT_COUNT; i++) {
_rb[i] = 0;
_attached[i] = 0;
}
_attach_point[SLOT_depth] = GL_DEPTH_ATTACHMENT_EXT;
_attach_point[SLOT_stencil] = GL_STENCIL_ATTACHMENT_EXT;
_attach_point[SLOT_color] = GL_COLOR_ATTACHMENT0_EXT;
_slot_format[SLOT_depth] = GL_DEPTH_COMPONENT;
_slot_format[SLOT_stencil] = GL_STENCIL_INDEX;
_slot_format[SLOT_color] = GL_RGBA;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -44,7 +62,7 @@ CLP(GraphicsBuffer)::
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: wglGraphicsBuffer::begin_frame // Function: glGraphicsBuffer::begin_frame
// Access: Public, Virtual // Access: Public, Virtual
// Description: This function will be called within the draw thread // Description: This function will be called within the draw thread
// before beginning rendering for a given frame. It // before beginning rendering for a given frame. It
@ -52,12 +70,10 @@ CLP(GraphicsBuffer)::
// if the frame should be rendered, or false if it // if the frame should be rendered, or false if it
// should be skipped. // should be skipped.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
bool wglGraphicsBuffer:: bool glGraphicsBuffer::
begin_frame(FrameMode mode) { begin_frame(FrameMode mode) {
PStatTimer timer(_make_current_pcollector); PStatTimer timer(_make_current_pcollector);
if (!_is_valid) {
begin_frame_spam();
if (_gsg == (GraphicsStateGuardian *)NULL) {
return false; return false;
} }
@ -65,45 +81,220 @@ begin_frame(FrameMode mode) {
return false; return false;
} }
if (_track_host_size) { // Figure out the desired size of the buffer.
if (mode == FM_render) {
rebuild_bitplanes();
clear_cube_map_selection();
}
}
////////////////////////////////////////////////////////////////////
// Function: glGraphicsBuffer::rebuild_bitplanes
// Access: Public, Virtual
// Description: This function will be called within the draw thread
// to allocate/reallocate the fbo and all the associated
// renderbuffers, just before rendering a frame.
////////////////////////////////////////////////////////////////////
void glGraphicsBuffer::
rebuild_bitplanes() {
glGraphicsStateGuardian *glgsg;
DCAST_INTO_R(glgsg, _gsg, false);
// Bind the FBO
if (_fbo == 0) {
glgsg->_glGenFramebuffersEXT(1, &_fbo);
if (_fbo == 0) {
glgsg->report_my_gl_errors();
return;
}
}
glgsg->_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _fbo);
// Calculate bitplane size. This can be larger than the buffer.
if (_creation_flags & GraphicsPipe::BF_track_host_size) {
if ((_host->get_x_size() != _x_size)|| if ((_host->get_x_size() != _x_size)||
(_host->get_y_size() != _y_size)) { (_host->get_y_size() != _y_size)) {
set_size_and_recalc(_host->get_x_size(), set_size_and_recalc(_host->get_x_size(),
_host->get_y_size()); _host->get_y_size());
} }
} }
int desired_x = _x_size;
int desired_y = _y_size;
if (!glgsg->get_supports_tex_non_pow2()) {
desired_x = Texture::up_to_power_2(desired_x);
desired_y = Texture::up_to_power_2(desired_y);
}
// This function also contains the code to *reopen* the window // Scan the textures list and determine what should be attached.
// in the event that configuration parameters (ie, size, texture
// bindings) have changed.
open_window();
// bind the FBO Texture *attach[SLOT_COUNT];
for (int i=0; i<SLOT_COUNT; i++) {
attach[i] = 0;
}
for (int i=0; i<count_textures(); i++) {
if (get_rtm_mode(i) != RTM_bind_or_copy) {
continue;
}
Texture *tex = get_texture(i);
Texture::Format fmt = tex->get_format();
if (mode == FM_render) { // If it's a not a 2D texture or a cube map, punt it.
begin_render_texture(); if ((tex->get_texture_type() != Texture::TT_2d_texture)&&
clear_cube_map_selection(); (tex->get_texture_type() != Texture::TT_cube_map)) {
_rtm_mode[i] = RTM_copy_texture;
continue;
}
// Identify right attachment point.
int slot = SLOT_COUNT;
if (fmt == Texture::F_depth_component) {
slot = SLOT_depth;
} else if (fmt == Texture::F_stencil_index) {
slot = SLOT_stencil;
} else if (fmt == Texture::F_rgba) {
slot = SLOT_color;
} else {
_rtm_mode[i] = RTM_copy_texture;
continue;
}
// If there's already a texture bound to this slot,
// then punt this texture.
if (attach[slot]) {
_rtm_mode[i] = RTM_copy_texture;
continue;
}
// Assign the texture to this slot.
attach[slot] = tex;
}
// For all slots, update the slot.
for (int slot=0; slot<SLOT_COUNT; slot++) {
Texture *tex = attach[slot];
if (tex) {
// If the texture is already bound to the slot, and it's
// the right size, then no update of this slot is needed.
if ((_tex[slot] == tex)&&
(tex->get_x_size() == desired_x)&&
(tex->get_y_size() == desired_y)) {
continue;
}
// Bind the texture to the slot.
tex->set_x_size(desired_x);
tex->set_y_size(desired_y);
TextureContext *tc = tex->prepare_now(get_prepared_objects(), this);
nassertv(tc != (TextureContext *)NULL);
CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
if (tex->get_texture_type() == Texture::TT_2d_texture) {
glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, _attach_point[slot],
GL_TEXTURE_2D, gtc->_index, 0);
} else {
glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, _attach_point[slot],
GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, gtc->_index, 0);
}
_tex[slot] = tex;
// If there was a renderbuffer bound to this slot, delete it.
if (_rb[slot] != 0) {
glgsg->_glDeleteRenderbuffers(1, &(_rb[slot]));
_rb[slot] = 0;
}
} else {
// If a renderbuffer is already attached to the slot, and it's
// the right size, then no update of this slot is needed.
if ((_rb[slot] != 0)&&
(_rb_size_x == desired_x)&&
(_rb_size_y == desired_y)) {
continue;
}
// If there's no renderbuffer for this slot, create one.
if (_rb[slot] == 0) {
glgsg->_glGenRenderbuffers(1, &(_rb[slot]));
}
// Resize the renderbuffer appropriately.
glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rb[slot]);
glgsg->_glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, _slot_format[slot],
desired_x, desired_y);
glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, 0);
// Bind the renderbuffer to the slot.
glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, _attach_point[slot],
GL_RENDERBUFFER_EXT, _rb[slot]);
// Toss any texture that was connected to the slot.
_tex[slot] = 0;
}
}
// These record the size of all nonzero renderbuffers.
_rb_size_x = desired_x;
_rb_size_y = desired_y;
}
////////////////////////////////////////////////////////////////////
// Function: glGraphicsBuffer::generate_mipmaps
// Access: Private
// Description: This function will be called within the draw thread
// after rendering is completed for a given frame.
// If we've just rendered into level zero of a mipmapped
// texture, then all subsequent mipmap levels will now
// be calculated.
////////////////////////////////////////////////////////////////////
void glGraphicsBuffer::
generate_mipmaps() {
glGraphicsStateGuardian *glgsg;
DCAST_INTO_R(glgsg, _gsg, false);
for (int slot=0; slot<SLOT_COUNT; slot++) {
Texture *tex = _tex[slot];
if ((tex != 0) && (tex->uses_mipmaps())) {
glgsg->_state._texture = 0;
TextureContext *tc = tex->prepare_now(get_prepared_objects(), this);
nassert(tc != (TextureContext *)NULL);
CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
GLenum target = get_texture_target(tex->get_texture_type());
GLP(BindTexture)(target, gtc->_index);
glgsg->_glGenerateMipmap(target);
GLP(BindTexture)(target, 0);
}
} }
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: wglGraphicsBuffer::end_frame // Function: glGraphicsBuffer::end_frame
// Access: Public, Virtual // Access: Public, Virtual
// Description: This function will be called within the draw thread // Description: This function will be called within the draw thread
// after rendering is completed for a given frame. It // after rendering is completed for a given frame. It
// should do whatever finalization is required. // should do whatever finalization is required.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void wglGraphicsBuffer:: void glGraphicsBuffer::
end_frame(FrameMode mode) { end_frame(FrameMode mode) {
end_frame_spam(); end_frame_spam();
nassertv(_gsg != (GraphicsStateGuardian *)NULL); nassertv(_gsg != (GraphicsStateGuardian *)NULL);
if (mode == FM_render) { if (mode == FM_render) {
end_render_texture();
copy_to_textures(); copy_to_textures();
} }
// Unbind the FBO // Unbind the FBO
glgsg->_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
if (mode == FM_render) {
generate_mipmaps();
}
_host->end_frame(FM_parasite); _host->end_frame(FM_parasite);
@ -127,68 +318,58 @@ end_frame(FrameMode mode) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void CLP(GraphicsBuffer):: void CLP(GraphicsBuffer)::
select_cube_map(int cube_map_index) { select_cube_map(int cube_map_index) {
open_buffer(); GLCAT.error() << "select_cube_map not implemented yet.\n";
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: wglGraphicsBuffer::open_buffer // Function: glGraphicsBuffer::open_buffer
// Access: Protected, Virtual // Access: Protected, Virtual
// Description: Opens the window right now. Called from the window // Description: Opens the window right now. Called from the window
// thread. Returns true if the window is successfully // thread. Returns true if the window is successfully
// opened, or false if there was a problem. // opened, or false if there was a problem.
//
// This particular version of open_buffer is also
// capable of reopening the buffer, which is necessary
// if the texture bindings, size, or cube face has
// changed. Caution: since this function is called
// at every cube-map switch, it needs to be reasonably
// fast in the case that little is changing.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
bool wglGraphicsBuffer:: bool glGraphicsBuffer::
open_buffer() { open_buffer() {
// Make sure the FBO is allocated.
// Figure out the desired size of the FBO // Check for support of relevant extensions.
int desired_x = _x_size; glGraphicsStateGuardian *glgsg;
int desired_y = _y_size; DCAST_INTO_R(glgsg, _gsg, false);
if (!_gsg->get_supports_tex_non_pow2()) { if ((!glgsg->_supports_framebuffer_object)||
desired_x = Texture::up_to_power_2(desired_x); (glgsg->_glDrawBuffers == 0)) {
desired_y = Texture::up_to_power_2(desired_y); return false;
} }
// Scan the textures list to see which textures should be attached. _is_valid = true;
Texture *attach_color = 0; return true;
Texture *attach_depth = 0;
Texture *attach_stencil = 0;
// For all slots containing textures, detach if:
// - they aren't supposed to be attached any more, or
// - there is a size mismatch, or
// - there is a cube-face mismatch
// For all renderbuffers, detach and deallocate if:
// - there is a size mismatch, or,
// - there is a texture to be attached at that point
// For all to-be-attached textures, attach if not already
// attached. During attachment process, resize and reformat
// if needed.
// For all unfilled slots, allocate and attach render buffers.
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: wglGraphicsBuffer::close_buffer // Function: glGraphicsBuffer::close_buffer
// Access: Protected, Virtual // Access: Protected, Virtual
// Description: Closes the buffer right now. Called from the window // Description: Closes the buffer right now. Called from the window
// thread. // thread.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void wglGraphicsBuffer:: void glGraphicsBuffer::
close_buffer() { close_buffer() {
// Detach all renderbuffers
// Detach all textures // Get the glgsg.
// Deallocate the FBO itself. glGraphicsStateGuardian *glgsg;
DCAST_INTO_R(glgsg, _gsg, false);
// Delete the renderbuffers.
for (int i=0; i<SLOT_COUNT; i++) {
if (_rb[i] != 0) {
glgsg->_glDeleteRenderbuffersEXT(1, &(_rb[i]));
_rb[i] = 0;
}
_tex[i] = 0;
}
_rb_size_x = 0;
_rb_size_y = 0;
// Delete the FBO itself.
nassertv(_fbo != 0, false);
glgsg->_glDeleteFramebuffersEXT(1, &_fbo);
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////

View File

@ -24,38 +24,38 @@
// Class : glGraphicsBuffer // Class : glGraphicsBuffer
// Description : An offscreen render buffer. // Description : An offscreen render buffer.
// //
// The glGraphicsBuffer can export its color buffer as a // The glGraphicsBuffer is based on the OpenGL
// texture. It can also export its depth buffer as a depth // EXT_framebuffer_object and ARB_draw_buffers extensions.
// texture and its stencil buffer as a stencil texture. // This design has three significant advantages over the
// Finally, it can have auxiliary buffers (additional // wglGraphicsBuffer and glxGraphicsBuffer.
// bitplanes above and beyond the usual set), which can
// also be exported as textures. This is the key advantage
// of the glGraphicsBuffer: it can render to many textures
// at the same time.
// //
// The glGraphicsBuffer shares a gsg with a host window. // As you might expect, this type of buffer can export
// If the host window is destroyed, the glGraphicsBuffer is // its color buffer as a texture. But it can also export
// lost as well. If desired, the glGraphicsBuffer can // its depth buffer, its stencil buffer, and any number
// track the size of the host window. // of auxiliary buffers. This is the biggest advantage:
// it can render to many textures at the same time.
// //
// The glGraphicsBuffer is implemented using the following // There is also a speed advantage. When using a
// OpenGL extensions: // glGraphicsBuffer, it is not necessary to call the
// extremely expensive wglMakeCurrent on buffer switches.
// //
// EXT_framebuffer_object // The glGraphicsBuffer can also track the size of a host
// ARB_draw_buffers // window, and automatically resize itself to match.
// //
// If any of these extensions is missing, then // If either of the necessary OpenGL extensions is not
// glGraphicsBuffer is not available (although it may // available, then the glGraphicsBuffer will not be
// still be possible to create a wglGraphicsBuffer or // available (although it may still be possible to
// glxGraphicsBuffer). // create a wglGraphicsBuffer or glxGraphicsBuffer).
// //
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
class EXPCL_GL CLP(GraphicsBuffer) : public GraphicsBuffer { class EXPCL_GL CLP(GraphicsBuffer) : public GraphicsBuffer {
public: public:
CLP(GraphicsBuffer)(GraphicsPipe *pipe, GraphicsStateGuardian *gsg, CLP(GraphicsBuffer)(GraphicsPipe *pipe,
const string &name, const string &name,
int x_size, int y_size); int x_size, int y_size, int flags,
GraphicsStateGuardian *gsg,
GraphicsOutput *host);
virtual ~CLP(GraphicsBuffer)(); virtual ~CLP(GraphicsBuffer)();
virtual bool begin_frame(FrameMode mode); virtual bool begin_frame(FrameMode mode);
@ -68,19 +68,24 @@ protected:
virtual bool open_buffer(); virtual bool open_buffer();
private: private:
PT(GraphicsOutput) _host;
bool _track_host_size;
int _fbo; void generate_mipmaps();
int _fbo_size_x; void rebuild_bitplanes();
int _fbo_size_y;
int _attached_cube_face; enum {
Texture *_attached_color; SLOT_color,
Texture *_attached_depth; SLOT_depth,
Texture *_attached_stencil; SLOT_stencil,
int _attached_color_rb; SLOT_COUNT
int _attached_depth_rb; };
int _attached_stencil_rb;
GLuint _fbo;
int _rb_size_x;
int _rb_size_y;
GLuint _rb[SLOT_COUNT];
PT(Texture) _tex[SLOT_COUNT];
GLenum _attach_point[SLOT_COUNT];
GLenum _slot_format[SLOT_COUNT];
public: public:
static TypeHandle get_class_type() { static TypeHandle get_class_type() {