diff --git a/panda/src/display/graphicsBuffer.cxx b/panda/src/display/graphicsBuffer.cxx index dab0ca905e..1fd967a9ce 100644 --- a/panda/src/display/graphicsBuffer.cxx +++ b/panda/src/display/graphicsBuffer.cxx @@ -43,11 +43,7 @@ GraphicsBuffer(GraphicsPipe *pipe, GraphicsStateGuardian *gsg, } if (want_texture) { - _texture = new Texture(); - _texture->set_name(_name); - _texture->set_wrapu(Texture::WM_clamp); - _texture->set_wrapv(Texture::WM_clamp); - _copy_texture = true; + setup_copy_texture(_name); } _x_size = x_size; diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 6e895ab51f..d3e12c25a2 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -256,11 +256,7 @@ make_buffer(GraphicsStateGuardian *gsg, const string &name, window->request_properties(props); if (want_texture) { - window->_texture = new Texture(); - window->_texture->set_name(name); - window->_texture->set_wrapu(Texture::WM_clamp); - window->_texture->set_wrapv(Texture::WM_clamp); - window->_copy_texture = true; + window->setup_copy_texture(name); } return window; @@ -309,10 +305,7 @@ make_parasite(GraphicsOutput *host, const string &name, props.set_fixed_size(true); props.set_title(name); window->request_properties(props); - - window->_texture = new Texture(); - window->_texture->set_name(name); - window->_copy_texture = true; + window->setup_copy_texture(name); return window; } diff --git a/panda/src/display/graphicsOutput.cxx b/panda/src/display/graphicsOutput.cxx index c7854ff1b4..6ca5fb69f2 100644 --- a/panda/src/display/graphicsOutput.cxx +++ b/panda/src/display/graphicsOutput.cxx @@ -686,6 +686,43 @@ declare_channel(int index, GraphicsChannel *chan) { _channels[index] = chan; } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsOutput::setup_copy_texture +// Access: Protected +// Description: Creates a new Texture object, suitable for copying +// the contents of this buffer into, and stores it in +// _texture. +//////////////////////////////////////////////////////////////////// +void GraphicsOutput:: +setup_copy_texture(const string &name) { + _texture = new Texture(); + _texture->set_name(name); + _texture->set_wrapu(Texture::WM_clamp); + _texture->set_wrapv(Texture::WM_clamp); + + // We should match the texture format up with the framebuffer + // format. Easier said than done! + if (_gsg != (GraphicsStateGuardian *)NULL) { + int mode = _gsg->get_properties().get_frame_buffer_mode(); + PixelBuffer *pb = _texture->_pbuffer; + + if (mode & FrameBufferProperties::FM_alpha) { + pb->set_format(PixelBuffer::F_rgba8); + pb->set_num_components(4); + pb->set_component_width(1); + pb->set_image_type(PixelBuffer::T_unsigned_byte); + + } else { + pb->set_format(PixelBuffer::F_rgb8); + pb->set_num_components(3); + pb->set_component_width(1); + pb->set_image_type(PixelBuffer::T_unsigned_byte); + } + } + + _copy_texture = true; +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsOutput::do_determine_display_regions // Access: Private diff --git a/panda/src/display/graphicsOutput.h b/panda/src/display/graphicsOutput.h index 4b9e8fcdf8..4fc8551389 100644 --- a/panda/src/display/graphicsOutput.h +++ b/panda/src/display/graphicsOutput.h @@ -145,6 +145,7 @@ public: protected: void declare_channel(int index, GraphicsChannel *chan); + void setup_copy_texture(const string &name); protected: PT(GraphicsStateGuardian) _gsg; diff --git a/panda/src/display/parasiteBuffer.cxx b/panda/src/display/parasiteBuffer.cxx index 2fd10ba3cd..b1317e5fa0 100644 --- a/panda/src/display/parasiteBuffer.cxx +++ b/panda/src/display/parasiteBuffer.cxx @@ -44,11 +44,7 @@ ParasiteBuffer(GraphicsOutput *host, const string &name, << " on " << _host->get_name() << "\n"; } - _texture = new Texture(); - _texture->set_name(_name); - _texture->set_wrapu(Texture::WM_clamp); - _texture->set_wrapv(Texture::WM_clamp); - _copy_texture = true; + setup_copy_texture(_name); _x_size = x_size; _y_size = y_size; diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index a0155a27ae..abf122a71c 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -302,6 +302,8 @@ reset() { _supports_bgr = has_extension("GL_EXT_bgra"); _supports_multisample = has_extension("GL_ARB_multisample"); + _supports_generate_mipmap = + has_extension("GL_SGIS_generate_mipmap") || is_at_least_version(1, 4); _supports_multitexture = false; @@ -1824,13 +1826,11 @@ TextureContext *CLP(GraphicsStateGuardian):: prepare_texture(Texture *tex) { CLP(TextureContext) *gtc = new CLP(TextureContext)(tex); GLP(GenTextures)(1, >c->_index); - //cerr << "preparing texture " << tex->get_name() << ", assigning " - // << gtc->_index << "\n"; bind_texture(gtc); GLP(PrioritizeTextures)(1, >c->_index, >c->_priority); specify_texture(tex); - apply_texture_immediate(tex); + apply_texture_immediate(gtc, tex); report_my_gl_errors(); return gtc; @@ -1844,20 +1844,22 @@ prepare_texture(Texture *tex) { //////////////////////////////////////////////////////////////////// void CLP(GraphicsStateGuardian):: apply_texture(TextureContext *tc) { - add_to_texture_record(tc); - bind_texture(tc); + CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); - int dirty = tc->get_dirty_flags(); + add_to_texture_record(gtc); + bind_texture(gtc); + + int dirty = gtc->get_dirty_flags(); if ((dirty & (Texture::DF_wrap | Texture::DF_filter)) != 0) { // We need to re-specify the texture properties. - specify_texture(tc->_texture); + specify_texture(gtc->_texture); } if ((dirty & (Texture::DF_image | Texture::DF_mipmap)) != 0) { // We need to re-apply the image. - apply_texture_immediate(tc->_texture); + apply_texture_immediate(gtc, gtc->_texture); } - tc->clear_dirty_flags(); + gtc->clear_dirty_flags(); report_my_gl_errors(); } @@ -3201,21 +3203,28 @@ bind_texture(TextureContext *tc) { void CLP(GraphicsStateGuardian):: specify_texture(Texture *tex) { GLP(TexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, - get_texture_wrap_mode(tex->get_wrapu())); + get_texture_wrap_mode(tex->get_wrapu())); GLP(TexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, - get_texture_wrap_mode(tex->get_wrapv())); + get_texture_wrap_mode(tex->get_wrapv())); + Texture::FilterType minfilter = tex->get_minfilter(); + Texture::FilterType magfilter = tex->get_magfilter(); + + bool uses_mipmaps = tex->uses_mipmaps(); + +#ifndef NDEBUG if (CLP(force_mipmaps)) { - GLP(TexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - GL_LINEAR_MIPMAP_LINEAR); - GLP(TexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - } else { - GLP(TexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, - get_texture_filter_type(tex->get_minfilter())); - GLP(TexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, - get_texture_filter_type(tex->get_magfilter())); + minfilter = Texture::FT_linear_mipmap_linear; + magfilter = Texture::FT_linear; + uses_mipmaps = true; } +#endif + + GLP(TexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, + get_texture_filter_type(tex->get_minfilter())); + GLP(TexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, + get_texture_filter_type(tex->get_magfilter())); + report_my_gl_errors(); } @@ -3292,20 +3301,22 @@ compute_gl_image_size(int xsize, int ysize, int external_format, int type) { // the texture has no image. //////////////////////////////////////////////////////////////////// bool CLP(GraphicsStateGuardian):: -apply_texture_immediate(Texture *tex) { +apply_texture_immediate(CLP(TextureContext) *gtc, Texture *tex) { PixelBuffer *pb = tex->get_ram_image(); if (pb == (PixelBuffer *)NULL) { return false; } - int xsize = pb->get_xsize(); - int ysize = pb->get_ysize(); + int width = pb->get_xsize(); + int height = pb->get_ysize(); - GLenum internal_format = get_internal_image_format(pb->get_format()); - GLenum external_format = get_external_image_format(pb->get_format()); + GLint internal_format = get_internal_image_format(pb->get_format()); + GLint external_format = get_external_image_format(pb->get_format()); GLenum type = get_image_type(pb->get_image_type()); PTA_uchar image = pb->_image; + nassertr(!image.empty(), false); + if (!_supports_bgr) { // If the GL doesn't claim to support BGR, we may have to reverse // the component ordering of the image. @@ -3314,7 +3325,7 @@ apply_texture_immediate(Texture *tex) { #ifndef NDEBUG int wanted_size = - compute_gl_image_size(xsize, ysize, external_format, type); + compute_gl_image_size(width, height, external_format, type); nassertr(wanted_size == (int)pb->_image.size(), false); #endif // NDEBUG @@ -3324,46 +3335,95 @@ apply_texture_immediate(Texture *tex) { GLCAT.debug() << "glTexImage2D(GL_TEXTURE_2D, " << (int)internal_format << ", " - << xsize << ", " << ysize << ", " + << width << ", " << height << ", " << pb->get_border() << ", " << (int)external_format << ", " << (int)type << ", " << tex->get_name() << ")\n"; #endif - if (!CLP(ignore_mipmaps) || CLP(force_mipmaps)) { - if (tex->uses_mipmaps() || CLP(force_mipmaps)) { -#ifndef NDEBUG - if (CLP(show_mipmaps)) { - build_phony_mipmaps(tex); - } else -#endif - { - GLUP(Build2DMipmaps)(GL_TEXTURE_2D, internal_format, - xsize, ysize, - external_format, type, image); -#ifndef NDEBUG - if (CLP(save_mipmaps)) { - save_mipmap_images(tex); - } -#endif - } - report_my_gl_errors(); + bool uses_mipmaps = tex->uses_mipmaps() && !CLP(ignore_mipmaps); +#ifndef NDEBUG + if (CLP(force_mipmaps)) { + uses_mipmaps = true; + } +#endif + + if (uses_mipmaps) { +#ifndef NDEBUG + if (CLP(show_mipmaps)) { + build_phony_mipmaps(tex); + report_my_gl_errors(); return true; + + } else +#endif + if (_supports_generate_mipmap) { + GLP(TexParameteri)(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + + } else { + // We only need to build the mipmaps by hand if the GL + // doesn't support generating them automatically. + GLUP(Build2DMipmaps)(GL_TEXTURE_2D, internal_format, + width, height, + external_format, type, image); + + gtc->_already_applied = false; + gtc->_internal_format = internal_format; + gtc->_width = width; + gtc->_height = height; + gtc->_border = 0; + +#ifndef NDEBUG + if (CLP(save_mipmaps)) { + save_mipmap_images(tex); + } +#endif + report_my_gl_errors(); + return true; + } + } else { + if (_supports_generate_mipmap) { + GLP(TexParameteri)(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); } } - nassertr(!pb->_image.empty(), false); - GLP(TexImage2D)(GL_TEXTURE_2D, 0, internal_format, - xsize, ysize, pb->get_border(), - external_format, type, image); + GLint border = pb->get_border(); + + if (!gtc->_already_applied || + gtc->_internal_format != internal_format || + gtc->_width != width || + gtc->_height != height || + gtc->_border != border) { + // We need to reload a new image. + GLP(TexImage2D)(GL_TEXTURE_2D, 0, internal_format, + width, height, pb->get_border(), + external_format, type, image); + gtc->_already_applied = true; + gtc->_internal_format = internal_format; + gtc->_width = width; + gtc->_height = height; + gtc->_border = border; + + } else { + // We can reload the image over the previous image, saving on + // texture memory fragmentation. + GLP(TexSubImage2D)(GL_TEXTURE_2D, 0, 0, 0, width, height, + external_format, type, image); + } //report_my_gl_errors(); // want to give explict error for texture creation failure GLenum error_code = GLP(GetError)(); - if(error_code != GL_NO_ERROR) { + if (error_code != GL_NO_ERROR) { const GLubyte *error_string = GLUP(ErrorString)(error_code); - GLCAT.error() << "GL texture creation failed for " << tex->get_name() << - ((error_string != (const GLubyte *)NULL) ? " : " : "") << endl; + GLCAT.error() + << "GL texture creation failed for " << tex->get_name(); + if (error_string != (const GLubyte *)NULL) { + GLCAT.error(false) + << " : " << error_string; + } + GLCAT.error(false) + << "\n"; } return true; @@ -3696,7 +3756,7 @@ get_image_type(PixelBuffer::Type type) { // Description: Maps from the PixelBuffer's Format symbols // to GL's. //////////////////////////////////////////////////////////////////// -GLenum CLP(GraphicsStateGuardian):: +GLint CLP(GraphicsStateGuardian):: get_external_image_format(PixelBuffer::Format format) { switch (format) { case PixelBuffer::F_color_index: @@ -3744,7 +3804,7 @@ get_external_image_format(PixelBuffer::Format format) { // Description: Maps from the PixelBuffer's Format symbols to a // suitable internal format for GL textures. //////////////////////////////////////////////////////////////////// -GLenum CLP(GraphicsStateGuardian):: +GLint CLP(GraphicsStateGuardian):: get_internal_image_format(PixelBuffer::Format format) { switch (format) { case PixelBuffer::F_rgba: @@ -4621,8 +4681,8 @@ build_phony_mipmap_level(int level, int xsize, int ysize) { GLenum type = get_image_type(pb->get_image_type()); GLP(TexImage2D)(GL_TEXTURE_2D, level, internal_format, - pb->get_xsize(), pb->get_ysize(), pb->get_border(), - external_format, type, pb->_image ); + pb->get_xsize(), pb->get_ysize(), pb->get_border(), + external_format, type, pb->_image ); delete pb; } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 1891d2d1ee..028d25c7be 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -222,7 +222,7 @@ protected: void bind_texture(TextureContext *tc); void specify_texture(Texture *tex); - bool apply_texture_immediate(Texture *tex); + bool apply_texture_immediate(CLP(TextureContext) *gtc, Texture *tex); void draw_texture(TextureContext *tc, const DisplayRegion *dr); void draw_texture(TextureContext *tc, const DisplayRegion *dr, @@ -234,8 +234,8 @@ protected: GLenum get_texture_wrap_mode(Texture::WrapMode wm); GLenum get_texture_filter_type(Texture::FilterType ft); GLenum get_image_type(PixelBuffer::Type type); - GLenum get_external_image_format(PixelBuffer::Format format); - GLenum get_internal_image_format(PixelBuffer::Format format); + GLint get_external_image_format(PixelBuffer::Format format); + GLint get_internal_image_format(PixelBuffer::Format format); GLint get_texture_apply_mode_type(TextureStage::Mode am) const; GLint get_texture_combine_type(TextureStage::CombineMode cm) const; GLint get_texture_src_type(TextureStage::CombineSource cs) const; @@ -330,6 +330,7 @@ protected: public: bool _supports_bgr; bool _supports_multisample; + bool _supports_generate_mipmap; bool _supports_multitexture; PFNGLACTIVETEXTUREPROC _glActiveTexture; diff --git a/panda/src/glstuff/glTextureContext_src.I b/panda/src/glstuff/glTextureContext_src.I index 1e1436d5a0..485d39411b 100644 --- a/panda/src/glstuff/glTextureContext_src.I +++ b/panda/src/glstuff/glTextureContext_src.I @@ -28,4 +28,6 @@ CLP(TextureContext)(Texture *tex) : { _index = 0; _priority = 0.5; // For keeping resident in texture memory + + _already_applied = false; } diff --git a/panda/src/glstuff/glTextureContext_src.h b/panda/src/glstuff/glTextureContext_src.h index ebf10d633a..4ab86f2acb 100644 --- a/panda/src/glstuff/glTextureContext_src.h +++ b/panda/src/glstuff/glTextureContext_src.h @@ -33,6 +33,15 @@ public: // This is a GL texture priority. GLfloat _priority; + // These are the parameters that we specified with the last + // glTexImage2D() call. If none of these have changed, we can + // reload the texture image with a glTexSubImage2D(). + bool _already_applied; + GLint _internal_format; + GLsizei _width; + GLsizei _height; + GLint _border; + public: static TypeHandle get_class_type() { return _type_handle;