|
|
@@ -24,14 +24,32 @@ TypeHandle CLP(GraphicsBuffer)::_type_handle;
|
|
|
// Description:
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
CLP(GraphicsBuffer)::
|
|
|
-CLP(GraphicsBuffer)(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
|
|
|
- const string &name,
|
|
|
- int x_size, int y_size) :
|
|
|
- GraphicsBuffer(pipe, gsg, name, x_size, y_size)
|
|
|
+CLP(GraphicsBuffer)(GraphicsPipe *pipe,
|
|
|
+ const string &name,
|
|
|
+ int x_size, int y_size, int flags,
|
|
|
+ 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
|
|
|
// same buffer we draw into.
|
|
|
_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
|
|
|
// Description: This function will be called within the draw thread
|
|
|
// before beginning rendering for a given frame. It
|
|
|
@@ -52,59 +70,232 @@ CLP(GraphicsBuffer)::
|
|
|
// if the frame should be rendered, or false if it
|
|
|
// should be skipped.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-bool wglGraphicsBuffer::
|
|
|
+bool glGraphicsBuffer::
|
|
|
begin_frame(FrameMode mode) {
|
|
|
PStatTimer timer(_make_current_pcollector);
|
|
|
-
|
|
|
- begin_frame_spam();
|
|
|
- if (_gsg == (GraphicsStateGuardian *)NULL) {
|
|
|
+ if (!_is_valid) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (!_host->begin_frame(FM_parasite)) {
|
|
|
return false;
|
|
|
}
|
|
|
+
|
|
|
+ // 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 (_track_host_size) {
|
|
|
+ if (_creation_flags & GraphicsPipe::BF_track_host_size) {
|
|
|
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 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
|
|
|
- // in the event that configuration parameters (ie, size, texture
|
|
|
- // bindings) have changed.
|
|
|
- open_window();
|
|
|
+ // Scan the textures list and determine what should be attached.
|
|
|
|
|
|
- // 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) {
|
|
|
- begin_render_texture();
|
|
|
- clear_cube_map_selection();
|
|
|
+ // If it's a not a 2D texture or a cube map, punt it.
|
|
|
+ if ((tex->get_texture_type() != Texture::TT_2d_texture)&&
|
|
|
+ (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: wglGraphicsBuffer::end_frame
|
|
|
+// 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: 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 wglGraphicsBuffer::
|
|
|
+void glGraphicsBuffer::
|
|
|
end_frame(FrameMode mode) {
|
|
|
end_frame_spam();
|
|
|
nassertv(_gsg != (GraphicsStateGuardian *)NULL);
|
|
|
|
|
|
if (mode == FM_render) {
|
|
|
- end_render_texture();
|
|
|
copy_to_textures();
|
|
|
}
|
|
|
|
|
|
// Unbind the FBO
|
|
|
+ glgsg->_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
|
|
|
|
|
|
+ if (mode == FM_render) {
|
|
|
+ generate_mipmaps();
|
|
|
+ }
|
|
|
+
|
|
|
_host->end_frame(FM_parasite);
|
|
|
|
|
|
if (mode == FM_render) {
|
|
|
@@ -127,68 +318,58 @@ end_frame(FrameMode mode) {
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void CLP(GraphicsBuffer)::
|
|
|
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
|
|
|
// 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.
|
|
|
-//
|
|
|
-// 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() {
|
|
|
- // Make sure the FBO is allocated.
|
|
|
-
|
|
|
- // Figure out the desired size of the FBO
|
|
|
- int desired_x = _x_size;
|
|
|
- int desired_y = _y_size;
|
|
|
- if (!_gsg->get_supports_tex_non_pow2()) {
|
|
|
- desired_x = Texture::up_to_power_2(desired_x);
|
|
|
- desired_y = Texture::up_to_power_2(desired_y);
|
|
|
- }
|
|
|
|
|
|
- // Scan the textures list to see which textures should be attached.
|
|
|
- Texture *attach_color = 0;
|
|
|
- Texture *attach_depth = 0;
|
|
|
- Texture *attach_stencil = 0;
|
|
|
-
|
|
|
+ // Check for support of relevant extensions.
|
|
|
+ glGraphicsStateGuardian *glgsg;
|
|
|
+ DCAST_INTO_R(glgsg, _gsg, false);
|
|
|
+ if ((!glgsg->_supports_framebuffer_object)||
|
|
|
+ (glgsg->_glDrawBuffers == 0)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
|
|
|
- // 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.
|
|
|
+ _is_valid = true;
|
|
|
+ return true;
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: wglGraphicsBuffer::close_buffer
|
|
|
+// Function: glGraphicsBuffer::close_buffer
|
|
|
// Access: Protected, Virtual
|
|
|
// Description: Closes the buffer right now. Called from the window
|
|
|
// thread.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-void wglGraphicsBuffer::
|
|
|
+void glGraphicsBuffer::
|
|
|
close_buffer() {
|
|
|
- // Detach all renderbuffers
|
|
|
- // Detach all textures
|
|
|
- // Deallocate the FBO itself.
|
|
|
+
|
|
|
+ // Get the glgsg.
|
|
|
+ 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);
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|