// Filename: glGraphicsBuffer_src.cxx // Created by: jyelon (15Jan06) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved // // All use of this software is subject to the terms of the Panda 3d // Software license. You should have received a copy of this license // along with this source code; you will also find a current copy of // the license at http://etc.cmu.edu/panda3d/docs/license/ . // // To contact the maintainers of this program write to // panda3d-general@lists.sourceforge.net . // TypeHandle CLP(GraphicsBuffer)::_type_handle; //////////////////////////////////////////////////////////////////// // Function: glGraphicsBuffer::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// CLP(GraphicsBuffer):: CLP(GraphicsBuffer)(GraphicsPipe *pipe, const string &name, const FrameBufferProperties &fb_prop, const WindowProperties &win_prop, int flags, GraphicsStateGuardian *gsg, GraphicsOutput *host) : GraphicsBuffer(pipe, name, fb_prop, win_prop, flags, gsg, host) { // An FBO doesn't have a back buffer. _draw_buffer_type = RenderBuffer::T_front; _screenshot_buffer_type = RenderBuffer::T_front; // Initialize these. _fbo = 0; _rb_size_x = 0; _rb_size_y = 0; _cube_face_active = 0; for (int i=0; ibegin_frame(FM_parasite, current_thread)) { return false; } // Figure out the desired size of the buffer. if (mode == FM_render) { rebuild_bitplanes(); clear_cube_map_selection(); if (!check_fbo()) { return false; } } _gsg->set_current_properties(&get_fb_properties()); return true; } //////////////////////////////////////////////////////////////////// // Function: glGraphicsBuffer::check_fbo // Access: Private // Description: Calls 'glCheckFramebufferStatus'. On error, // prints out an appropriate error message and unbinds // the fbo. Returns true for OK or false for error. //////////////////////////////////////////////////////////////////// bool CLP(GraphicsBuffer):: check_fbo() { CLP(GraphicsStateGuardian) *glgsg; DCAST_INTO_R(glgsg, _gsg, false); GLenum status = glgsg->_glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { GLCAT.error() << "EXT_framebuffer_object reports non-framebuffer-completeness.\n"; switch(status) { case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT\n"; break; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT\n"; break; case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT\n"; break; case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_FORMATS_EXT\n"; break; case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT\n"; break; case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: GLCAT.error() << "FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT\n"; break; case GL_FRAMEBUFFER_UNSUPPORTED_EXT: GLCAT.error() << "FRAMEBUFFER_UNSUPPORTED_EXT\n"; break; default: GLCAT.error() << "OTHER PROBLEM\n"; break; } glgsg->bind_fbo(0); return false; } return true; } //////////////////////////////////////////////////////////////////// // 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 CLP(GraphicsBuffer):: rebuild_bitplanes() { CLP(GraphicsStateGuardian) *glgsg; DCAST_INTO_V(glgsg, _gsg); // Bind the FBO if (_fbo == 0) { glgsg->_glGenFramebuffers(1, &_fbo); if (_fbo == 0) { glgsg->report_my_gl_errors(); return; } } glgsg->bind_fbo(_fbo); // Calculate bitplane size. This can be larger than the buffer. if (_creation_flags & GraphicsPipe::BF_size_track_host) { if ((_host->get_x_size() != _x_size)|| (_host->get_y_size() != _y_size)) { set_size_and_recalc(_host->get_x_size(), _host->get_y_size()); } } int bitplane_x = _x_size; int bitplane_y = _y_size; if (!glgsg->get_supports_tex_non_pow2()) { bitplane_x = Texture::up_to_power_2(bitplane_x); bitplane_y = Texture::up_to_power_2(bitplane_y); } bool rb_resize = false; if ((bitplane_x != _rb_size_x)|| (bitplane_y != _rb_size_y)) { _rb_size_x = bitplane_x; _rb_size_y = bitplane_y; rb_resize = true; } // These variables indicate what should be bound to each bitplane. Texture *attach[RTP_COUNT]; attach[RTP_color] = 0; attach[RTP_depth_stencil] = 0; for (int i=0; i<_fb_properties.get_aux_rgba(); i++) { attach[RTP_aux_rgba_0+i] = 0; } for (int i=0; i<_fb_properties.get_aux_hrgba(); i++) { attach[RTP_aux_hrgba_0+i] = 0; } for (int i=0; i<_fb_properties.get_aux_float(); i++) { attach[RTP_aux_float_0+i] = 0; } // Sort the textures list into appropriate slots. for (int i=0; iget_texture_type() != Texture::TT_2d_texture)&& (tex->get_texture_type() != Texture::TT_cube_map)) { _textures[i]._rtm_mode = RTM_copy_texture; continue; } // If I can't find an appropriate slot, or if there's // already a texture bound to this slot, then punt // this texture. if (attach[plane]) { _textures[i]._rtm_mode = RTM_copy_texture; continue; } // Assign the texture to this slot. attach[plane] = tex; } // For all slots, update the slot. bind_slot(rb_resize, attach, RTP_depth_stencil, GL_DEPTH_ATTACHMENT_EXT); bind_slot(rb_resize, attach, RTP_color, GL_COLOR_ATTACHMENT0_EXT); int next = GL_COLOR_ATTACHMENT1_EXT; for (int i=0; i<_fb_properties.get_aux_rgba(); i++) { bind_slot(rb_resize, attach, (RenderTexturePlane)(RTP_aux_rgba_0+i), next); next += 1; } for (int i=0; i<_fb_properties.get_aux_hrgba(); i++) { bind_slot(rb_resize, attach, (RenderTexturePlane)(RTP_aux_hrgba_0+i), next); next += 1; } for (int i=0; i<_fb_properties.get_aux_float(); i++) { bind_slot(rb_resize, attach, (RenderTexturePlane)(RTP_aux_float_0+i), next); next += 1; } _cube_face_active = 0; glgsg->report_my_gl_errors(); } //////////////////////////////////////////////////////////////////// // Function: glGraphicsBuffer::bind_slot // Access: Private // Description: Attaches either a texture or a renderbuffer to the // specified bitplane. //////////////////////////////////////////////////////////////////// void CLP(GraphicsBuffer):: bind_slot(bool rb_resize, Texture **attach, RenderTexturePlane slot, GLenum attachpoint) { CLP(GraphicsStateGuardian) *glgsg; DCAST_INTO_V(glgsg, _gsg); 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() == _rb_size_x)&& (tex->get_y_size() == _rb_size_y)) { return; } // Bind the texture to the slot. tex->set_x_size(_rb_size_x); tex->set_y_size(_rb_size_y); if (attachpoint == GL_DEPTH_ATTACHMENT_EXT) { tex->set_format(Texture::F_depth_stencil); TextureContext *tc = tex->prepare_now(glgsg->get_prepared_objects(), glgsg); nassertv(tc != (TextureContext *)NULL); CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); glgsg->apply_texture(tc); if (tex->get_texture_type() == Texture::TT_2d_texture) { glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, gtc->_index, 0); } else { glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, gtc->_index, 0); } if (_gsg->get_supports_depth_stencil()) { if (tex->get_texture_type() == Texture::TT_2d_texture) { glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, gtc->_index, 0); } else { glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, gtc->_index, 0); } } } else { tex->set_format(Texture::F_rgba); TextureContext *tc = tex->prepare_now(glgsg->get_prepared_objects(), glgsg); nassertv(tc != (TextureContext *)NULL); CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); glgsg->apply_texture(tc); if (tex->get_texture_type() == Texture::TT_2d_texture) { glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, attachpoint, GL_TEXTURE_2D, gtc->_index, 0); } else { glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, attachpoint, GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, gtc->_index, 0); } } _tex[slot] = tex; _attach_point[slot] = attachpoint; // 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_resize)) { return; } // If there's no renderbuffer for this slot, create one. if (_rb[slot] == 0) { glgsg->_glGenRenderbuffers(1, &(_rb[slot])); } // Allocate and bind the renderbuffer. glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, _rb[slot]); if (attachpoint == GL_DEPTH_ATTACHMENT_EXT) { if (_gsg->get_supports_depth_stencil()) { glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, _rb_size_x, _rb_size_y); glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, 0); glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, _rb[slot]); glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, _rb[slot]); } else { glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, _rb_size_x, _rb_size_y); glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, 0); glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, _rb[slot]); } } else { glgsg->_glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_RGBA, _rb_size_x, _rb_size_y); glgsg->_glBindRenderbuffer(GL_RENDERBUFFER_EXT, 0); glgsg->_glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, attachpoint, GL_RENDERBUFFER_EXT, _rb[slot]); } // Toss any texture that was connected to the slot. _tex[slot] = 0; _attach_point[slot] = attachpoint; } } //////////////////////////////////////////////////////////////////// // 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 CLP(GraphicsBuffer):: generate_mipmaps() { CLP(GraphicsStateGuardian) *glgsg; DCAST_INTO_V(glgsg, _gsg); for (int slot=0; slotuses_mipmaps())) { glgsg->_state._texture = 0; TextureContext *tc = tex->prepare_now(glgsg->get_prepared_objects(), glgsg); nassertv(tc != (TextureContext *)NULL); CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); GLenum target = glgsg->get_texture_target(tex->get_texture_type()); GLP(BindTexture)(target, gtc->_index); glgsg->_glGenerateMipmap(target); GLP(BindTexture)(target, 0); } } glgsg->report_my_gl_errors(); } //////////////////////////////////////////////////////////////////// // Function: glGraphicsBuffer::end_frame // Access: Public, Virtual // Description: This function will be called within the draw thread // after rendering is completed for a given frame. It // should do whatever finalization is required. //////////////////////////////////////////////////////////////////// void CLP(GraphicsBuffer):: end_frame(FrameMode mode, Thread *current_thread) { end_frame_spam(); nassertv(_gsg != (GraphicsStateGuardian *)NULL); if (mode == FM_render) { copy_to_textures(); } // Unbind the FBO CLP(GraphicsStateGuardian) *glgsg; DCAST_INTO_V(glgsg, _gsg); glgsg->bind_fbo(0); if (mode == FM_render) { generate_mipmaps(); } _host->end_frame(FM_parasite, current_thread); if (mode == FM_render) { trigger_flip(); if (_one_shot) { prepare_for_deletion(); } clear_cube_map_selection(); } glgsg->report_my_gl_errors(); } //////////////////////////////////////////////////////////////////// // Function: glGraphicsBuffer::select_cube_map // Access: Public, Virtual // Description: Called internally when the window is in // render-to-a-texture mode and we are in the process of // rendering the six faces of a cube map. This should // do whatever needs to be done to switch the buffer to // the indicated face. //////////////////////////////////////////////////////////////////// void CLP(GraphicsBuffer):: select_cube_map(int cube_map_index) { if (cube_map_index == _cube_face_active) { return; } _cube_face_active = cube_map_index; CLP(GraphicsStateGuardian) *glgsg; DCAST_INTO_V(glgsg, _gsg); for (int i=0; iget_texture_type() != Texture::TT_cube_map)) { continue; } TextureContext *tc = tex->prepare_now(glgsg->get_prepared_objects(), glgsg); nassertv(tc != (TextureContext *)NULL); CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, _attach_point[i], GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + cube_map_index, gtc->_index, 0); } } //////////////////////////////////////////////////////////////////// // Function: glGraphicsBuffer::open_buffer // Access: Protected, Virtual // Description: Opens the window right now. Called from the window // thread. Returns true if the window is successfully // opened, or false if there was a problem. //////////////////////////////////////////////////////////////////// bool CLP(GraphicsBuffer):: open_buffer() { // Double check that we have a host nassertr(_host != 0, false); // Check for support of relevant extensions. CLP(GraphicsStateGuardian) *glgsg; DCAST_INTO_R(glgsg, _gsg, false); if ((!glgsg->_supports_framebuffer_object)|| (glgsg->_glDrawBuffers == 0)) { return false; } // Describe the framebuffer properties of the FBO. // // The rule is that the properties should be as close // as possible to those requested, subject to the limits // of the implementation. This particular implementation // is fairly limited. But that's okay, we just have to // tell the truth about what we actually provide by setting // the _fb_properties accurately. _fb_properties.set_depth_bits(1); _fb_properties.set_color_bits(1); _fb_properties.set_alpha_bits(1); if (_gsg->get_supports_depth_stencil()) { _fb_properties.set_stencil_bits(1); } else { _fb_properties.set_stencil_bits(0); } _fb_properties.set_accum_bits(0); _fb_properties.set_multisamples(0); _fb_properties.set_back_buffers(0); _fb_properties.set_indexed_color(0); _fb_properties.set_rgb_color(1); _fb_properties.set_stereo(0); _fb_properties.set_force_hardware(_host->get_fb_properties().get_force_hardware()); _fb_properties.set_force_software(_host->get_fb_properties().get_force_software()); _is_valid = true; return true; } //////////////////////////////////////////////////////////////////// // Function: glGraphicsBuffer::close_buffer // Access: Protected, Virtual // Description: Closes the buffer right now. Called from the window // thread. //////////////////////////////////////////////////////////////////// void CLP(GraphicsBuffer):: close_buffer() { _active = false; if (_gsg == 0) { return; } // Get the glgsg. CLP(GraphicsStateGuardian) *glgsg; DCAST_INTO_V(glgsg, _gsg); // Delete the renderbuffers. for (int i=0; i_glDeleteRenderbuffers(1, &(_rb[i])); _rb[i] = 0; } _tex[i] = 0; } _rb_size_x = 0; _rb_size_y = 0; // Delete the FBO itself. if (_fbo != 0) { glgsg->_glDeleteFramebuffers(1, &_fbo); _fbo = 0; } // Release the Gsg _gsg.clear(); _is_valid = false; }