From 07f9f9d89722a82234e0d111e61b7ff98e88e2db Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 13 Jun 2023 13:46:37 +0200 Subject: [PATCH] (multiview) texture preparation changes: Multiview textures are now handled as a single texture context instead of as one texture context per view. This should be marginally more efficient, but more importantly, it paves the way for implementing multiview textures as subviews of an array texture down the line, which paves the way for hardware-accelerated stereo rendering (where the left and right views get rendered simultaneously to different layers of an array texture). The difference between a multiview texture done this way and an array texture is that the views of a multiview texture will still be treated as individual textures from the shader's point of view, only the views will be sharing the same storage on the GPU. This can be done by using a feature offered by graphics APIs that is (serendipitously) also called "texture views", where it's possible to create multiple texture objects that point to layer ranges of a bigger array texture. That part has not yet been done as doing this in OpenGL will be a little tricky due to texture views being an optional feature (so both paths would need to be implemented), but texture views are native in Vulkan (and D3D10+ for that matter), so doing this now allows me to implement multiview textures natively on the Vulkan end right from the start, and OpenGL can follow later if desired. Furthermore, this commit includes: - OpenGL: Overhaul texture creation and binding - OpenGL: Change how reloading a texture with mipmaps works - OpenGL: Use DSA in a couple of places - OpenGL ES: Add support for buffer textures - OpenGL: Remove support for bindless textures for now; this was never implemented in a very useful way, not used to my knowledge, and it is better to re-implement it later after the new shader pipeline lands - DirectX9: All views of a texture are uploaded at the same time - TinyDisplay: Support multiview textures natively (consecutive storage in memory) - General: Per-page texture update tracking is working properly now for multiview textures --- panda/src/display/graphicsStateGuardian.cxx | 2 +- panda/src/display/graphicsStateGuardian.h | 2 +- panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx | 39 +- panda/src/dxgsg9/dxGraphicsStateGuardian9.h | 4 +- panda/src/dxgsg9/dxShaderContext9.cxx | 4 +- panda/src/dxgsg9/dxTextureContext9.I | 17 +- panda/src/dxgsg9/dxTextureContext9.cxx | 750 +++++++------- panda/src/dxgsg9/dxTextureContext9.h | 19 +- panda/src/dxgsg9/wdxGraphicsBuffer9.cxx | 24 +- panda/src/gles2gsg/gles2gsg.h | 17 + panda/src/glstuff/glCgShaderContext_src.cxx | 52 +- panda/src/glstuff/glGeomMunger_src.cxx | 2 +- panda/src/glstuff/glGraphicsBuffer_src.cxx | 87 +- panda/src/glstuff/glGraphicsBuffer_src.h | 2 +- .../glstuff/glGraphicsStateGuardian_src.cxx | 933 ++++++++++-------- .../src/glstuff/glGraphicsStateGuardian_src.h | 22 +- panda/src/glstuff/glShaderContext_src.cxx | 145 +-- panda/src/glstuff/glShaderContext_src.h | 3 - panda/src/glstuff/glTextureContext_src.I | 40 +- panda/src/glstuff/glTextureContext_src.cxx | 130 +-- panda/src/glstuff/glTextureContext_src.h | 29 +- panda/src/glstuff/glmisc_src.cxx | 10 - panda/src/glstuff/glmisc_src.h | 1 - panda/src/gobj/preparedGraphicsObjects.cxx | 20 +- panda/src/gobj/preparedGraphicsObjects.h | 2 +- panda/src/gobj/texture.cxx | 236 ++--- panda/src/gobj/texture.h | 10 +- panda/src/gobj/textureContext.I | 20 +- panda/src/gobj/textureContext.h | 4 +- panda/src/gsgbase/graphicsStateGuardianBase.h | 2 +- .../tinydisplay/tinyGraphicsStateGuardian.cxx | 249 ++--- .../tinydisplay/tinyGraphicsStateGuardian.h | 18 +- panda/src/tinydisplay/tinyTextureContext.I | 6 +- panda/src/tinydisplay/tinyTextureContext.cxx | 8 + panda/src/tinydisplay/tinyTextureContext.h | 2 +- panda/src/tinydisplay/zbuffer.h | 2 +- panda/src/tinydisplay/zgl.h | 7 +- panda/src/wgldisplay/wglGraphicsBuffer.cxx | 4 +- 38 files changed, 1567 insertions(+), 1357 deletions(-) diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 795579fbc9..00672f6dbc 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -548,7 +548,7 @@ get_scene() const { * call Texture::prepare(). */ TextureContext *GraphicsStateGuardian:: -prepare_texture(Texture *, int view) { +prepare_texture(Texture *) { return nullptr; } diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 61797f954e..78869cabd6 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -289,7 +289,7 @@ PUBLISHED: MAKE_PROPERTY(scene, get_scene, set_scene); public: - virtual TextureContext *prepare_texture(Texture *tex, int view); + virtual TextureContext *prepare_texture(Texture *tex); virtual bool update_texture(TextureContext *tc, bool force); virtual void release_texture(TextureContext *tc); virtual void release_textures(const pvector &contexts); diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index 28c8e50281..90bd610455 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -189,8 +189,8 @@ DXGraphicsStateGuardian9:: * call Texture::prepare(). */ TextureContext *DXGraphicsStateGuardian9:: -prepare_texture(Texture *tex, int view) { - DXTextureContext9 *dtc = new DXTextureContext9(_prepared_objects, tex, view); +prepare_texture(Texture *tex) { + DXTextureContext9 *dtc = new DXTextureContext9(_prepared_objects, tex); if (!get_supports_compressed_texture_format(tex->get_ram_image_compression())) { dxgsg9_cat.error() @@ -206,7 +206,7 @@ prepare_texture(Texture *tex, int view) { * stage. */ void DXGraphicsStateGuardian9:: -apply_texture(int i, TextureContext *tc, const SamplerState &sampler) { +apply_texture(int i, TextureContext *tc, int view, const SamplerState &sampler) { if (tc == nullptr) { // The texture wasn't bound properly or something, so ensure texturing is // disabled and just return. @@ -296,7 +296,7 @@ apply_texture(int i, TextureContext *tc, const SamplerState &sampler) { float lod_bias = sampler.get_lod_bias(); set_sampler_state(i, D3DSAMP_MIPMAPLODBIAS, *(DWORD*)&lod_bias); - _d3d_device->SetTexture(i, dtc->get_d3d_texture()); + _d3d_device->SetTexture(i, dtc->get_d3d_texture(view)); } /** @@ -384,20 +384,10 @@ release_texture(TextureContext *tc) { */ bool DXGraphicsStateGuardian9:: extract_texture_data(Texture *tex) { - bool success = true; - - int num_views = tex->get_num_views(); - for (int view = 0; view < num_views; ++view) { - TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this); - nassertr(tc != nullptr, false); - DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc); - - if (!dtc->extract_texture_data(*_screen)) { - success = false; - } - } - - return success; + TextureContext *tc = tex->prepare_now(get_prepared_objects(), this); + nassertr(tc != nullptr, false); + DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc); + return dtc->extract_texture_data(*_screen); } /** @@ -1851,7 +1841,7 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, // must use a render target type texture for StretchRect tex->set_render_to_texture(true); - TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this); + TextureContext *tc = tex->prepare_now(get_prepared_objects(), this); if (tc == nullptr) { return false; } @@ -1868,10 +1858,11 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, // for now. return do_framebuffer_copy_to_ram(tex, view, z, dr, rb, true); } - nassertr(dtc->get_d3d_2d_texture() != nullptr, false); + IDirect3DTexture9 *d3d_2d_texture = dtc->get_d3d_2d_texture(view); + nassertr(d3d_2d_texture != nullptr, false); IDirect3DSurface9 *tex_level_0; - hr = dtc->get_d3d_2d_texture()->GetSurfaceLevel(0, &tex_level_0); + hr = d3d_2d_texture->GetSurfaceLevel(0, &tex_level_0); if (FAILED(hr)) { dxgsg9_cat.error() << "GetSurfaceLev failed in copy_texture" << D3DERRORSTRING(hr); return false; @@ -1896,7 +1887,7 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, << "Unable to re-create texture " << *dtc->get_texture() << endl; return false; } - hr = dtc->get_d3d_2d_texture()->GetSurfaceLevel(0, &tex_level_0); + hr = d3d_2d_texture->GetSurfaceLevel(0, &tex_level_0); if (FAILED(hr)) { dxgsg9_cat.error() << "GetSurfaceLev failed in copy_texture" << D3DERRORSTRING(hr); return false; @@ -3663,8 +3654,8 @@ update_standard_texture_bindings() { // We always reissue every stage in DX, just in case the texcoord index or // texgen mode or some other property has changed. int view = get_current_tex_view_offset() + stage->get_tex_view_offset(); - TextureContext *tc = texture->prepare_now(view, _prepared_objects, this); - apply_texture(si, tc, sampler); + TextureContext *tc = texture->prepare_now(_prepared_objects, this); + apply_texture(si, tc, view, sampler); set_texture_blend_mode(si, stage); int texcoord_dimensions = 2; diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h b/panda/src/dxgsg9/dxGraphicsStateGuardian9.h index 01fa8cf981..995dbc2442 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.h @@ -63,8 +63,8 @@ public: calc_fb_properties(DWORD cformat, DWORD dformat, DWORD multisampletype, DWORD multisamplequality); - virtual TextureContext *prepare_texture(Texture *tex, int view); - void apply_texture(int i, TextureContext *tc, const SamplerState &sampler); + virtual TextureContext *prepare_texture(Texture *tex); + void apply_texture(int i, TextureContext *tc, int view, const SamplerState &sampler); virtual bool update_texture(TextureContext *tc, bool force); bool upload_texture(DXTextureContext9 *dtc, bool force); virtual void release_texture(TextureContext *tc); diff --git a/panda/src/dxgsg9/dxShaderContext9.cxx b/panda/src/dxgsg9/dxShaderContext9.cxx index 53a4b6bac9..a4f3f7e24a 100644 --- a/panda/src/dxgsg9/dxShaderContext9.cxx +++ b/panda/src/dxgsg9/dxShaderContext9.cxx @@ -730,13 +730,13 @@ update_shader_texture_bindings(DXShaderContext9 *prev, GSG *gsg) { continue; } - TextureContext *tc = tex->prepare_now(view, gsg->_prepared_objects, gsg); + TextureContext *tc = tex->prepare_now(gsg->_prepared_objects, gsg); if (tc == nullptr) { continue; } int texunit = cgGetParameterResourceIndex(p); - gsg->apply_texture(texunit, tc, sampler); + gsg->apply_texture(texunit, tc, view, sampler); } } #endif diff --git a/panda/src/dxgsg9/dxTextureContext9.I b/panda/src/dxgsg9/dxTextureContext9.I index 6dbfea34a0..22a9d2443a 100644 --- a/panda/src/dxgsg9/dxTextureContext9.I +++ b/panda/src/dxgsg9/dxTextureContext9.I @@ -24,8 +24,9 @@ has_mipmaps() const { * texture it is. */ INLINE IDirect3DBaseTexture9 *DXTextureContext9:: -get_d3d_texture() const { - return _d3d_texture; +get_d3d_texture(int view) const { + view = (std::max)((std::min)(view, (int)_d3d_textures.size() - 1), 0); + return _d3d_textures[view]; } /** @@ -33,8 +34,8 @@ get_d3d_texture() const { * 1-d or 2-d texture. */ INLINE IDirect3DTexture9 *DXTextureContext9:: -get_d3d_2d_texture() const { - return _d3d_2d_texture; +get_d3d_2d_texture(int view) const { + return (IDirect3DTexture9 *)get_d3d_texture(view); } /** @@ -42,8 +43,8 @@ get_d3d_2d_texture() const { * 3-d texture. */ INLINE IDirect3DVolumeTexture9 *DXTextureContext9:: -get_d3d_volume_texture() const { - return _d3d_volume_texture; +get_d3d_volume_texture(int view) const { + return (IDirect3DVolumeTexture9 *)get_d3d_texture(view); } /** @@ -51,6 +52,6 @@ get_d3d_volume_texture() const { * cube map texture. */ INLINE IDirect3DCubeTexture9 *DXTextureContext9:: -get_d3d_cube_texture() const { - return _d3d_cube_texture; +get_d3d_cube_texture(int view) const { + return (IDirect3DCubeTexture9 *)get_d3d_texture(view); } diff --git a/panda/src/dxgsg9/dxTextureContext9.cxx b/panda/src/dxgsg9/dxTextureContext9.cxx index aedf120669..e8522f4167 100644 --- a/panda/src/dxgsg9/dxTextureContext9.cxx +++ b/panda/src/dxgsg9/dxTextureContext9.cxx @@ -36,18 +36,14 @@ static const DWORD g_LowByteMask = 0x000000FF; * */ DXTextureContext9:: -DXTextureContext9(PreparedGraphicsObjects *pgo, Texture *tex, int view) : - TextureContext(pgo, tex, view) { +DXTextureContext9(PreparedGraphicsObjects *pgo, Texture *tex) : + TextureContext(pgo, tex) { if (dxgsg9_cat.is_spam()) { dxgsg9_cat.spam() << "Creating DX texture [" << tex->get_name() << "], minfilter(" << tex->get_minfilter() << "), magfilter(" << tex->get_magfilter() << "), anisodeg(" << tex->get_anisotropic_degree() << ")\n"; } - _d3d_texture = nullptr; - _d3d_2d_texture = nullptr; - _d3d_volume_texture = nullptr; - _d3d_cube_texture = nullptr; _has_mipmaps = false; _is_render_target = false; _managed = -1; @@ -101,7 +97,7 @@ bool DXTextureContext9:: create_texture(DXScreenData &scrn) { // check if the texture has already been created - if (_d3d_2d_texture || _d3d_cube_texture || _d3d_volume_texture) { + if (!_d3d_textures.empty()) { // texture already created, no need to create return true; } @@ -146,21 +142,22 @@ create_texture(DXScreenData &scrn) { } } - switch (tex->get_texture_type()) { - case Texture::TT_1d_texture: - case Texture::TT_2d_texture: - case Texture::TT_cube_map: - // no compression for render target textures, or very small textures - if (!tex->get_render_to_texture() && - orig_width >= 4 && orig_height >= 4) { - if (texture_wants_compressed){ - compress_texture = true; - } + Texture::TextureType texture_type = tex->get_texture_type(); + switch (texture_type) { + case Texture::TT_1d_texture: + case Texture::TT_2d_texture: + case Texture::TT_cube_map: + // no compression for render target textures, or very small textures + if (!tex->get_render_to_texture() && orig_width >= 4 && orig_height >= 4) { + if (texture_wants_compressed){ + compress_texture = true; } - break; - case Texture::TT_3d_texture: - // compression of 3d textures not supported by all video chips - break; + } + break; + + case Texture::TT_3d_texture: + // compression of 3d textures not supported by all video chips + break; } if (texture_stored_compressed && !compress_texture) { @@ -259,7 +256,7 @@ create_texture(DXScreenData &scrn) { DWORD filter_caps; - switch (tex->get_texture_type()) { + switch (texture_type) { case Texture::TT_1d_texture: case Texture::TT_2d_texture: filter_caps = scrn._d3dcaps.TextureFilterCaps; @@ -353,7 +350,7 @@ create_texture(DXScreenData &scrn) { if (orig_width != target_width || orig_height != target_height || orig_depth != target_depth) { - if (tex->get_texture_type() == Texture::TT_3d_texture) { + if (texture_type == Texture::TT_3d_texture) { dxgsg9_cat.info() << "Reducing size of " << tex->get_name() << " from " << orig_width << "x" << orig_height << "x" << orig_depth @@ -970,17 +967,18 @@ create_texture(DXScreenData &scrn) { << "D3D create_texture ( ) unknown texture format\n"; } - int data_size; - data_size = target_width * target_height * target_depth; + int num_views = tex->get_num_views(); + + size_t data_size = target_width * target_height * target_depth; if (_has_mipmaps) { - data_size = (int) ((PN_stdfloat) data_size * 1.3333333); + data_size += data_size / 3; } - data_size = (int) ((PN_stdfloat) data_size * bytes_per_texel); - if (tex->get_texture_type() == Texture::TT_cube_map) { + data_size *= bytes_per_texel; + if (texture_type == Texture::TT_cube_map) { data_size *= 6; } - update_data_size_bytes(data_size); + update_data_size_bytes((size_t)data_size * (size_t)num_views); int attempts; @@ -991,14 +989,14 @@ create_texture(DXScreenData &scrn) { << " reported available.\n"; dxgsg9_cat.debug() << " size is " << target_width << " w * " << target_height << " h * " - << target_depth << " d"; + << target_depth << " d * " << num_views << " v"; if (_has_mipmaps) { dxgsg9_cat.debug(false) << " * 1.3333333 mipmaps"; } dxgsg9_cat.debug(false) << " * " << bytes_per_texel << " bpt"; - if (tex->get_texture_type() == Texture::TT_cube_map) { + if (texture_type == Texture::TT_cube_map) { dxgsg9_cat.debug(false) << " * 6 faces"; } @@ -1006,45 +1004,55 @@ create_texture(DXScreenData &scrn) { << "\n"; } - attempts = 0; - do - { - switch (tex->get_texture_type()) { - case Texture::TT_1d_texture: - case Texture::TT_2d_texture: - hr = scrn._d3d_device->CreateTexture - (target_width, target_height, mip_level_count, usage, - target_pixel_format, pool, &_d3d_2d_texture, nullptr); - _d3d_texture = _d3d_2d_texture; - break; + for (int view = 0; view < num_views; ++view) { + IDirect3DBaseTexture9 *d3d_texture = nullptr; + IDirect3DTexture9 *d3d_2d_texture; + IDirect3DVolumeTexture9 *d3d_volume_texture; + IDirect3DCubeTexture9 *d3d_cube_texture; - case Texture::TT_3d_texture: - hr = scrn._d3d_device->CreateVolumeTexture - (target_width, target_height, target_depth, mip_level_count, usage, - target_pixel_format, pool, &_d3d_volume_texture, nullptr); - _d3d_texture = _d3d_volume_texture; - break; + attempts = 0; + do { + switch (texture_type) { + case Texture::TT_1d_texture: + case Texture::TT_2d_texture: + hr = scrn._d3d_device->CreateTexture + (target_width, target_height, mip_level_count, usage, + target_pixel_format, pool, &d3d_2d_texture, nullptr); + d3d_texture = d3d_2d_texture; + break; - case Texture::TT_cube_map: - hr = scrn._d3d_device->CreateCubeTexture - (target_width, mip_level_count, usage, - target_pixel_format, pool, &_d3d_cube_texture, nullptr); - _d3d_texture = _d3d_cube_texture; + case Texture::TT_3d_texture: + hr = scrn._d3d_device->CreateVolumeTexture + (target_width, target_height, target_depth, mip_level_count, usage, + target_pixel_format, pool, &d3d_volume_texture, nullptr); + d3d_texture = d3d_volume_texture; + break; - target_height = target_width; - break; + case Texture::TT_cube_map: + hr = scrn._d3d_device->CreateCubeTexture + (target_width, mip_level_count, usage, + target_pixel_format, pool, &d3d_cube_texture, nullptr); + d3d_texture = d3d_cube_texture; + + target_height = target_width; + break; + } + + attempts++; + } + while (scrn._dxgsg9->check_dx_allocation(hr, data_size, attempts)); + + if (FAILED(hr)) { + dxgsg9_cat.error() + << "D3D create_texture failed!" << D3DERRORSTRING(hr); + dxgsg9_cat.error() + << " width = " << target_width << " height = " << target_height + << " target_pixel_format = " << target_pixel_format << "\n"; + + goto error_exit; } - attempts++; - } while (scrn._dxgsg9 -> check_dx_allocation (hr, data_size, attempts)); - - if (FAILED(hr)) { - dxgsg9_cat.error() - << "D3D create_texture failed!" << D3DERRORSTRING(hr); - dxgsg9_cat.error() - << " width = " << target_width << " height = " << target_height << " target_pixel_format = " << target_pixel_format << "\n"; - - goto error_exit; + _d3d_textures.push_back(d3d_texture); } if (DEBUG_TEXTURES && dxgsg9_cat.is_debug()) { @@ -1088,12 +1096,11 @@ create_texture(DXScreenData &scrn) { return true; - error_exit: - - RELEASE(_d3d_texture, dxgsg9, "texture", RELEASE_ONCE); - _d3d_2d_texture = nullptr; - _d3d_volume_texture = nullptr; - _d3d_cube_texture = nullptr; +error_exit: + for (IDirect3DBaseTexture9 *d3d_texture : _d3d_textures) { + RELEASE(d3d_texture, dxgsg9, "texture", RELEASE_ONCE); + } + _d3d_textures.clear(); return false; } @@ -1121,18 +1128,20 @@ create_simple_texture(DXScreenData &scrn) { int data_size = target_width * target_height * 4; + IDirect3DTexture9 *d3d_2d_texture = nullptr; hr = scrn._d3d_device->CreateTexture (target_width, target_height, mip_level_count, usage, - target_pixel_format, pool, &_d3d_2d_texture, nullptr); - _d3d_texture = _d3d_2d_texture; + target_pixel_format, pool, &d3d_2d_texture, nullptr); if (FAILED(hr)) { dxgsg9_cat.error() << "D3D create_simple_texture failed!" << D3DERRORSTRING(hr); dxgsg9_cat.error() - << " width = " << target_width << " height = " << target_height << " target_pixel_format = " << target_pixel_format << "\n"; + << " width = " << target_width << " height = " << target_height + << " target_pixel_format = " << target_pixel_format << "\n"; goto error_exit; } + _d3d_textures.push_back(d3d_2d_texture); if (DEBUG_TEXTURES && dxgsg9_cat.is_debug()) { dxgsg9_cat.debug() @@ -1144,10 +1153,10 @@ create_simple_texture(DXScreenData &scrn) { CPTA_uchar image = get_texture()->get_simple_ram_image(); hr = -1; - // hr = fill_d3d_texture_pixels(scrn); + // hr = fill_d3d_texture_pixels(view, scrn); IDirect3DSurface9 *surface = nullptr; - _d3d_2d_texture->GetSurfaceLevel(0, &surface); + d3d_2d_texture->GetSurfaceLevel(0, &surface); RECT source_size; source_size.left = source_size.top = 0; @@ -1176,11 +1185,11 @@ create_simple_texture(DXScreenData &scrn) { mark_loaded(); return true; - error_exit: - RELEASE(_d3d_texture, dxgsg9, "texture", RELEASE_ONCE); - _d3d_2d_texture = nullptr; - _d3d_volume_texture = nullptr; - _d3d_cube_texture = nullptr; +error_exit: + for (IDirect3DBaseTexture9 *d3d_texture : _d3d_textures) { + RELEASE(d3d_texture, dxgsg9, "texture", RELEASE_ONCE); + } + _d3d_textures.clear(); return false; } @@ -1189,16 +1198,15 @@ create_simple_texture(DXScreenData &scrn) { */ void DXTextureContext9:: delete_texture() { - - if (_d3d_texture == nullptr) { + if (_d3d_textures.empty()) { // don't bother printing the msg below, since we already released it. return; } - RELEASE(_d3d_texture, dxgsg9, "texture", RELEASE_ONCE); - _d3d_2d_texture = nullptr; - _d3d_volume_texture = nullptr; - _d3d_cube_texture = nullptr; + for (IDirect3DBaseTexture9 *d3d_texture : _d3d_textures) { + RELEASE(d3d_texture, dxgsg9, "texture", RELEASE_ONCE); + } + _d3d_textures.clear(); } /** @@ -1219,10 +1227,13 @@ extract_texture_data(DXScreenData &screen) { << "\n"; return state; } - nassertr(IS_VALID_PTR(_d3d_2d_texture), false); + nassertr(!_d3d_textures.empty(), false); + + //FIXME: support for multiview textures + IDirect3DTexture9 *d3d_2d_texture = get_d3d_2d_texture(0); D3DSURFACE_DESC desc; - hr = _d3d_2d_texture->GetLevelDesc(0, &desc); + hr = d3d_2d_texture->GetLevelDesc(0, &desc); if (FAILED(hr)) { dxgsg9_cat.error() << "Texture::GetLevelDesc() failed!" << D3DERRORSTRING(hr); @@ -1299,11 +1310,12 @@ extract_texture_data(DXScreenData &screen) { return state; } - int num_levels = _d3d_2d_texture->GetLevelCount(); + int num_levels = d3d_2d_texture->GetLevelCount(); tex->set_x_size(desc.Width); tex->set_y_size(desc.Height); tex->set_z_size(1); + tex->set_num_views(1); tex->set_component_type(Texture::T_unsigned_byte); tex->set_format(format); tex->clear_ram_image(); @@ -1315,7 +1327,7 @@ extract_texture_data(DXScreenData &screen) { source_surface = 0; destination_surface = 0; - hr = _d3d_2d_texture -> GetSurfaceLevel (0, &source_surface); + hr = d3d_2d_texture->GetSurfaceLevel(0, &source_surface); if (hr == D3D_OK) { D3DPOOL pool; @@ -1389,7 +1401,7 @@ extract_texture_data(DXScreenData &screen) { else { for (int n = 0; n < num_levels; ++n) { D3DLOCKED_RECT rect; - hr = _d3d_2d_texture->LockRect(n, &rect, nullptr, D3DLOCK_READONLY); + hr = d3d_2d_texture->LockRect(n, &rect, nullptr, D3DLOCK_READONLY); if (FAILED(hr)) { dxgsg9_cat.error() << "Texture::LockRect() failed! level = " << n << " " << D3DERRORSTRING(hr); @@ -1428,7 +1440,7 @@ extract_texture_data(DXScreenData &screen) { memcpy(image.p(), rect.pBits, size); } - _d3d_2d_texture->UnlockRect(n); + d3d_2d_texture->UnlockRect(n); if (n == 0) { tex->set_ram_image(image, compression); } else { @@ -1741,62 +1753,49 @@ static UINT calculate_row_byte_length (int width, int num_color_channels, D3DFOR * with texture data. Takes care of all necessary conversions and error * handling. */ -HRESULT DXTextureContext9::fill_d3d_texture_mipmap_pixels(int mip_level, int depth_index, D3DFORMAT source_format) -{ +HRESULT DXTextureContext9:: +fill_d3d_texture_mipmap_pixels(int mip_level, int depth_index, D3DFORMAT source_format) { // This whole function was refactored out of fill_d3d_texture_pixels to make // the code more readable and to avoid code duplication. - IDirect3DSurface9 *mip_surface = nullptr; - bool using_temp_buffer = false; HRESULT hr = E_FAIL; CPTA_uchar image = get_texture()->get_ram_mipmap_image(mip_level); - BYTE *pixels = (BYTE*) image.p(); - DWORD width = (DWORD) get_texture()->get_expected_mipmap_x_size(mip_level); - DWORD height = (DWORD) get_texture()->get_expected_mipmap_y_size(mip_level); + DWORD width = (DWORD)get_texture()->get_expected_mipmap_x_size(mip_level); + DWORD height = (DWORD)get_texture()->get_expected_mipmap_y_size(mip_level); + DWORD num_color_channels = get_texture()->get_num_components(); int component_width = get_texture()->get_component_width(); size_t page_size = get_texture()->get_expected_ram_mipmap_page_size(mip_level); size_t view_size; + size_t view_stride; vector_uchar clear_data; + BYTE *image_pixels; if (page_size > 0) { if (image.is_null()) { // Make an image, filled with the texture's clear color. image = get_texture()->make_ram_mipmap_image(mip_level); nassertr(!image.is_null(), E_FAIL); - pixels = (BYTE *)image.p(); } - view_size = image.size(); - pixels += view_size * get_view(); - pixels += page_size * depth_index; + image_pixels = (BYTE *)image.p(); + view_size = get_texture()->get_ram_mipmap_view_size(mip_level); + view_stride = view_size; + image_pixels += page_size * depth_index; } else { // This is a 0x0 texture, which gets loaded as though it were 1x1. width = 1; height = 1; clear_data = get_texture()->get_clear_data(); - pixels = clear_data.data(); + image_pixels = clear_data.data(); view_size = clear_data.size(); + view_stride = 0; } - - if (get_texture()->get_texture_type() == Texture::TT_cube_map) { - nassertr(IS_VALID_PTR(_d3d_cube_texture), E_FAIL); - hr = _d3d_cube_texture->GetCubeMapSurface((D3DCUBEMAP_FACES)depth_index, mip_level, &mip_surface); - } else { - nassertr(IS_VALID_PTR(_d3d_2d_texture), E_FAIL); - hr = _d3d_2d_texture->GetSurfaceLevel(mip_level, &mip_surface); - } - - if (FAILED(hr)) { - dxgsg9_cat.error() - << "FillDDTextureMipmapPixels failed for " << get_texture()->get_name() - << ", GetSurfaceLevel failed" << D3DERRORSTRING(hr); - return E_FAIL; - } + nassertr(IS_VALID_PTR(image_pixels), E_FAIL); RECT source_size; source_size.left = source_size.top = 0; source_size.right = width; source_size.bottom = height; - UINT source_row_byte_length = calculate_row_byte_length(width, get_texture()->get_num_components(), source_format); + UINT source_row_byte_length = calculate_row_byte_length(width, num_color_channels, source_format); DWORD mip_filter; // need filtering if size changes, (also if bitdepth reduced (need @@ -1807,87 +1806,128 @@ HRESULT DXTextureContext9::fill_d3d_texture_mipmap_pixels(int mip_level, int dep mip_filter |= D3DX_FILTER_SRGB; } - // D3DXLoadSurfaceFromMemory will load black luminance and we want full - // white, so convert to explicit luminance-alpha format - if (_d3d_format == D3DFMT_A8) { - // alloc buffer for explicit D3DFMT_A8L8 - USHORT *temp_buffer = new USHORT[width * height]; + // Preallocate temporary buffer for conversion + BYTE *temp_buffer = nullptr; + if (source_format == D3DFMT_A8 || component_width != 1) { + int num_pixels = width * height; + if (source_format == D3DFMT_A8) { + num_pixels *= 2; + source_format = D3DFMT_A8L8; + source_row_byte_length *= 2; + } else { + num_pixels *= num_color_channels; + } + temp_buffer = new BYTE[num_pixels]; if (!IS_VALID_PTR(temp_buffer)) { dxgsg9_cat.error() - << "FillDDTextureMipmapPixels couldnt alloc mem for temp pixbuf!\n"; - goto exit_FillMipmapSurf; + << "FillDDSurfaceTexturePixels couldnt alloc mem for temp pixbuf!\n"; + return E_FAIL; } - using_temp_buffer = true; - - USHORT *out_pixels = temp_buffer; - BYTE *source_pixels = pixels + component_width - 1; - for (UINT y = 0; y < height; y++) { - for (UINT x = 0; x < width; x++, source_pixels += component_width, out_pixels++) { - // add full white, which is our interpretation of alpha-only (similar - // to default adding full opaque alpha 0xFF to RGB-only textures) - *out_pixels = ((*source_pixels) << 8 ) | 0xFF; - } - } - - source_format = D3DFMT_A8L8; - source_row_byte_length = width * sizeof(USHORT); - pixels = (BYTE*)temp_buffer; - } - else if (component_width != 1) { - // Convert from 16-bit per channel (or larger) format down to 8-bit per - // channel. This throws away precision in the original image, but dx8 - // doesn't support high-precision images anyway. - - int num_components = get_texture()->get_num_components(); - int num_pixels = width * height * num_components; - BYTE *temp_buffer = new BYTE[num_pixels]; - if (!IS_VALID_PTR(temp_buffer)) { - dxgsg9_cat.error() << "FillDDTextureMipmapPixels couldnt alloc mem for temp pixbuf!\n"; - goto exit_FillMipmapSurf; - } - using_temp_buffer = true; - - BYTE *source_pixels = pixels + component_width - 1; - for (int i = 0; i < num_pixels; i++) { - temp_buffer[i] = *source_pixels; - source_pixels += component_width; - } - pixels = (BYTE*)temp_buffer; } // filtering may be done here if texture if targetsize != origsize + int num_views = (int)_d3d_textures.size(); + #ifdef DO_PSTATS - GraphicsStateGuardian::_data_transferred_pcollector.add_level(source_row_byte_length * height); + GraphicsStateGuardian::_data_transferred_pcollector.add_level(source_row_byte_length * height * num_views); #endif - if (source_format == D3DFMT_ATI1 || source_format == D3DFMT_ATI2) { - // These formats are not supported by D3DXLoadSurfaceFromMemory. - D3DLOCKED_RECT rect; - _d3d_2d_texture->LockRect(mip_level, &rect, 0, D3DLOCK_DISCARD); - unsigned char *dest = (unsigned char *)rect.pBits; - memcpy(dest, pixels, view_size); + for (int view = 0; view < num_views; ++view) { + BYTE *pixels = image_pixels; + image_pixels += view_stride; - _d3d_2d_texture->UnlockRect(mip_level); + if (source_format == D3DFMT_ATI1 || source_format == D3DFMT_ATI2) { + // These formats are not supported by D3DXLoadSurfaceFromMemory. + D3DLOCKED_RECT rect; - } else { - hr = D3DXLoadSurfaceFromMemory - (mip_surface, nullptr, nullptr, (LPCVOID)pixels, - source_format, source_row_byte_length, nullptr, - &source_size, mip_filter, (D3DCOLOR)0x0); - if (FAILED(hr)) { - dxgsg9_cat.error() - << "FillDDTextureMipmapPixels failed for " << get_texture()->get_name() - << ", mip_level " << mip_level - << ", D3DXLoadSurfFromMem failed" << D3DERRORSTRING(hr); + if (get_texture()->get_texture_type() == Texture::TT_cube_map) { + IDirect3DCubeTexture9 *d3d_cube_texture = get_d3d_cube_texture(view); + nassertr(IS_VALID_PTR(d3d_cube_texture), E_FAIL); + d3d_cube_texture->LockRect((D3DCUBEMAP_FACES)depth_index, mip_level, &rect, 0, D3DLOCK_DISCARD); + + unsigned char *dest = (unsigned char *)rect.pBits; + memcpy(dest, pixels, view_size); + + d3d_cube_texture->UnlockRect((D3DCUBEMAP_FACES)depth_index, mip_level); + } else { + IDirect3DTexture9 *d3d_2d_texture = get_d3d_2d_texture(view); + nassertr(IS_VALID_PTR(d3d_2d_texture), E_FAIL); + d3d_2d_texture->LockRect(mip_level, &rect, 0, D3DLOCK_DISCARD); + + unsigned char *dest = (unsigned char *)rect.pBits; + memcpy(dest, pixels, view_size); + + d3d_2d_texture->UnlockRect(mip_level); + } + } + else { + if (_d3d_format == D3DFMT_A8 && source_format == D3DFMT_A8L8) { + USHORT *out_pixels = (USHORT *)temp_buffer; + BYTE *source_pixels = pixels + component_width - 1; + for (UINT y = 0; y < height; y++) { + for (UINT x = 0; x < width; x++, source_pixels += component_width, out_pixels++) { + // add full white, which is our interpretation of alpha-only + // (similar to default adding full opaque alpha 0xFF to RGB-only + // textures) + *out_pixels = ((*source_pixels) << 8) | 0xFF; + } + } + pixels = (BYTE *)temp_buffer; + } + else if (component_width != 1) { + // Convert from 16-bit per channel (or larger) format down to 8-bit per + // channel. This throws away precision in the original image, but dx8 + // doesn't support high-precision images anyway. + + int num_components = get_texture()->get_num_components(); + int num_pixels = width * height * num_components; + + BYTE *source_pixels = pixels + component_width - 1; + for (int i = 0; i < num_pixels; i++) { + temp_buffer[i] = *source_pixels; + source_pixels += component_width; + } + pixels = (BYTE *)temp_buffer; + } + + IDirect3DSurface9 *mip_surface = nullptr; + if (get_texture()->get_texture_type() == Texture::TT_cube_map) { + IDirect3DCubeTexture9 *d3d_cube_texture = get_d3d_cube_texture(view); + nassertr(IS_VALID_PTR(d3d_cube_texture), E_FAIL); + hr = d3d_cube_texture->GetCubeMapSurface((D3DCUBEMAP_FACES)depth_index, mip_level, &mip_surface); + } else { + IDirect3DTexture9 *d3d_2d_texture = get_d3d_2d_texture(view); + nassertr(IS_VALID_PTR(d3d_2d_texture), E_FAIL); + hr = d3d_2d_texture->GetSurfaceLevel(mip_level, &mip_surface); + } + + if (FAILED(hr)) { + dxgsg9_cat.error() + << "FillDDTextureMipmapPixels failed for " << get_texture()->get_name() + << ", view " << view << ", mip_level " << mip_level + << ", GetSurfaceLevel failed" << D3DERRORSTRING(hr); + return E_FAIL; + } + + hr = D3DXLoadSurfaceFromMemory + (mip_surface, nullptr, nullptr, (LPCVOID)pixels, + source_format, source_row_byte_length, nullptr, + &source_size, mip_filter, (D3DCOLOR)0x0); + if (FAILED(hr)) { + dxgsg9_cat.error() + << "FillDDTextureMipmapPixels failed for " << get_texture()->get_name() + << ", view " << view << ", mip_level " << mip_level + << ", D3DXLoadSurfFromMem failed" << D3DERRORSTRING(hr); + } + + RELEASE(mip_surface, dxgsg9, "FillDDTextureMipmapPixels MipSurface texture ptr", RELEASE_ONCE); } } -exit_FillMipmapSurf: - if (using_temp_buffer) { - SAFE_DELETE_ARRAY(pixels); + if (temp_buffer != nullptr) { + delete[] temp_buffer; } - RELEASE(mip_surface, dxgsg9, "FillDDTextureMipmapPixels MipSurface texture ptr", RELEASE_ONCE); return hr; } @@ -1899,7 +1939,8 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) { IDirect3DDevice9 *device = scrn._d3d_device; Texture *tex = get_texture(); nassertr(IS_VALID_PTR(tex), E_FAIL); - if (tex->get_texture_type() == Texture::TT_3d_texture) { + Texture::TextureType texture_type = tex->get_texture_type(); + if (texture_type == Texture::TT_3d_texture) { return fill_d3d_volume_texture_pixels(scrn); } @@ -1924,14 +1965,15 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) { if (image.is_null()) { // The texture doesn't have an image to load. That's ok; it might be a // texture we've rendered to by frame buffer operations or something. - if (tex->get_render_to_texture()) { + if (tex->get_render_to_texture() && texture_type != Texture::TT_cube_map) { HRESULT result; - if (_d3d_2d_texture) { + for (int view = 0; view < (int)_d3d_textures.size(); ++view) { + IDirect3DTexture9 *d3d_2d_texture = get_d3d_2d_texture(view); + // clear render to texture IDirect3DSurface9 *surface; - - result = _d3d_2d_texture -> GetSurfaceLevel (0, &surface); + result = d3d_2d_texture->GetSurfaceLevel(0, &surface); if (result == D3D_OK) { D3DSURFACE_DESC surface_description; @@ -1981,7 +2023,6 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) { } } //nassertr(IS_VALID_PTR((BYTE*)image.p()), E_FAIL); - nassertr(IS_VALID_PTR(_d3d_texture), E_FAIL); PStatTimer timer(GraphicsStateGuardian::_load_texture_pcollector); @@ -2017,6 +2058,7 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) { break; } + int num_views = (int)_d3d_textures.size(); for (unsigned int di = 0; di < orig_depth; di++) { // fill top level mipmap @@ -2028,7 +2070,7 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) { if (_has_mipmaps) { // if we have pre-calculated mipmap levels, use them, otherwise generate // on the fly - int miplevel_count = _d3d_texture->GetLevelCount(); + int miplevel_count = _d3d_textures[0]->GetLevelCount(); if (miplevel_count <= tex->get_num_loadable_ram_mipmap_images()) { if (dxgsg9_cat.is_debug()) { dxgsg9_cat.debug() @@ -2041,45 +2083,51 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) { return hr; // error message was already output in fill_d3d_texture_mipmap_pixels } } + + return D3D_OK; } else { // mipmaps need to be generated, either use autogen or d3dx functions + for (int view = 0; view < num_views; ++view) { + IDirect3DBaseTexture9 *d3d_texture = get_d3d_texture(view); + nassertr(IS_VALID_PTR(d3d_texture), E_FAIL); - if (_managed == false && scrn._supports_automatic_mipmap_generation) { - if (false) - { - // hr = _d3d_texture -> SetAutoGenFilterType - // (D3DTEXF_PYRAMIDALQUAD); hr = _d3d_texture -> - // SetAutoGenFilterType (D3DTEXF_GAUSSIANQUAD); hr = _d3d_texture - // -> SetAutoGenFilterType (D3DTEXF_ANISOTROPIC); - hr = _d3d_texture -> SetAutoGenFilterType (D3DTEXF_LINEAR); - if (FAILED(hr)) { - dxgsg9_cat.error() << "SetAutoGenFilterType failed " << D3DERRORSTRING(hr); + if (_managed == false && scrn._supports_automatic_mipmap_generation) { + if (false) { + IDirect3DBaseTexture9 *d3d_texture = get_d3d_texture(view); + nassertr(IS_VALID_PTR(d3d_texture), E_FAIL); + //hr = d3d_texture->SetAutoGenFilterType(D3DTEXF_PYRAMIDALQUAD); + //hr = d3d_texture->SetAutoGenFilterType(D3DTEXF_GAUSSIANQUAD); + //hr = d3d_texture->SetAutoGenFilterType(D3DTEXF_ANISOTROPIC); + hr = d3d_texture->SetAutoGenFilterType(D3DTEXF_LINEAR); + if (FAILED(hr)) { + dxgsg9_cat.error() << "SetAutoGenFilterType failed " << D3DERRORSTRING(hr); + } + + d3d_texture->GenerateMipSubLevels(); + } + } + else { + DWORD mip_filter_flags; + if (!dx_use_triangle_mipgen_filter) { + mip_filter_flags = D3DX_FILTER_BOX; + } else { + mip_filter_flags = D3DX_FILTER_TRIANGLE; } - _d3d_texture -> GenerateMipSubLevels ( ); - } - } - else { - DWORD mip_filter_flags; - if (!dx_use_triangle_mipgen_filter) { - mip_filter_flags = D3DX_FILTER_BOX; - } else { - mip_filter_flags = D3DX_FILTER_TRIANGLE; - } + if (Texture::is_srgb(tex->get_format())) { + mip_filter_flags |= D3DX_FILTER_SRGB; + } - if (Texture::is_srgb(tex->get_format())) { - mip_filter_flags |= D3DX_FILTER_SRGB; - } + // mip_filter_flags |= D3DX_FILTER_DITHER; + hr = D3DXFilterTexture(d3d_texture, nullptr, 0, mip_filter_flags); - // mip_filter_flags |= D3DX_FILTER_DITHER; - hr = D3DXFilterTexture(_d3d_texture, nullptr, 0, - mip_filter_flags); - - if (FAILED(hr)) { - dxgsg9_cat.error() - << "FillDDSurfaceTexturePixels failed for " << tex->get_name() - << ", D3DXFilterTex failed" << D3DERRORSTRING(hr); + if (FAILED(hr)) { + dxgsg9_cat.error() + << "FillDDSurfaceTexturePixels failed for " << tex->get_name() + << " view " << view << ", D3DXFilterTex failed" + << D3DERRORSTRING(hr); + } } } } @@ -2126,159 +2174,167 @@ fill_d3d_volume_texture_pixels(DXScreenData &scrn) { PStatTimer timer(GraphicsStateGuardian::_load_texture_pcollector); - nassertr(IS_VALID_PTR(_d3d_texture), E_FAIL); + nassertr(!_d3d_textures.empty(), E_FAIL); nassertr(tex->get_texture_type() == Texture::TT_3d_texture, E_FAIL); - DWORD orig_width = (DWORD) tex->get_x_size(); - DWORD orig_height = (DWORD) tex->get_y_size(); - DWORD orig_depth = (DWORD) tex->get_z_size(); + DWORD orig_width = (DWORD)tex->get_x_size(); + DWORD orig_height = (DWORD)tex->get_y_size(); + DWORD orig_depth = (DWORD)tex->get_z_size(); DWORD num_color_channels = tex->get_num_components(); - D3DFORMAT source_format = _d3d_format; - BYTE *image_pixels = (BYTE*)image.p(); int component_width = tex->get_component_width(); - nassertr(IS_VALID_PTR(image_pixels), E_FAIL); - - size_t view_size = tex->get_ram_mipmap_view_size(0); - image_pixels += view_size * get_view(); - - IDirect3DVolume9 *mip_level_0 = nullptr; - bool using_temp_buffer = false; - BYTE *pixels = image_pixels; - - nassertr(IS_VALID_PTR(_d3d_volume_texture), E_FAIL); - hr = _d3d_volume_texture->GetVolumeLevel(0, &mip_level_0); - - if (FAILED(hr)) { - dxgsg9_cat.error() - << "FillDDSurfaceTexturePixels failed for " << tex->get_name() - << ", GetSurfaceLevel failed" << D3DERRORSTRING(hr); - return E_FAIL; - } - - D3DBOX source_size; - source_size.Left = source_size.Top = source_size.Front = 0; - source_size.Right = orig_width; - source_size.Bottom = orig_height; - source_size.Back = orig_depth; - + D3DFORMAT source_format = _d3d_format; UINT source_row_byte_length = orig_width * num_color_channels; UINT source_page_byte_length = orig_height * source_row_byte_length; - DWORD level_0_filter, mip_filter_flags; - using_temp_buffer = false; - - // need filtering if size changes, (also if bitdepth reduced (need - // dithering)??) - level_0_filter = D3DX_FILTER_LINEAR ; //| D3DX_FILTER_DITHER; //dithering looks ugly on i810 for 4444 textures - - if (Texture::is_srgb(tex->get_format())) { - level_0_filter |= D3DX_FILTER_SRGB; - } - - // D3DXLoadSurfaceFromMemory will load black luminance and we want full - // white, so convert to explicit luminance-alpha format - if (_d3d_format == D3DFMT_A8) { - // alloc buffer for explicit D3DFMT_A8L8 - USHORT *temp_buffer = new USHORT[orig_width * orig_height * orig_depth]; + // Preallocate temporary buffer for conversion + BYTE *temp_buffer = nullptr; + if (_d3d_format == D3DFMT_A8 || component_width != 1) { + int num_pixels = orig_width * orig_height * orig_depth; + if (_d3d_format == D3DFMT_A8) { + num_pixels *= 2; + source_format = D3DFMT_A8L8; + source_row_byte_length *= 2; + source_page_byte_length *= 2; + } else { + num_pixels *= num_color_channels; + } + temp_buffer = new BYTE[num_pixels]; if (!IS_VALID_PTR(temp_buffer)) { dxgsg9_cat.error() << "FillDDSurfaceTexturePixels couldnt alloc mem for temp pixbuf!\n"; - goto exit_FillDDSurf; + return E_FAIL; } - using_temp_buffer = true; - - USHORT *out_pixels = temp_buffer; - BYTE *source_pixels = pixels + component_width - 1; - for (UINT z = 0; z < orig_depth; z++) { - for (UINT y = 0; y < orig_height; y++) { - for (UINT x = 0; - x < orig_width; - x++, source_pixels += component_width, out_pixels++) { - // add full white, which is our interpretation of alpha-only - // (similar to default adding full opaque alpha 0xFF to RGB-only - // textures) - *out_pixels = ((*source_pixels) << 8 ) | 0xFF; - } - } - } - - source_format = D3DFMT_A8L8; - source_row_byte_length = orig_width * sizeof(USHORT); - source_page_byte_length = orig_height * source_row_byte_length; - pixels = (BYTE*)temp_buffer; - - } else if (component_width != 1) { - // Convert from 16-bit per channel (or larger) format down to 8-bit per - // channel. This throws away precision in the original image, but dx8 - // doesn't support high-precision images anyway. - - int num_components = tex->get_num_components(); - int num_pixels = orig_width * orig_height * orig_depth * num_components; - BYTE *temp_buffer = new BYTE[num_pixels]; - if (!IS_VALID_PTR(temp_buffer)) { - dxgsg9_cat.error() << "FillDDSurfaceTexturePixels couldnt alloc mem for temp pixbuf!\n"; - goto exit_FillDDSurf; - } - using_temp_buffer = true; - - BYTE *source_pixels = pixels + component_width - 1; - for (int i = 0; i < num_pixels; i++) { - temp_buffer[i] = *source_pixels; - source_pixels += component_width; - } - pixels = (BYTE*)temp_buffer; } + BYTE *image_pixels = (BYTE *)image.p(); + nassertr(IS_VALID_PTR(image_pixels), E_FAIL); + + size_t view_size = tex->get_ram_mipmap_view_size(0); + int num_views = (int)_d3d_textures.size(); - // filtering may be done here if texture if targetsize != origsize #ifdef DO_PSTATS - GraphicsStateGuardian::_data_transferred_pcollector.add_level(source_page_byte_length * orig_depth); + GraphicsStateGuardian::_data_transferred_pcollector.add_level(source_page_byte_length * orig_depth * num_views); #endif - hr = D3DXLoadVolumeFromMemory - (mip_level_0, nullptr, nullptr, (LPCVOID)pixels, - source_format, source_row_byte_length, source_page_byte_length, - nullptr, - &source_size, level_0_filter, (D3DCOLOR)0x0); - if (FAILED(hr)) { - dxgsg9_cat.error() - << "FillDDSurfaceTexturePixels failed for " << tex->get_name() - << ", D3DXLoadVolumeFromMem failed" << D3DERRORSTRING(hr); - goto exit_FillDDSurf; - } - if (_has_mipmaps) { - if (!dx_use_triangle_mipgen_filter) { - mip_filter_flags = D3DX_FILTER_BOX; - } else { - mip_filter_flags = D3DX_FILTER_TRIANGLE; - } + bool success = true; - if (Texture::is_srgb(tex->get_format())) { - mip_filter_flags |= D3DX_FILTER_SRGB; - } + for (int view = 0; view < num_views; ++view) { + IDirect3DVolumeTexture9 *d3d_volume_texture = get_d3d_volume_texture(view); + nassertr(IS_VALID_PTR(d3d_volume_texture), E_FAIL); - // mip_filter_flags| = D3DX_FILTER_DITHER; + IDirect3DVolume9 *mip_level_0 = nullptr; + BYTE *pixels = image_pixels; + image_pixels += view_size; - hr = D3DXFilterTexture(_d3d_texture, nullptr, 0, - mip_filter_flags); + hr = d3d_volume_texture->GetVolumeLevel(0, &mip_level_0); if (FAILED(hr)) { dxgsg9_cat.error() << "FillDDSurfaceTexturePixels failed for " << tex->get_name() - << ", D3DXFilterTex failed" << D3DERRORSTRING(hr); - goto exit_FillDDSurf; + << ", GetSurfaceLevel failed" << D3DERRORSTRING(hr); + success = false; + continue; } + + D3DBOX source_size; + source_size.Left = source_size.Top = source_size.Front = 0; + source_size.Right = orig_width; + source_size.Bottom = orig_height; + source_size.Back = orig_depth; + + DWORD level_0_filter, mip_filter_flags; + + // need filtering if size changes, (also if bitdepth reduced (need + // dithering)??) + level_0_filter = D3DX_FILTER_LINEAR ; //| D3DX_FILTER_DITHER; //dithering looks ugly on i810 for 4444 textures + + if (Texture::is_srgb(tex->get_format())) { + level_0_filter |= D3DX_FILTER_SRGB; + } + + // D3DXLoadSurfaceFromMemory will load black luminance and we want full + // white, so convert to explicit luminance-alpha format + if (_d3d_format == D3DFMT_A8 && source_format == D3DFMT_A8L8) { + USHORT *out_pixels = (USHORT *)temp_buffer; + BYTE *source_pixels = pixels + component_width - 1; + for (UINT z = 0; z < orig_depth; z++) { + for (UINT y = 0; y < orig_height; y++) { + for (UINT x = 0; + x < orig_width; + x++, source_pixels += component_width, out_pixels++) { + // add full white, which is our interpretation of alpha-only + // (similar to default adding full opaque alpha 0xFF to RGB-only + // textures) + *out_pixels = ((*source_pixels) << 8) | 0xFF; + } + } + } + + pixels = (BYTE *)temp_buffer; + } + else if (component_width != 1) { + // Convert from 16-bit per channel (or larger) format down to 8-bit per + // channel. This throws away precision in the original image, but dx8 + // doesn't support high-precision images anyway. + + int num_components = tex->get_num_components(); + int num_pixels = orig_width * orig_height * orig_depth * num_components; + + BYTE *source_pixels = pixels + component_width - 1; + for (int i = 0; i < num_pixels; i++) { + temp_buffer[i] = *source_pixels; + source_pixels += component_width; + } + pixels = (BYTE *)temp_buffer; + } + + // filtering may be done here if texture if targetsize != origsize + hr = D3DXLoadVolumeFromMemory + (mip_level_0, nullptr, nullptr, (LPCVOID)pixels, + source_format, source_row_byte_length, source_page_byte_length, + nullptr, + &source_size, level_0_filter, (D3DCOLOR)0x0); + if (FAILED(hr)) { + dxgsg9_cat.error() + << "FillDDSurfaceTexturePixels failed for " << tex->get_name() + << " view " << view << ", D3DXLoadVolumeFromMem failed" << D3DERRORSTRING(hr); + success = false; + continue; + } + + if (_has_mipmaps) { + if (!dx_use_triangle_mipgen_filter) { + mip_filter_flags = D3DX_FILTER_BOX; + } else { + mip_filter_flags = D3DX_FILTER_TRIANGLE; + } + + if (Texture::is_srgb(tex->get_format())) { + mip_filter_flags |= D3DX_FILTER_SRGB; + } + + // mip_filter_flags| = D3DX_FILTER_DITHER; + + hr = D3DXFilterTexture(d3d_volume_texture, nullptr, 0, mip_filter_flags); + if (FAILED(hr)) { + dxgsg9_cat.error() + << "FillDDSurfaceTexturePixels failed for " << tex->get_name() + << " view " << view << ", D3DXFilterTex failed" << D3DERRORSTRING(hr); + success = false; + continue; + } + } + + RELEASE(mip_level_0, dxgsg9, "FillDDSurf MipLev0 texture ptr", RELEASE_ONCE); } - exit_FillDDSurf: - if (using_temp_buffer) { - SAFE_DELETE_ARRAY(pixels); + if (temp_buffer != nullptr) { + delete[] temp_buffer; } - RELEASE(mip_level_0, dxgsg9, "FillDDSurf MipLev0 texture ptr", RELEASE_ONCE); - return hr; + + return success ? D3D_OK : E_FAIL; } - /** * Returns the largest power of 2 less than or equal to value. */ diff --git a/panda/src/dxgsg9/dxTextureContext9.h b/panda/src/dxgsg9/dxTextureContext9.h index 53558d39f0..a8cd2db0bb 100644 --- a/panda/src/dxgsg9/dxTextureContext9.h +++ b/panda/src/dxgsg9/dxTextureContext9.h @@ -17,13 +17,14 @@ #include "dxgsg9base.h" #include "texture.h" #include "textureContext.h" +#include "small_vector.h" /** * */ class EXPCL_PANDADX DXTextureContext9 : public TextureContext { public: - DXTextureContext9(PreparedGraphicsObjects *pgo, Texture *tex, int view); + DXTextureContext9(PreparedGraphicsObjects *pgo, Texture *tex); virtual ~DXTextureContext9(); virtual void evict_lru(); @@ -34,10 +35,10 @@ public: bool extract_texture_data(DXScreenData &scrn); INLINE bool has_mipmaps() const; - INLINE IDirect3DBaseTexture9 *get_d3d_texture() const; - INLINE IDirect3DTexture9 *get_d3d_2d_texture() const; - INLINE IDirect3DVolumeTexture9 *get_d3d_volume_texture() const; - INLINE IDirect3DCubeTexture9 *get_d3d_cube_texture() const; + INLINE IDirect3DBaseTexture9 *get_d3d_texture(int view) const; + INLINE IDirect3DTexture9 *get_d3d_2d_texture(int view) const; + INLINE IDirect3DVolumeTexture9 *get_d3d_volume_texture(int view) const; + INLINE IDirect3DCubeTexture9 *get_d3d_cube_texture(int view) const; static HRESULT d3d_surface_to_texture(RECT &source_rect, IDirect3DSurface9 *d3d_surface, @@ -54,10 +55,10 @@ private: private: D3DFORMAT _d3d_format; // the 'D3DFORMAT' the Panda TextureBuffer fmt corresponds to - IDirect3DBaseTexture9 *_d3d_texture; - IDirect3DTexture9 *_d3d_2d_texture; - IDirect3DVolumeTexture9 *_d3d_volume_texture; - IDirect3DCubeTexture9 *_d3d_cube_texture; + small_vector _d3d_textures; + //IDirect3DTexture9 *_d3d_2d_texture; + //IDirect3DVolumeTexture9 *_d3d_volume_texture; + //IDirect3DCubeTexture9 *_d3d_cube_texture; int _managed; diff --git a/panda/src/dxgsg9/wdxGraphicsBuffer9.cxx b/panda/src/dxgsg9/wdxGraphicsBuffer9.cxx index 86a8c9ee41..c09916fded 100644 --- a/panda/src/dxgsg9/wdxGraphicsBuffer9.cxx +++ b/panda/src/dxgsg9/wdxGraphicsBuffer9.cxx @@ -364,7 +364,7 @@ rebuild_bitplanes() { // color_tex->set_format(Texture::F_rgba); color_ctx = DCAST(DXTextureContext9, - color_tex->prepare_now(0, _gsg->get_prepared_objects(), _gsg)); + color_tex->prepare_now(_gsg->get_prepared_objects(), _gsg)); if (color_ctx) { if (!color_ctx->create_texture(*_dxgsg->_screen)) { @@ -373,7 +373,7 @@ rebuild_bitplanes() { return false; } if (color_tex->get_texture_type() == Texture::TT_2d_texture) { - color_d3d_tex = color_ctx->_d3d_2d_texture; + color_d3d_tex = color_ctx->get_d3d_2d_texture(0); nassertr(color_d3d_tex != 0, false); hr = color_d3d_tex -> GetSurfaceLevel(0, &color_surf); if (!SUCCEEDED(hr)) { @@ -381,7 +381,7 @@ rebuild_bitplanes() { } } if (color_tex->get_texture_type() == Texture::TT_cube_map) { - color_cube = color_ctx->_d3d_cube_texture; + color_cube = color_ctx->get_d3d_cube_texture(0); nassertr(color_cube != 0, false); if (_cube_map_index >= 0 && _cube_map_index < 6) { @@ -443,7 +443,7 @@ rebuild_bitplanes() { depth_tex->set_format(Texture::F_depth_stencil); depth_ctx = DCAST(DXTextureContext9, - depth_tex->prepare_now(0, _gsg->get_prepared_objects(), _gsg)); + depth_tex->prepare_now(_gsg->get_prepared_objects(), _gsg)); if (depth_ctx) { if (!depth_ctx->create_texture(*_dxgsg->_screen)) { dxgsg9_cat.error() @@ -451,7 +451,7 @@ rebuild_bitplanes() { return false; } if (depth_tex->get_texture_type() == Texture::TT_2d_texture) { - depth_d3d_tex = depth_ctx->_d3d_2d_texture; + depth_d3d_tex = depth_ctx->get_d3d_2d_texture(0); nassertr(depth_d3d_tex != 0, false); hr = depth_d3d_tex -> GetSurfaceLevel(0, &depth_surf); if (!SUCCEEDED(hr)) { @@ -459,7 +459,7 @@ rebuild_bitplanes() { } } if (depth_tex->get_texture_type() == Texture::TT_cube_map) { - depth_cube = depth_ctx->_d3d_cube_texture; + depth_cube = depth_ctx->get_d3d_cube_texture(0); nassertr(depth_cube != 0, false); hr = depth_cube -> GetCubeMapSurface ((D3DCUBEMAP_FACES) _cube_map_index, 0, &depth_surf); if (!SUCCEEDED(hr)) { @@ -518,7 +518,7 @@ rebuild_bitplanes() { IDirect3DSurface9 *color_surf = 0; IDirect3DCubeTexture9 *color_cube = 0; - color_ctx = DCAST(DXTextureContext9, tex->prepare_now(0, _gsg->get_prepared_objects(), _gsg)); + color_ctx = DCAST(DXTextureContext9, tex->prepare_now(_gsg->get_prepared_objects(), _gsg)); if (color_ctx) { if (!color_ctx->create_texture(*_dxgsg->_screen)) { dxgsg9_cat.error() @@ -526,7 +526,7 @@ rebuild_bitplanes() { return false; } if (tex->get_texture_type() == Texture::TT_2d_texture) { - color_d3d_tex = color_ctx->_d3d_2d_texture; + color_d3d_tex = color_ctx->get_d3d_2d_texture(0); nassertr(color_d3d_tex != 0, false); hr = color_d3d_tex -> GetSurfaceLevel(0, &color_surf); @@ -613,8 +613,8 @@ select_target_tex_page(int page) { if (color_tex) { color_ctx = DCAST(DXTextureContext9, - color_tex->prepare_now(0, _gsg->get_prepared_objects(), _gsg)); - color_cube = color_ctx->_d3d_cube_texture; + color_tex->prepare_now(_gsg->get_prepared_objects(), _gsg)); + color_cube = color_ctx->get_d3d_cube_texture(0); if (color_cube && _cube_map_index >= 0 && _cube_map_index < 6) { hr = color_cube -> GetCubeMapSurface ((D3DCUBEMAP_FACES) _cube_map_index, 0, &color_surf); if (!SUCCEEDED(hr)) { @@ -657,7 +657,7 @@ select_target_tex_page(int page) { IDirect3DSurface9 *color_surf = 0; IDirect3DCubeTexture9 *color_cube = 0; - color_ctx = DCAST(DXTextureContext9, tex->prepare_now(0, _gsg->get_prepared_objects(), _gsg)); + color_ctx = DCAST(DXTextureContext9, tex->prepare_now(_gsg->get_prepared_objects(), _gsg)); if (color_ctx) { if (tex->get_texture_type() == Texture::TT_cube_map) { @@ -665,7 +665,7 @@ select_target_tex_page(int page) { printf ("CUBEMAP i = %d, RenderTexturePlane = %d, _cube_map_index %d \n", i, plane, _cube_map_index); } - color_cube = color_ctx->_d3d_cube_texture; + color_cube = color_ctx->get_d3d_cube_texture(0); if (color_cube && _cube_map_index >= 0 && _cube_map_index < 6) { hr = color_cube -> GetCubeMapSurface ((D3DCUBEMAP_FACES) _cube_map_index, 0, &color_surf); if (!SUCCEEDED(hr)) { diff --git a/panda/src/gles2gsg/gles2gsg.h b/panda/src/gles2gsg/gles2gsg.h index db422f2163..967f341703 100644 --- a/panda/src/gles2gsg/gles2gsg.h +++ b/panda/src/gles2gsg/gles2gsg.h @@ -151,6 +151,8 @@ typedef char GLchar; #define GL_COLOR 0x1800 #define GL_DEPTH 0x1801 #define GL_STENCIL 0x1802 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 #define GL_RGB10_A2 0x8059 #define GL_TEXTURE_WRAP_R 0x8072 #define GL_TEXTURE_MIN_LOD 0x813A @@ -203,6 +205,7 @@ typedef char GLchar; #define GL_FLOAT_MAT4x3 0x8B6A #define GL_TEXTURE_2D_ARRAY 0x8C1A #define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_TEXTURE_BUFFER 0x8C2A #define GL_R11F_G11F_B10F 0x8C3A #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B #define GL_RGB9_E5 0x8C3D @@ -228,6 +231,7 @@ typedef char GLchar; #define GL_RGB_INTEGER 0x8D98 #define GL_RGBA_INTEGER 0x8D99 #define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_BUFFER 0x8DC2 #define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 #define GL_SAMPLER_CUBE_SHADOW 0x8DC5 #define GL_UNSIGNED_INT_VEC2 0x8DC6 @@ -237,24 +241,37 @@ typedef char GLchar; #define GL_INT_SAMPLER_3D 0x8DCB #define GL_INT_SAMPLER_CUBE 0x8DCC #define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_INT_SAMPLER_BUFFER 0x8DD0 #define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 #define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 #define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 #define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 #define GL_MAX_IMAGE_UNITS 0x8F38 #define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F #define GL_IMAGE_2D 0x904D #define GL_IMAGE_3D 0x904E #define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 #define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 #define GL_INT_IMAGE_2D 0x9058 #define GL_INT_IMAGE_3D 0x9059 #define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C #define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F #define GL_UNSIGNED_INT_IMAGE_2D 0x9063 #define GL_UNSIGNED_INT_IMAGE_3D 0x9064 #define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 #define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 #define GL_UNSIGNALED 0x9118 #define GL_SIGNALED 0x9119 diff --git a/panda/src/glstuff/glCgShaderContext_src.cxx b/panda/src/glstuff/glCgShaderContext_src.cxx index b4d38ff8e9..edf23b0658 100644 --- a/panda/src/glstuff/glCgShaderContext_src.cxx +++ b/panda/src/glstuff/glCgShaderContext_src.cxx @@ -1054,26 +1054,38 @@ disable_shader_texture_bindings() { return; } - for (int i = 0; i < (int)_shader->_tex_spec.size(); ++i) { - CGparameter p = _cg_parameter_map[_shader->_tex_spec[i]._id._seqno]; - if (p == 0) continue; + if (_glgsg->_supports_dsa) { + // The DSA extension has a single call for unbinding all targets for a + // given texture unit. + for (int i = 0; i < (int)_shader->_tex_spec.size(); ++i) { + CGparameter p = _cg_parameter_map[_shader->_tex_spec[i]._id._seqno]; + if (p == 0) continue; - int texunit = cgGetParameterResourceIndex(p); - _glgsg->set_active_texture_stage(texunit); + int texunit = cgGetParameterResourceIndex(p); + _glgsg->_glBindTextureUnit(texunit, 0); + } + } else { + for (int i = 0; i < (int)_shader->_tex_spec.size(); ++i) { + CGparameter p = _cg_parameter_map[_shader->_tex_spec[i]._id._seqno]; + if (p == 0) continue; - glBindTexture(GL_TEXTURE_1D, 0); - glBindTexture(GL_TEXTURE_2D, 0); - if (_glgsg->_supports_3d_texture) { - glBindTexture(GL_TEXTURE_3D, 0); + int texunit = cgGetParameterResourceIndex(p); + _glgsg->set_active_texture_stage(texunit); + + glBindTexture(GL_TEXTURE_1D, 0); + glBindTexture(GL_TEXTURE_2D, 0); + if (_glgsg->_supports_3d_texture) { + glBindTexture(GL_TEXTURE_3D, 0); + } + if (_glgsg->_supports_2d_texture_array) { + glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, 0); + } + if (_glgsg->_supports_cube_map) { + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + } + // This is probably faster - but maybe not as safe? + // cgGLDisableTextureParameter(p); } - if (_glgsg->_supports_2d_texture_array) { - glBindTexture(GL_TEXTURE_2D_ARRAY_EXT, 0); - } - if (_glgsg->_supports_cube_map) { - glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - } - // This is probably faster - but maybe not as safe? - // cgGLDisableTextureParameter(p); } cg_report_errors(); @@ -1132,7 +1144,7 @@ update_shader_texture_bindings(ShaderContext *prev) { _glgsg->set_active_texture_stage(texunit); - TextureContext *tc = tex->prepare_now(view, _glgsg->_prepared_objects, _glgsg); + TextureContext *tc = tex->prepare_now(_glgsg->_prepared_objects, _glgsg); if (tc == nullptr) { continue; } @@ -1148,8 +1160,8 @@ update_shader_texture_bindings(ShaderContext *prev) { } CLP(TextureContext) *gtc = (CLP(TextureContext) *)tc; - _glgsg->apply_texture(gtc); - _glgsg->apply_sampler(texunit, sampler, gtc); + _glgsg->apply_texture(gtc, view); + _glgsg->apply_sampler(texunit, sampler, gtc, view); } cg_report_errors(); diff --git a/panda/src/glstuff/glGeomMunger_src.cxx b/panda/src/glstuff/glGeomMunger_src.cxx index f6bf36da4a..ac1ae02496 100644 --- a/panda/src/glstuff/glGeomMunger_src.cxx +++ b/panda/src/glstuff/glGeomMunger_src.cxx @@ -274,7 +274,7 @@ munge_format_impl(const GeomVertexFormat *orig, column_alignment = texcoord_type->get_column_alignment(); num_values = texcoord_type->get_num_values(); } - if (start + num_values * sizeof(PN_stdfloat) > glgsg->get_max_vertex_attrib_stride()) { + if (start + num_values * sizeof(PN_stdfloat) > (size_t)glgsg->get_max_vertex_attrib_stride()) { // We are exceeding the limit for stride reported by the driver. // Start a new array. new_format->insert_array(insert_at++, new_array_format); diff --git a/panda/src/glstuff/glGraphicsBuffer_src.cxx b/panda/src/glstuff/glGraphicsBuffer_src.cxx index c3c6dd0fb4..0a26277ea9 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.cxx +++ b/panda/src/glstuff/glGraphicsBuffer_src.cxx @@ -268,11 +268,8 @@ begin_frame(FrameMode mode, Thread *current_thread) { if (gl_enable_memory_barriers && _fbo_multisample == 0) { CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); - TextureContexts::iterator it; - for (it = _texture_contexts.begin(); it != _texture_contexts.end(); ++it) { - CLP(TextureContext) *gtc = *it; - - if (gtc != nullptr && gtc->needs_barrier(GL_FRAMEBUFFER_BARRIER_BIT)) { + for (CLP(TextureContext) *gtc : _texture_contexts) { + if (gtc->needs_barrier(GL_FRAMEBUFFER_BARRIER_BIT)) { glgsg->issue_memory_barrier(GL_FRAMEBUFFER_BARRIER_BIT); // If we've done it for one, we've done it for all. break; @@ -478,6 +475,12 @@ rebuild_bitplanes() { // Assign the texture to this slot. attach[plane] = tex; + + if (plane == RTP_color && _fb_properties.is_stereo()) { + if (tex->get_num_views() < 2) { + tex->set_num_views(2); + } + } } } @@ -581,7 +584,9 @@ rebuild_bitplanes() { // The second tex view has already been initialized, so bind it // straight away. if (attach[RTP_color] != nullptr) { - attach_tex(layer, 1, attach[RTP_color], next++); + CLP(TextureContext) *gtc = _texture_contexts.back(); + nassertv(gtc->get_texture() == attach[RTP_color]); + attach_tex(next++, gtc, 1, layer); } else { // XXX hack: I needed a slot to use, and we don't currently use // RTP_stencil and it's treated as a color attachment below, so this @@ -776,7 +781,15 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, _fb_properties.setup_color_texture(tex); } - GLenum target = glgsg->get_texture_target(tex->get_texture_type()); + TextureContext *tc = tex->prepare_now(glgsg->get_prepared_objects(), glgsg); + nassertv(tc != nullptr); + CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); + + glgsg->update_texture(gtc, true); + gtc->set_active(true); + _texture_contexts.push_back(gtc); + + GLenum target = gtc->_target; if (target == GL_TEXTURE_CUBE_MAP) { target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer; } @@ -793,7 +806,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, _rb[RTP_depth_stencil] = 0; } - attach_tex(layer, 0, tex, GL_DEPTH_ATTACHMENT_EXT); + attach_tex(GL_DEPTH_ATTACHMENT_EXT, gtc, 0, layer); #ifndef OPENGLES GLint depth_size = 0; @@ -806,7 +819,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, GLCAT.debug() << "Binding texture " << *tex << " to stencil attachment.\n"; } - attach_tex(layer, 0, tex, GL_STENCIL_ATTACHMENT_EXT); + attach_tex(GL_STENCIL_ATTACHMENT_EXT, gtc, 0, layer); #ifndef OPENGLES GLint stencil_size = 0; @@ -820,7 +833,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot, GLCAT.debug() << "Binding texture " << *tex << " to color attachment.\n"; } - attach_tex(layer, 0, tex, attachpoint); + attach_tex(attachpoint, gtc, 0, layer); #ifndef OPENGLES if (attachpoint == GL_COLOR_ATTACHMENT0_EXT) { @@ -1344,30 +1357,22 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot, * This function attaches the given texture to the given attachment point. */ void CLP(GraphicsBuffer):: -attach_tex(int layer, int view, Texture *attach, GLenum attachpoint) { +attach_tex(GLenum attachpoint, CLP(TextureContext) *gtc, int view, int layer) { + // We should have created the right number of views ahead of time. + nassertv(view < gtc->_num_views); + CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p(); - if (view >= attach->get_num_views()) { - attach->set_num_views(view + 1); - } - - // Create the OpenGL texture object. - TextureContext *tc = attach->prepare_now(view, glgsg->get_prepared_objects(), glgsg); - nassertv(tc != nullptr); - CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); - - glgsg->update_texture(gtc, true); - gtc->set_active(true); - _texture_contexts.push_back(gtc); - // It seems that binding the texture is necessary before binding to a // framebuffer attachment. - glgsg->apply_texture(gtc); + glgsg->apply_texture(gtc, view); + + GLuint index = gtc->get_view_index(view); #if !defined(OPENGLES) && defined(SUPPORT_FIXED_FUNCTION) if (glgsg->has_fixed_function_pipeline()) { GLclampf priority = 1.0f; - glPrioritizeTextures(1, >c->_index, &priority); + glPrioritizeTextures(1, &index, &priority); } #endif @@ -1375,31 +1380,29 @@ attach_tex(int layer, int view, Texture *attach, GLenum attachpoint) { if (_rb_size_z != 1) { // Bind all of the layers of the texture. nassertv(glgsg->_glFramebufferTexture != nullptr); - glgsg->_glFramebufferTexture(GL_FRAMEBUFFER_EXT, attachpoint, - gtc->_index, 0); + glgsg->_glFramebufferTexture(GL_FRAMEBUFFER_EXT, attachpoint, index, 0); return; } #endif - GLenum target = glgsg->get_texture_target(attach->get_texture_type()); - if (target == GL_TEXTURE_CUBE_MAP) { - target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer; - } - + GLenum target = gtc->_target; switch (target) { #ifndef OPENGLES_1 case GL_TEXTURE_3D: glgsg->_glFramebufferTexture3D(GL_FRAMEBUFFER_EXT, attachpoint, - target, gtc->_index, 0, layer); + target, index, 0, layer); break; case GL_TEXTURE_2D_ARRAY: glgsg->_glFramebufferTextureLayer(GL_FRAMEBUFFER_EXT, attachpoint, - gtc->_index, 0, layer); + index, 0, layer); break; #endif + case GL_TEXTURE_CUBE_MAP: + target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer; + // fall through default: glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, attachpoint, - target, gtc->_index, 0); + target, index, 0); } } @@ -1419,10 +1422,7 @@ generate_mipmaps() { // PStatGPUTimer timer(glgsg, _generate_mipmap_pcollector); - pvector::iterator it; - for (it = _texture_contexts.begin(); it != _texture_contexts.end(); ++it) { - CLP(TextureContext) *gtc = *it; - + for (CLP(TextureContext) *gtc : _texture_contexts) { if (gtc->_generate_mipmaps) { glgsg->generate_mipmaps(gtc); } @@ -1954,11 +1954,8 @@ resolve_multisamples() { if (gl_enable_memory_barriers) { // Issue memory barriers as necessary to make sure that the texture memory // is synchronized before we blit to it. - pvector::iterator it; - for (it = _texture_contexts.begin(); it != _texture_contexts.end(); ++it) { - CLP(TextureContext) *gtc = *it; - - if (gtc != nullptr && gtc->needs_barrier(GL_FRAMEBUFFER_BARRIER_BIT)) { + for (CLP(TextureContext) *gtc : _texture_contexts) { + if (gtc->needs_barrier(GL_FRAMEBUFFER_BARRIER_BIT)) { glgsg->issue_memory_barrier(GL_FRAMEBUFFER_BARRIER_BIT); // If we've done it for one, we've done it for all. break; diff --git a/panda/src/glstuff/glGraphicsBuffer_src.h b/panda/src/glstuff/glGraphicsBuffer_src.h index dfd4a92e94..83c58ba45b 100644 --- a/panda/src/glstuff/glGraphicsBuffer_src.h +++ b/panda/src/glstuff/glGraphicsBuffer_src.h @@ -92,7 +92,7 @@ protected: RenderTexturePlane plane, GLenum attachpoint); void bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane plane, GLenum attachpoint); - void attach_tex(int layer, int view, Texture *attach, GLenum attachpoint); + void attach_tex(GLenum attachpoint, CLP(TextureContext) *gtc, int view, int layer); bool check_fbo(); void generate_mipmaps(); void rebuild_bitplanes(); diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index e9ea3f1ae6..9242c584e8 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -1201,6 +1201,15 @@ reset() { _glTexBuffer = (PFNGLTEXBUFFERPROC)get_extension_func("glTexBufferARB"); _supports_buffer_texture = true; } +#elif !defined(OPENGLES_1) + if (is_at_least_gles_version(3, 2)) { + _glTexBuffer = (PFNGLTEXBUFFERPROC)get_extension_func("glTexBuffer"); + _supports_buffer_texture = (_glTexBuffer != nullptr); + } + else if (has_extension("GL_OES_texture_buffer")) { + _glTexBuffer = (PFNGLTEXBUFFERPROC)get_extension_func("glTexBufferOES"); + _supports_buffer_texture = (_glTexBuffer != nullptr); + } #endif #ifdef OPENGLES @@ -2562,8 +2571,18 @@ reset() { #ifndef OPENGLES if (is_at_least_gl_version(4, 5) || has_extension("GL_ARB_direct_state_access")) { + _glCreateTextures = (PFNGLCREATETEXTURESPROC) + get_extension_func("glCreateTextures"); + _glTextureStorage2D = (PFNGLTEXTURESTORAGE2DPROC) + get_extension_func("glTextureStorage2D"); + _glTextureSubImage2D = (PFNGLTEXTURESUBIMAGE2DPROC) + get_extension_func("glTextureSubImage2D"); + _glTextureParameteri = (PFNGLTEXTUREPARAMETERIPROC) + get_extension_func("glTextureParameteri"); _glGenerateTextureMipmap = (PFNGLGENERATETEXTUREMIPMAPPROC) get_extension_func("glGenerateTextureMipmap"); + _glBindTextureUnit = (PFNGLBINDTEXTUREUNITPROC) + get_extension_func("glBindTextureUnit"); _supports_dsa = true; } else { @@ -6243,7 +6262,7 @@ issue_memory_barrier(GLbitfield barriers) { * call Texture::prepare(). */ TextureContext *CLP(GraphicsStateGuardian):: -prepare_texture(Texture *tex, int view) { +prepare_texture(Texture *tex) { PStatGPUTimer timer(this, _prepare_texture_pcollector); report_my_gl_errors(); @@ -6295,8 +6314,12 @@ prepare_texture(Texture *tex, int view) { break; } - CLP(TextureContext) *gtc = new CLP(TextureContext)(this, _prepared_objects, tex, view); - gtc->_target = get_texture_target(texture_type); + CLP(TextureContext) *gtc = new CLP(TextureContext)(this, _prepared_objects, tex); + + // Make sure we have an index so that get_native_id() will already work. + GLenum target = get_texture_target(texture_type); + gtc->reset_data(target, 1); + report_my_gl_errors(); return gtc; @@ -6317,17 +6340,18 @@ update_texture(TextureContext *tc, bool force) { CLP(TextureContext) *gtc; DCAST_INTO_R(gtc, tc, false); + Texture *tex = tc->get_texture(); + GLenum target = get_texture_target(tex->get_texture_type()); + if (gtc->_target != target) { + // The target has changed. That means we have to re-bind a new texture + // object. + gtc->reset_data(target, tex->get_num_views()); + } + if (gtc->was_image_modified() || !gtc->_has_storage) { PStatGPUTimer timer(this, _texture_update_pcollector); // If the texture image was modified, reload the texture. - apply_texture(gtc); - - Texture *tex = tc->get_texture(); - if (gtc->was_properties_modified()) { - specify_texture(gtc, tex->get_default_sampler()); - } - bool okflag = upload_texture(gtc, force, tex->uses_mipmaps()); if (!okflag) { GLCAT.error() @@ -6335,16 +6359,29 @@ update_texture(TextureContext *tc, bool force) { return false; } - } else if (gtc->was_properties_modified()) { + if (gtc->was_properties_modified()) { + for (int view = 0; view < gtc->_num_views; ++view) { + apply_texture(gtc, view); + specify_texture(gtc, tex->get_default_sampler()); + } + } + } + else if (gtc->was_properties_modified()) { PStatGPUTimer timer(this, _texture_update_pcollector); // If only the properties have been modified, we don't necessarily need to // reload the texture. - apply_texture(gtc); + bool needs_reload = false; + for (int view = 0; view < gtc->_num_views; ++view) { + apply_texture(gtc, view); - Texture *tex = tc->get_texture(); - if (specify_texture(gtc, tex->get_default_sampler())) { - // Actually, looks like the texture *does* need to be reloaded. + if (specify_texture(gtc, tex->get_default_sampler())) { + // Actually, looks like the texture *does* need to be reloaded. + needs_reload = true; + } + } + + if (needs_reload) { gtc->mark_needs_reload(); bool okflag = upload_texture(gtc, force, tex->uses_mipmaps()); if (!okflag) { @@ -6352,8 +6389,8 @@ update_texture(TextureContext *tc, bool force) { << "Could not load " << *tex << "\n"; return false; } - - } else { + } + else { // The texture didn't need reloading, but mark it fully updated now. gtc->mark_loaded(); } @@ -6381,12 +6418,7 @@ release_texture(TextureContext *tc) { _textures_needing_framebuffer_barrier.erase(gtc); #endif - glDeleteTextures(1, >c->_index); - - if (gtc->_buffer != 0) { - _glDeleteBuffers(1, >c->_buffer); - } - + gtc->set_num_views(0); delete gtc; } @@ -6401,8 +6433,6 @@ release_textures(const pvector &contexts) { return; } - GLuint *indices = (GLuint *)alloca(sizeof(GLuint) * contexts.size() * 2); - GLuint *buffers = indices + contexts.size(); size_t num_indices = 0; size_t num_buffers = 0; @@ -6416,15 +6446,33 @@ release_textures(const pvector &contexts) { _textures_needing_framebuffer_barrier.erase(gtc); #endif - indices[num_indices++] = gtc->_index; - - if (gtc->_buffer != 0) { - buffers[num_buffers++] = gtc->_buffer; + num_indices += gtc->_num_views; + if (gtc->_buffers != nullptr) { + num_buffers += gtc->_num_views; } + } + + GLuint *indices = (GLuint *)alloca(sizeof(GLuint) * num_indices); + GLuint *buffers = (GLuint *)alloca(sizeof(GLuint) * num_buffers); + num_indices = 0; + num_buffers = 0; + + for (TextureContext *tc : contexts) { + CLP(TextureContext) *gtc = (CLP(TextureContext) *)tc; + + for (int view = 0; view < gtc->_num_views; ++view) { + indices[num_indices++] = gtc->_indices[view]; + + if (gtc->_buffers != nullptr) { + buffers[num_buffers++] = gtc->_buffers[view]; + } + } + gtc->_num_views = 0; delete gtc; } + glDeleteTextures(num_indices, indices); if (num_buffers > 0) { @@ -6446,17 +6494,45 @@ extract_texture_data(Texture *tex) { // Make sure the error stack is cleared out before we begin. report_my_gl_errors(); + TextureContext *tc = tex->prepare_now(get_prepared_objects(), this); + nassertr(tc != nullptr, false); + CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); + + GLenum target = gtc->_target; + int num_views = tex->get_num_views(); for (int view = 0; view < num_views; ++view) { - TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this); - nassertr(tc != nullptr, false); - CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); + GLuint index = gtc->get_view_index(view); + glBindTexture(target, index); + if (GLCAT.is_spam()) { + GLCAT.spam() + << "glBindTexture(0x" << hex << target << dec << ", " << index << "): " + << *tex << " view " << view << "\n"; + } - if (!do_extract_texture_data(gtc)) { +#ifndef OPENGLES_1 + if (target == GL_TEXTURE_BUFFER) { + _glBindBuffer(GL_TEXTURE_BUFFER, gtc->get_view_buffer(view)); + } +#endif + + if (!do_extract_texture_data(gtc, view)) { success = false; } } + glBindTexture(target, 0); + if (GLCAT.is_spam()) { + GLCAT.spam() + << "glBindTexture(0x" << hex << target << dec << ", 0)\n"; + } + +#ifndef OPENGLES_1 + if (target == GL_TEXTURE_BUFFER) { + _glBindBuffer(GL_TEXTURE_BUFFER, 0); + } +#endif + return success; } @@ -7642,27 +7718,44 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, } } - TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this); + TextureContext *tc = tex->prepare_now(get_prepared_objects(), this); nassertr(tc != nullptr, false); CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); - apply_texture(gtc); + GLenum target = get_texture_target(tex->get_texture_type()); + if (gtc->_target != target) { + gtc->reset_data(target, view + 1); + } + else if (view >= gtc->_num_views) { + gtc->set_num_views(view + 1); + } + + apply_texture(gtc, view); bool needs_reload = specify_texture(gtc, tex->get_default_sampler()); - GLenum target = get_texture_target(tex->get_texture_type()); GLint internal_format = get_internal_image_format(tex); int width = tex->get_x_size(); int height = tex->get_y_size(); int depth = tex->get_z_size(); bool uses_mipmaps = tex->uses_mipmaps() && !gl_ignore_mipmaps; + int num_levels = 1; + bool can_generate = _supports_generate_mipmap; +#if defined(OPENGLES) && !defined(OPENGLES_1) + // OpenGL ES doesn't support generating mipmaps for sRGB textures, so we + // have to disable mipmaps, unless we have a special extension. + if (internal_format == GL_SRGB8 || internal_format == GL_SRGB8_ALPHA8) { + can_generate = has_extension("GL_NV_generate_mipmap_sRGB"); + } +#endif if (uses_mipmaps) { - if (_supports_generate_mipmap) { + if (can_generate) { #ifndef OPENGLES_2 if (_glGenerateMipmap == nullptr) { glTexParameteri(target, GL_GENERATE_MIPMAP, true); } #endif + num_levels = tex->get_expected_num_mipmap_levels(); } else { // If we can't auto-generate mipmaps, do without mipmaps. glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -7687,21 +7780,23 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, if (!gtc->_has_storage || internal_format != gtc->_internal_format || - uses_mipmaps != gtc->_uses_mipmaps || width != gtc->_width || height != gtc->_height || - depth != gtc->_depth) { + depth != gtc->_depth || + (gtc->_immutable && num_levels > gtc->_num_levels)) { // If the texture properties have changed, we need to reload the image. new_image = true; } if (new_image && gtc->_immutable) { - gtc->reset_data(); - glBindTexture(target, gtc->_index); + gtc->reset_data(target, view + 1); + GLuint index = gtc->get_view_index(view); + glBindTexture(target, index); if (GLCAT.is_spam()) { GLCAT.spam() - << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *tex << "\n"; + << "glBindTexture(0x" << hex << target << dec << ", " << index << "): " + << *tex << " view " << view << "\n"; } } @@ -7743,12 +7838,12 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, } gtc->_has_storage = true; - gtc->_simple_loaded = false; - gtc->_uses_mipmaps = uses_mipmaps; gtc->_internal_format = internal_format; gtc->_width = width; gtc->_height = height; gtc->_depth = depth; + gtc->_num_levels = num_levels; + gtc->_may_reload_with_mipmaps = !uses_mipmaps && can_generate; gtc->mark_loaded(); gtc->enqueue_lru(&_prepared_objects->_graphics_memory_lru); @@ -10019,7 +10114,7 @@ get_texture_target(Texture::TextureType texture_type) const { return GL_NONE; case Texture::TT_buffer_texture: -#ifndef OPENGLES +#ifndef OPENGLES_1 if (_supports_buffer_texture) { return GL_TEXTURE_BUFFER; } @@ -10445,7 +10540,7 @@ get_external_image_format(Texture *tex) const { return GL_DEPTH_COMPONENT; case Texture::F_depth_stencil: return _supports_depth_stencil ? GL_DEPTH_STENCIL : GL_DEPTH_COMPONENT; -#ifndef OPENGLES +#ifndef OPENGLES_1 case Texture::F_red: case Texture::F_r16: case Texture::F_r32: @@ -12690,7 +12785,7 @@ update_standard_texture_bindings() { #endif // OPENGLES int view = get_current_tex_view_offset() + stage->get_tex_view_offset(); - TextureContext *tc = texture->prepare_now(view, _prepared_objects, this); + TextureContext *tc = texture->prepare_now(_prepared_objects, this); if (tc == nullptr) { // Something wrong with this texture; skip it. continue; @@ -12716,8 +12811,8 @@ update_standard_texture_bindings() { } // Don't DCAST(); we already did the verification in update_texture. CLP(TextureContext) *gtc = (CLP(TextureContext) *)tc; - apply_texture(gtc); - apply_sampler(i, _target_texture->get_on_sampler(stage), gtc); + apply_texture(gtc, view); + apply_sampler(i, _target_texture->get_on_sampler(stage), gtc, view); if (stage->involves_color_scale() && _color_scale_enabled) { LColor color = stage->get_color(); @@ -12868,8 +12963,22 @@ update_standard_texture_bindings() { */ void CLP(GraphicsStateGuardian):: apply_white_texture(GLuint unit) { - set_active_texture_stage(unit); - glBindTexture(GL_TEXTURE_2D, get_white_texture()); +#ifndef OPENGLES + if (_supports_dsa) { + _glBindTextureUnit(unit, get_white_texture()); + } else +#endif + { + set_active_texture_stage(unit); + + GLuint index = get_white_texture(); + glBindTexture(GL_TEXTURE_2D, index); + + if (GLCAT.is_spam()) { + GLCAT.spam() + << "glBindTexture(GL_TEXTURE_2D, " << index << "): all-white texture\n"; + } + } // Also apply the default sampler, if there's a chance we'd applied anything // else. @@ -12886,7 +12995,24 @@ apply_white_texture(GLuint unit) { */ GLuint CLP(GraphicsStateGuardian):: get_white_texture() { - if (_white_texture == 0) { + if (_white_texture != 0) { + return _white_texture; + } + + const unsigned char data[] = {0xff, 0xff, 0xff, 0xff}; + +#ifndef OPENGLES + if (_supports_dsa && _supports_tex_storage) { + _glCreateTextures(GL_TEXTURE_2D, 1, &_white_texture); + _glTextureParameteri(_white_texture, GL_TEXTURE_WRAP_S, GL_REPEAT); + _glTextureParameteri(_white_texture, GL_TEXTURE_WRAP_T, GL_REPEAT); + _glTextureParameteri(_white_texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + _glTextureParameteri(_white_texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + _glTextureStorage2D(_white_texture, 1, GL_RGBA8, 1, 1); + _glTextureSubImage2D(_white_texture, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data); + } else +#endif + { glGenTextures(1, &_white_texture); glBindTexture(GL_TEXTURE_2D, _white_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); @@ -12894,7 +13020,6 @@ get_white_texture() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - unsigned char data[] = {0xff, 0xff, 0xff, 0xff}; glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); } @@ -12928,8 +13053,7 @@ update_show_usage_texture_bindings(int show_stage_index) { Texture *texture = _target_texture->get_on_texture(stage); nassertv(texture != nullptr); - int view = get_current_tex_view_offset() + stage->get_tex_view_offset(); - TextureContext *tc = texture->prepare_now(view, _prepared_objects, this); + TextureContext *tc = texture->prepare_now(_prepared_objects, this); if (tc == nullptr) { // Something wrong with this texture; skip it. break; @@ -13423,18 +13547,13 @@ do_issue_tex_gen() { */ bool CLP(GraphicsStateGuardian):: specify_texture(CLP(TextureContext) *gtc, const SamplerState &sampler) { -#ifndef OPENGLES - nassertr(gtc->_handle == 0 /* can't modify tex with active handle */, false); -#endif - Texture *tex = gtc->get_texture(); - - GLenum target = get_texture_target(tex->get_texture_type()); + GLenum target = gtc->_target; if (target == GL_NONE) { // Unsupported target (e.g. 3-d texturing on GL 1.1). return false; } -#ifndef OPENGLES +#ifndef OPENGLES_1 if (target == GL_TEXTURE_BUFFER) { // Buffer textures may not receive texture parameters. return false; @@ -13550,9 +13669,8 @@ specify_texture(CLP(TextureContext) *gtc, const SamplerState &sampler) { report_my_gl_errors(); - if (uses_mipmaps && !gtc->_uses_mipmaps) { - // Suddenly we require mipmaps. This means the texture may need - // reloading. + if (uses_mipmaps && gtc->_may_reload_with_mipmaps) { + // Suddenly we require mipmaps, which means the texture may need reloading. return true; } @@ -13562,26 +13680,26 @@ specify_texture(CLP(TextureContext) *gtc, const SamplerState &sampler) { /** * Updates OpenGL with the current information for this texture, and makes it * the current texture available for rendering. + * + * The texture needs to have valid storage, call update_texture() first. */ bool CLP(GraphicsStateGuardian):: -apply_texture(CLP(TextureContext) *gtc) { +apply_texture(CLP(TextureContext) *gtc, int view) { gtc->set_active(true); - GLenum target = get_texture_target(gtc->get_texture()->get_texture_type()); + + GLenum target = gtc->_target; + assert(target != GL_NONE); // REMOVE ME if (target == GL_NONE) { return false; } - if (gtc->_target != target) { - // The target has changed. That means we have to re-bind a new texture - // object. - gtc->reset_data(); - gtc->_target = target; - } - - glBindTexture(target, gtc->_index); + GLuint index = gtc->get_view_index(view); + glBindTexture(target, index); if (GLCAT.is_spam()) { + Texture *tex = gtc->get_texture(); GLCAT.spam() - << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *gtc->get_texture() << "\n"; + << "glBindTexture(GL_TEXTURE_2D, " << index << "): " << *tex + << " view " << view << "\n"; } report_my_gl_errors(); @@ -13597,7 +13715,7 @@ apply_texture(CLP(TextureContext) *gtc) { * applied to the given texture context instead. */ bool CLP(GraphicsStateGuardian):: -apply_sampler(GLuint unit, const SamplerState &sampler, CLP(TextureContext) *gtc) { +apply_sampler(GLuint unit, const SamplerState &sampler, CLP(TextureContext) *gtc, int view) { #ifndef OPENGLES_1 if (_supports_sampler_objects) { // We support sampler objects. Prepare the sampler object and bind it to @@ -13622,19 +13740,18 @@ apply_sampler(GLuint unit, const SamplerState &sampler, CLP(TextureContext) *gtc // change the texture parameters if they don't match. if (gtc->_active_sampler != sampler) { set_active_texture_stage(unit); - apply_texture(gtc); + apply_texture(gtc, view); specify_texture(gtc, sampler); } } - if (sampler.uses_mipmaps() && !gtc->_uses_mipmaps && !gtc->_simple_loaded && !gl_ignore_mipmaps) { + if (sampler.uses_mipmaps() && gtc->_may_reload_with_mipmaps) { // The texture wasn't created with mipmaps, but we are trying to sample it // with mipmaps. We will need to reload it. GLCAT.info() << "reloading texture " << gtc->get_texture()->get_name() << " with mipmaps\n"; - apply_texture(gtc); gtc->mark_needs_reload(); bool okflag = upload_texture(gtc, false, true); if (!okflag) { @@ -13642,6 +13759,8 @@ apply_sampler(GLuint unit, const SamplerState &sampler, CLP(TextureContext) *gtc << "Could not load " << *gtc->get_texture() << "\n"; return false; } + // Make sure that the correct view is bound again after upload_texture. + apply_texture(gtc, view); } report_my_gl_errors(); @@ -13651,6 +13770,8 @@ apply_sampler(GLuint unit, const SamplerState &sampler, CLP(TextureContext) *gtc /** * Uploads the entire texture image to OpenGL, including all pages. * + * It does not need to be currently bound. + * * The return value is true if successful, or false if the texture has no * image. */ @@ -13700,12 +13821,6 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { } } - int mipmap_bias = 0; - - int width = tex->get_x_size(); - int height = tex->get_y_size(); - int depth = tex->get_z_size(); - // If we'll use immutable texture storage, we have to pick a sized image // format. bool force_sized = (gl_immutable_texture_storage && _supports_tex_storage) || @@ -13789,6 +13904,7 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { // instead; of course, the user doesn't always know ahead of time what the // hardware limits are. + int mipmap_bias = 0; if ((max_dimension_x > 0 && max_dimension_y > 0 && max_dimension_z > 0) && image_compression == Texture::CM_off) { while (tex->get_expected_mipmap_x_size(mipmap_bias) > max_dimension_x || @@ -13808,18 +13924,18 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { } } } + } - width = tex->get_expected_mipmap_x_size(mipmap_bias); - height = tex->get_expected_mipmap_y_size(mipmap_bias); - depth = tex->get_expected_mipmap_z_size(mipmap_bias); + int width = tex->get_expected_mipmap_x_size(mipmap_bias); + int height = tex->get_expected_mipmap_y_size(mipmap_bias); + int depth = tex->get_expected_mipmap_z_size(mipmap_bias); - if (mipmap_bias != 0) { - GLCAT.info() - << "Reducing image " << tex->get_name() - << " from " << tex->get_x_size() << " x " << tex->get_y_size() - << " x " << tex->get_z_size() << " to " - << width << " x " << height << " x " << depth << "\n"; - } + if (mipmap_bias != 0) { + GLCAT.info() + << "Reducing image " << tex->get_name() + << " from " << tex->get_x_size() << " x " << tex->get_y_size() + << " x " << tex->get_z_size() << " to " + << width << " x " << height << " x " << depth << "\n"; } if (image_compression != Texture::CM_off) { @@ -13843,68 +13959,30 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - GLenum target = get_texture_target(texture_type); - uses_mipmaps = (uses_mipmaps && !gl_ignore_mipmaps) || gl_force_mipmaps; -#ifndef OPENGLES - if (target == GL_TEXTURE_BUFFER) { - // Buffer textures may not have mipmaps. - uses_mipmaps = false; - } -#endif // OPENGLES - bool needs_reload = false; if (!gtc->_has_storage || - gtc->_uses_mipmaps != uses_mipmaps || gtc->_internal_format != internal_format || gtc->_width != width || gtc->_height != height || gtc->_depth != depth) { // We need to reload a new GL Texture object. needs_reload = true; - - if (_use_object_labels) { - // This seems like a good time to assign a label for the debug messages. - const string &name = tex->get_name(); - _glObjectLabel(GL_TEXTURE, gtc->_index, name.size(), name.data()); - } } - if (needs_reload && gtc->_immutable) { - GLCAT.info() << "Attempt to modify texture with immutable storage, recreating texture.\n"; - gtc->reset_data(); - glBindTexture(target, gtc->_index); - - if (GLCAT.is_spam()) { - GLCAT.spam() - << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *tex << "\n"; - } + // Figure out whether mipmaps will be generated by the GPU or by Panda (or + // not at all), and how many mipmap levels should be created. + uses_mipmaps = (uses_mipmaps && !gl_ignore_mipmaps) || gl_force_mipmaps; + int num_levels = 1; + if (texture_type == Texture::TT_buffer_texture || + (width == 1 && height == 1 && depth == 1)) { + // Buffer textures, or 1x1x1 textures, never have mipmaps. + gtc->_num_levels = 1; + gtc->_generate_mipmaps = false; + gtc->_may_reload_with_mipmaps = false; } - -#ifndef OPENGLES - if (target == GL_TEXTURE_BUFFER) { - // Buffer textures don't support mipmappping. + else if (needs_reload || (uses_mipmaps && gtc->_num_levels <= 1)) { gtc->_generate_mipmaps = false; - - if (gtc->_buffer == 0) { - // The buffer object wasn't created yet. - _glGenBuffers(1, >c->_buffer); - _glBindBuffer(GL_TEXTURE_BUFFER, gtc->_buffer); - _glTexBuffer(GL_TEXTURE_BUFFER, internal_format, gtc->_buffer); - needs_reload = true; - } else { - _glBindBuffer(GL_TEXTURE_BUFFER, gtc->_buffer); - if (gtc->_internal_format != internal_format) { - _glTexBuffer(GL_TEXTURE_BUFFER, internal_format, gtc->_buffer); - } - } - } else -#endif // !OPENGLES - if (needs_reload) { - // Figure out whether mipmaps will be generated by the GPU or by Panda (or - // not at all), and how many mipmap levels should be created. - gtc->_generate_mipmaps = false; - int num_levels = 1; - CPTA_uchar image = tex->get_ram_mipmap_image(mipmap_bias); + gtc->_may_reload_with_mipmaps = false; bool can_generate = _supports_generate_mipmap; #if defined(OPENGLES) && !defined(OPENGLES_1) @@ -13918,17 +13996,16 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { if (image.is_null()) { // We don't even have a RAM image, so we have no choice but to let // mipmaps be generated on the GPU. - if (uses_mipmaps) { - if (can_generate) { + if (can_generate) { + if (uses_mipmaps) { num_levels = tex->get_expected_num_mipmap_levels() - mipmap_bias; gtc->_generate_mipmaps = true; - } else { - // If it can't, do without mipmaps. - num_levels = 1; - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + else if (!gl_ignore_mipmaps) { + // If someone asks for mipmaps, we can generate them. + gtc->_may_reload_with_mipmaps = true; } } - } else { if (uses_mipmaps) { num_levels = tex->get_num_ram_mipmap_images() - mipmap_bias; @@ -13953,11 +14030,202 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { gtc->_generate_mipmaps = true; } else { // If it can't, do without mipmaps. - glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + uses_mipmaps = false; num_levels = 1; } } } + else if (!gl_ignore_mipmaps) { + // We don't yet know whether this might succeed, so we will try if + // someone asks for it. + gtc->_may_reload_with_mipmaps = true; + } + } + if (num_levels > gtc->_num_levels) { + // Always need to reallocate storage when we're adding new mipmap levels. + needs_reload = true; + } + } + else { + // Maybe we need to generate mipmaps on the CPU. + num_levels = gtc->_num_levels; + if (!image.is_null() && uses_mipmaps) { + if (tex->get_num_ram_mipmap_images() - mipmap_bias <= 1) { + // No RAM mipmap levels available. Should we generate some? + if (!_supports_generate_mipmap || !driver_generate_mipmaps || + image_compression != Texture::CM_off) { + // Yes, the GL can't or won't generate them, so we need to. Note + // that some drivers (nVidia) will *corrupt memory* if you ask them + // to generate mipmaps for a pre-compressed texture. + tex->generate_ram_mipmap_images(); + } + } + } + } + + int num_views = tex->get_num_views(); + if (needs_reload) { + if (gtc->_immutable) { + GLCAT.info() + << "Attempt to modify texture with immutable storage, recreating texture.\n"; + gtc->reset_data(gtc->_target, num_views); + } + else if (_supports_tex_storage && gl_immutable_texture_storage) { + gtc->_immutable = true; + } + } + +#ifndef OPENGLES_1 + if (needs_reload || !image.is_null()) { + // Make sure that any incoherent writes to this texture have been synced. + if (gtc->needs_barrier(GL_TEXTURE_UPDATE_BARRIER_BIT)) { + issue_memory_barrier(GL_TEXTURE_UPDATE_BARRIER_BIT); + } + } +#endif + + // Make sure we have a GL texture name for every view of the texture. + int old_num_views = gtc->_num_views; + if (old_num_views != num_views) { + gtc->set_num_views(num_views); + } + + // For a buffer texture, make sure we've created the buffer indices. + if (texture_type == Texture::TT_buffer_texture) { + nassertr(gtc->_buffers != nullptr, false); + } + + bool extract_success = false; + if (tex->get_post_load_store_cache()) { + extract_success = true; + } + + bool success = true; + for (int view = 0; view < num_views; ++view) { + if (upload_texture_image(gtc, view, needs_reload || view >= old_num_views, + mipmap_bias, num_levels, + internal_format, external_format, + component_type, image_compression)) { + gtc->_has_storage = true; + gtc->_internal_format = internal_format; + gtc->_width = width; + gtc->_height = height; + gtc->_depth = depth; + gtc->_num_levels = num_levels; + + if (extract_success) { + // The next call assumes the texture is still bound. + if (!do_extract_texture_data(gtc, view)) { + extract_success = false; + } + } + } + else { + success = false; + } + } + + report_my_gl_errors(); + + if (success) { + if (needs_reload) { + gtc->update_data_size_bytes(get_texture_memory_size(gtc)); + } + + nassertr(gtc->_has_storage, false); + + if (extract_success) { + tex->set_post_load_store_cache(false); + // OK, get the RAM image, and save it in a BamCache record. + if (tex->has_ram_image()) { + BamCache *cache = BamCache::get_global_ptr(); + PT(BamCacheRecord) record = cache->lookup(tex->get_fullpath(), "txo"); + if (record != nullptr) { + record->set_data(tex, tex); + cache->store(record); + } + } + } + + GraphicsEngine *engine = get_engine(); + nassertr(engine != nullptr, false); + engine->texture_uploaded(tex); + gtc->mark_loaded(); + + return true; + } + + return false; +} + +/** + * Loads a texture image, or one page of a cube map image, from system RAM to + * texture memory. + */ +bool CLP(GraphicsStateGuardian):: +upload_texture_image(CLP(TextureContext) *gtc, int view, bool needs_reload, + int mipmap_bias, int num_levels, GLint internal_format, + GLint external_format, GLenum component_type, + Texture::CompressionMode image_compression) { + // Make sure the error stack is cleared out before we begin. + clear_my_gl_errors(); + + if (image_compression != Texture::CM_off && !_supports_compressed_texture) { + return false; + } + + GLenum target = gtc->_target; + if (target == GL_NONE) { + // Unsupported target (e.g. 3-d texturing on GL 1.1). + return false; + } + + Texture *tex = gtc->get_texture(); + nassertr(tex != nullptr, false); + Texture::TextureType texture_type = tex->get_texture_type(); + + GLuint index = gtc->get_view_index(view); + glBindTexture(target, index); + + if (GLCAT.is_spam()) { + GLCAT.spam() + << "glBindTexture(0x" << hex << target << dec << ", " << index << "): " + << *tex << " view " << view << "\n"; + } + + if (_use_object_labels && needs_reload) { + // This seems like a good time to assign a label for the debug messages. + const string &name = tex->get_name(); + if (gtc->_num_views == 1) { + _glObjectLabel(GL_TEXTURE, index, name.size(), name.data()); + } else { + // Add a suffix for a multiview texture. + char *buffer = (char *)alloca(name.size() + 32); + int size = sprintf(buffer, "%s#%d", name.c_str(), view); + _glObjectLabel(GL_TEXTURE, index, size, buffer); + } + } + + CPTA_uchar image = tex->get_ram_mipmap_image(mipmap_bias); + int width = tex->get_expected_mipmap_x_size(mipmap_bias); + int height = tex->get_expected_mipmap_y_size(mipmap_bias); + int depth = tex->get_expected_mipmap_z_size(mipmap_bias); + +#ifndef OPENGLES_1 + if (target == GL_TEXTURE_BUFFER) { + GLuint buffer = gtc->get_view_buffer(view); + nassertr(buffer != 0, false); + _glBindBuffer(GL_TEXTURE_BUFFER, buffer); + if (needs_reload) { + _glTexBuffer(GL_TEXTURE_BUFFER, internal_format, buffer); + } + } else +#endif // !OPENGLES + if (needs_reload) { + if (num_levels <= 1) { + // If we have no mipmaps, tell OpenGL this before we upload the texture + // images, to give it a hint not to expect them. + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } if (_supports_texture_max_level) { @@ -14011,12 +14279,12 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { // using glTexImage to load all of the individual images one by one later, // but we are not allowed to change the texture size or number of mipmap // levels after this point. - if (gl_immutable_texture_storage && _supports_tex_storage && !gtc->_has_storage) { + if (gtc->_immutable) { if (GLCAT.is_debug()) { GLCAT.debug() - << "allocating storage for texture " << tex->get_name() << ", " << width - << " x " << height << " x " << depth << ", mipmaps " << num_levels - << ", uses_mipmaps = " << uses_mipmaps << "\n"; + << "allocating storage for texture " << tex->get_name() << ", " + << width << " x " << height << " x " << depth << ", mipmaps " + << num_levels << "\n"; } switch (texture_type) { @@ -14039,179 +14307,46 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { break; } - gtc->_has_storage = true; - gtc->_simple_loaded = false; - gtc->_immutable = true; - gtc->_uses_mipmaps = uses_mipmaps; - gtc->_internal_format = internal_format; - gtc->_width = width; - gtc->_height = height; - gtc->_depth = depth; - gtc->update_data_size_bytes(get_texture_memory_size(gtc)); - needs_reload = false; } - } else { - // Maybe we need to generate mipmaps on the CPU. - if (!image.is_null() && uses_mipmaps) { - if (tex->get_num_ram_mipmap_images() - mipmap_bias <= 1) { - // No RAM mipmap levels available. Should we generate some? - if (!_supports_generate_mipmap || !driver_generate_mipmaps || - image_compression != Texture::CM_off) { - // Yes, the GL can't or won't generate them, so we need to. Note - // that some drivers (nVidia) will *corrupt memory* if you ask them - // to generate mipmaps for a pre-compressed texture. - tex->generate_ram_mipmap_images(); - } - } - } - } - - bool success = upload_texture_image - (gtc, needs_reload, uses_mipmaps, mipmap_bias, target, - internal_format, external_format, component_type, image_compression); - - if (gtc->_generate_mipmaps && _glGenerateMipmap != nullptr && - !image.is_null()) { - // We uploaded an image; we may need to generate mipmaps. - if (GLCAT.is_debug()) { - GLCAT.debug() - << "generating mipmaps for texture " << tex->get_name() << ", " - << width << " x " << height << " x " << depth - << ", uses_mipmaps = " << uses_mipmaps << "\n"; - } - _glGenerateMipmap(target); - } - - maybe_gl_finish(); - - if (success) { - if (needs_reload) { - gtc->_has_storage = true; - gtc->_simple_loaded = false; - gtc->_uses_mipmaps = uses_mipmaps; - gtc->_internal_format = internal_format; - gtc->_width = width; - gtc->_height = height; - gtc->_depth = depth; - - gtc->update_data_size_bytes(get_texture_memory_size(gtc)); - } - - nassertr(gtc->_has_storage, false); - - if (tex->get_post_load_store_cache()) { - tex->set_post_load_store_cache(false); - // OK, get the RAM image, and save it in a BamCache record. - if (do_extract_texture_data(gtc)) { - if (tex->has_ram_image()) { - BamCache *cache = BamCache::get_global_ptr(); - PT(BamCacheRecord) record = cache->lookup(tex->get_fullpath(), "txo"); - if (record != nullptr) { - record->set_data(tex, tex); - cache->store(record); - } - } - } - } - - GraphicsEngine *engine = get_engine(); - nassertr(engine != nullptr, false); - engine->texture_uploaded(tex); - gtc->mark_loaded(); - - report_my_gl_errors(); - return true; - } - - report_my_gl_errors(); - return false; -} - -/** - * Loads a texture image, or one page of a cube map image, from system RAM to - * texture memory. - */ -bool CLP(GraphicsStateGuardian):: -upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, - bool uses_mipmaps, int mipmap_bias, - GLenum texture_target, - GLint internal_format, - GLint external_format, GLenum component_type, - Texture::CompressionMode image_compression) { - // Make sure the error stack is cleared out before we begin. - clear_my_gl_errors(); - - if (texture_target == GL_NONE) { - // Unsupported target (e.g. 3-d texturing on GL 1.1). - return false; - } - if (image_compression != Texture::CM_off && !_supports_compressed_texture) { - return false; - } - - Texture *tex = gtc->get_texture(); - nassertr(tex != nullptr, false); - - CPTA_uchar image = tex->get_ram_mipmap_image(mipmap_bias); - int width = tex->get_expected_mipmap_x_size(mipmap_bias); - int height = tex->get_expected_mipmap_y_size(mipmap_bias); - int depth = tex->get_expected_mipmap_z_size(mipmap_bias); - - // Determine the number of images to upload. - int num_levels = mipmap_bias + 1; - if (uses_mipmaps) { - num_levels = tex->get_expected_num_mipmap_levels(); } + // How many mipmap levels do we have available to upload? int num_ram_mipmap_levels = 0; if (!image.is_null()) { - if (uses_mipmaps) { - num_ram_mipmap_levels = tex->get_num_ram_mipmap_images(); - } else { - num_ram_mipmap_levels = 1; - } + num_ram_mipmap_levels = std::min(num_levels, tex->get_num_ram_mipmap_images() - mipmap_bias); } -#ifndef OPENGLES_1 - if (needs_reload || num_ram_mipmap_levels > 0) { - // Make sure that any incoherent writes to this texture have been synced. - if (gtc->needs_barrier(GL_TEXTURE_UPDATE_BARRIER_BIT)) { - issue_memory_barrier(GL_TEXTURE_UPDATE_BARRIER_BIT); - } - } -#endif - if (!needs_reload) { // Try to subload the image over the existing GL Texture object, possibly // saving on texture memory fragmentation. if (GLCAT.is_debug()) { - SparseArray pages = gtc->get_image_modified_pages(0); + SparseArray pages = gtc->get_view_modified_pages(view, 0); if (num_ram_mipmap_levels == 0) { if (tex->has_clear_color()) { GLCAT.debug() - << "clearing texture " << tex->get_name() << ", " + << "clearing texture " << tex->get_name() << " view " << view << ", " << width << " x " << height << " x " << depth << ", pages " << pages - << ", uses_mipmaps = " << uses_mipmaps << ", clear_color = " + << ", mipmaps " << num_levels << ", clear_color = " << tex->get_clear_color() << "\n"; } else { GLCAT.debug() << "not loading NULL image for texture " << tex->get_name() - << ", " << width << " x " << height << " x " << depth - << ", pages " << pages << ", uses_mipmaps = " << uses_mipmaps << "\n"; + << " view " << view << ", " << width << " x " << height << " x " << depth + << ", pages " << pages << ", mipmaps = " << num_levels << "\n"; } } else { GLCAT.debug() - << "updating image data of texture " << tex->get_name() - << ", " << width << " x " << height << " x " << depth + << "updating image data of texture " << tex->get_name() << " view " + << view << ", " << width << " x " << height << " x " << depth << ", pages " << pages << ", mipmaps " << num_ram_mipmap_levels - << ", uses_mipmaps = " << uses_mipmaps << "\n"; + << " / " << num_levels << "\n"; } } - for (int n = mipmap_bias; n < num_levels; ++n) { - SparseArray pages = gtc->get_image_modified_pages(n); + for (int n = mipmap_bias; n < num_levels + mipmap_bias; ++n) { + SparseArray pages = gtc->get_view_modified_pages(view, n); // we grab the mipmap pointer first, if it is NULL we grab the normal // mipmap image pointer which is a PTA_uchar @@ -14220,7 +14355,7 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, if (image_ptr == nullptr) { ptimage = tex->get_ram_mipmap_image(n); if (ptimage.is_null()) { - if (n < num_ram_mipmap_levels) { + if (n - mipmap_bias < num_ram_mipmap_levels) { // We were told we'd have this many RAM mipmap images, but we // don't. Raise a warning. GLCAT.warning() @@ -14233,20 +14368,20 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, // The texture has a clear color, so we should fill this mipmap // level to a solid color. #ifndef OPENGLES - if (texture_target != GL_TEXTURE_BUFFER) { + if (target != GL_TEXTURE_BUFFER) { if (_supports_clear_texture) { // We can do that with the convenient glClearTexImage // function. vector_uchar clear_data = tex->get_clear_data(); if (pages.has_all_of(0, depth)) { - _glClearTexImage(gtc->_index, n - mipmap_bias, external_format, + _glClearTexImage(index, n - mipmap_bias, external_format, component_type, (void *)&clear_data[0]); } else for (size_t sri = 0; sri < pages.get_num_subranges(); ++sri) { int begin = pages.get_subrange_begin(sri); int num_pages = pages.get_subrange_end(sri) - begin; - _glClearTexSubImage(gtc->_index, n - mipmap_bias, 0, 0, begin, + _glClearTexSubImage(index, n - mipmap_bias, 0, 0, begin, width, height, num_pages, external_format, component_type, (void *)&clear_data[0]); } @@ -14282,7 +14417,7 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, if (image_ptr != nullptr) { const unsigned char *orig_image_ptr = image_ptr; size_t view_size = tex->get_ram_mipmap_view_size(n); - image_ptr += view_size * gtc->get_view(); + image_ptr += view_size * view; nassertr(image_ptr >= orig_image_ptr && image_ptr + view_size <= orig_image_ptr + tex->get_ram_mipmap_image_size(n), false); if (image_compression == Texture::CM_off) { @@ -14299,7 +14434,7 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, #ifdef DO_PSTATS _data_transferred_pcollector.add_level(page_size * pages.get_num_on_bits()); #endif - switch (texture_target) { + switch (target) { #ifndef OPENGLES_1 case GL_TEXTURE_3D: if (_supports_3d_texture) { @@ -14309,11 +14444,11 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, const unsigned char *page_ptr = image_ptr + page_size * begin; if (image_compression == Texture::CM_off) { - _glTexSubImage3D(texture_target, n - mipmap_bias, + _glTexSubImage3D(target, n - mipmap_bias, 0, 0, begin, width, height, num_pages, external_format, component_type, page_ptr); } else { - _glCompressedTexSubImage3D(texture_target, n - mipmap_bias, + _glCompressedTexSubImage3D(target, n - mipmap_bias, 0, 0, begin, width, height, num_pages, external_format, page_size * num_pages, page_ptr); @@ -14329,10 +14464,10 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, #ifndef OPENGLES case GL_TEXTURE_1D: if (image_compression == Texture::CM_off) { - glTexSubImage1D(texture_target, n - mipmap_bias, 0, width, + glTexSubImage1D(target, n - mipmap_bias, 0, width, external_format, component_type, image_ptr); } else { - _glCompressedTexSubImage1D(texture_target, n - mipmap_bias, 0, width, + _glCompressedTexSubImage1D(target, n - mipmap_bias, 0, width, external_format, page_size, image_ptr); } break; @@ -14348,11 +14483,11 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, const unsigned char *page_ptr = image_ptr + page_size * begin; if (image_compression == Texture::CM_off) { - _glTexSubImage3D(texture_target, n - mipmap_bias, + _glTexSubImage3D(target, n - mipmap_bias, 0, 0, begin, width, height, num_pages, external_format, component_type, page_ptr); } else { - _glCompressedTexSubImage3D(texture_target, n - mipmap_bias, + _glCompressedTexSubImage3D(target, n - mipmap_bias, 0, 0, begin, width, height, num_pages, external_format, page_size * num_pages, page_ptr); @@ -14365,7 +14500,7 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, break; #endif // OPENGLES_1 -#ifndef OPENGLES +#ifndef OPENGLES_1 case GL_TEXTURE_BUFFER: if (_supports_buffer_texture) { _glBufferSubData(GL_TEXTURE_BUFFER, 0, page_size, image_ptr); @@ -14411,10 +14546,10 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, // TexSubImage2D doesn't accept a row-stride parameter. height = tex->get_y_size() - tex->get_pad_y_size(); } - glTexSubImage2D(texture_target, n - mipmap_bias, 0, 0, width, height, + glTexSubImage2D(target, n - mipmap_bias, 0, 0, width, height, external_format, component_type, image_ptr); } else { - _glCompressedTexSubImage2D(texture_target, n - mipmap_bias, 0, 0, width, height, + _glCompressedTexSubImage2D(target, n - mipmap_bias, 0, 0, width, height, external_format, page_size, image_ptr); } break; @@ -14438,9 +14573,9 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, // Load the image up from scratch, creating a new GL Texture object. if (GLCAT.is_debug()) { GLCAT.debug() - << "loading new texture object for " << tex->get_name() << ", " << width - << " x " << height << " x " << depth << ", mipmaps " - << num_ram_mipmap_levels << ", uses_mipmaps = " << uses_mipmaps << "\n"; + << "loading new texture object for " << tex->get_name() << " view " + << view << ", " << width << " x " << height << " x " << depth + << ", mipmaps " << num_ram_mipmap_levels << " / " << num_levels << "\n"; } // If there is immutable storage, this is impossible to do, and we should @@ -14462,13 +14597,13 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, } } - for (int n = mipmap_bias; n < num_levels; ++n) { + for (int n = mipmap_bias; n < num_levels + mipmap_bias; ++n) { const unsigned char *image_ptr = (unsigned char*)tex->get_ram_mipmap_pointer(n); CPTA_uchar ptimage; if (image_ptr == nullptr) { ptimage = tex->get_ram_mipmap_image(n); if (ptimage.is_null()) { - if (n < num_ram_mipmap_levels) { + if (n - mipmap_bias < num_ram_mipmap_levels) { // We were told we'd have this many RAM mipmap images, but we // don't. Raise a warning. GLCAT.warning() @@ -14476,7 +14611,7 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, << "\n"; if (_supports_texture_max_level) { // Tell the GL we have no more mipmaps for it to use. - glTexParameteri(texture_target, GL_TEXTURE_MAX_LEVEL, n - mipmap_bias); + glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, n - mipmap_bias); } break; } @@ -14486,12 +14621,12 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, // fill it in with the correct clear color, which we can then // upload. ptimage = tex->make_ram_mipmap_image(n); - - } else if (image_compression != Texture::CM_off) { + } + else if (image_compression != Texture::CM_off) { // We can't upload a NULL compressed texture. if (_supports_texture_max_level) { // Tell the GL we have no more mipmaps for it to use. - glTexParameteri(texture_target, GL_TEXTURE_MAX_LEVEL, n - mipmap_bias); + glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, n - mipmap_bias); } break; } @@ -14503,7 +14638,7 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, size_t view_size = tex->get_ram_mipmap_view_size(n); if (image_ptr != nullptr) { const unsigned char *orig_image_ptr = image_ptr; - image_ptr += view_size * gtc->get_view(); + image_ptr += view_size * view; nassertr(image_ptr >= orig_image_ptr && image_ptr + view_size <= orig_image_ptr + tex->get_ram_mipmap_image_size(n), false); if (image_compression == Texture::CM_off) { @@ -14523,14 +14658,14 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, #ifdef DO_PSTATS _data_transferred_pcollector.add_level(view_size); #endif - switch (texture_target) { + switch (target) { #ifndef OPENGLES // 1-d textures not supported by OpenGL ES. Fall through. case GL_TEXTURE_1D: if (image_compression == Texture::CM_off) { - glTexImage1D(texture_target, n - mipmap_bias, internal_format, + glTexImage1D(target, n - mipmap_bias, internal_format, width, 0, external_format, component_type, image_ptr); } else { - _glCompressedTexImage1D(texture_target, n - mipmap_bias, external_format, + _glCompressedTexImage1D(target, n - mipmap_bias, external_format, width, 0, view_size, image_ptr); } break; @@ -14540,11 +14675,11 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, case GL_TEXTURE_3D: if (_supports_3d_texture) { if (image_compression == Texture::CM_off) { - _glTexImage3D(texture_target, n - mipmap_bias, internal_format, + _glTexImage3D(target, n - mipmap_bias, internal_format, width, height, depth, 0, external_format, component_type, image_ptr); } else { - _glCompressedTexImage3D(texture_target, n - mipmap_bias, external_format, + _glCompressedTexImage3D(target, n - mipmap_bias, external_format, width, height, depth, 0, view_size, image_ptr); } } else { @@ -14559,11 +14694,11 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, case GL_TEXTURE_CUBE_MAP_ARRAY: if (_supports_2d_texture_array) { if (image_compression == Texture::CM_off) { - _glTexImage3D(texture_target, n - mipmap_bias, internal_format, + _glTexImage3D(target, n - mipmap_bias, internal_format, width, height, depth, 0, external_format, component_type, image_ptr); } else { - _glCompressedTexImage3D(texture_target, n - mipmap_bias, external_format, + _glCompressedTexImage3D(target, n - mipmap_bias, external_format, width, height, depth, 0, view_size, image_ptr); } } else { @@ -14571,9 +14706,7 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, return false; } break; -#endif // OPENGLES_1 -#ifndef OPENGLES case GL_TEXTURE_BUFFER: if (_supports_buffer_texture) { _glBufferData(GL_TEXTURE_BUFFER, view_size, image_ptr, @@ -14583,7 +14716,7 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, return false; } break; -#endif // OPENGLES +#endif // OPENGLES_1 case GL_TEXTURE_CUBE_MAP: if (_supports_cube_map) { @@ -14612,11 +14745,11 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, default: if (image_compression == Texture::CM_off) { - glTexImage2D(texture_target, n - mipmap_bias, internal_format, + glTexImage2D(target, n - mipmap_bias, internal_format, width, height, 0, external_format, component_type, image_ptr); } else { - _glCompressedTexImage2D(texture_target, n - mipmap_bias, external_format, + _glCompressedTexImage2D(target, n - mipmap_bias, external_format, width, height, 0, view_size, image_ptr); } } @@ -14634,6 +14767,17 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, } } + if (gtc->_generate_mipmaps && _glGenerateMipmap != nullptr && !image.is_null()) { + // We uploaded an image; we may need to generate mipmaps. + if (GLCAT.is_debug()) { + GLCAT.debug() + << "generating mipmaps for texture " << tex->get_name() << " view " + << view << ", " << width << " x " << height << " x " << depth + << ", mipmaps = " << num_levels << "\n"; + } + _glGenerateMipmap(target); + } + report_my_gl_errors(); return true; @@ -14644,10 +14788,18 @@ upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, */ void CLP(GraphicsStateGuardian):: generate_mipmaps(CLP(TextureContext) *gtc) { + gtc->set_active(true); + + if (gtc->_num_levels <= 1) { + return; + } + #ifndef OPENGLES if (_supports_dsa) { // OpenGL 4.5 offers an easy way to do this without binding. - _glGenerateTextureMipmap(gtc->_index); + for (int view = 0; view < gtc->_num_views; ++view) { + _glGenerateTextureMipmap(gtc->_indices[view]); + } return; } #endif @@ -14655,9 +14807,20 @@ generate_mipmaps(CLP(TextureContext) *gtc) { if (_glGenerateMipmap != nullptr) { _state_texture = 0; update_texture(gtc, true); - apply_texture(gtc); - _glGenerateMipmap(gtc->_target); - glBindTexture(gtc->_target, 0); + + GLenum target = gtc->_target; + for (int view = 0; view < gtc->_num_views; ++view) { + glBindTexture(target, gtc->_indices[view]); + _glGenerateMipmap(target); + } + glBindTexture(target, 0); + + if (GLCAT.is_spam()) { + GLCAT.spam() + << "glBindTexture(0x" << hex << target << dec << ", 0)\n"; + } + + report_my_gl_errors(); } } @@ -14675,6 +14838,15 @@ upload_simple_texture(CLP(TextureContext) *gtc) { Texture *tex = gtc->get_texture(); nassertr(tex != nullptr, false); + gtc->set_num_views(1); + GLuint index = gtc->get_view_index(0); + glBindTexture(GL_TEXTURE_2D, index); + + if (GLCAT.is_spam()) { + GLCAT.spam() + << "glBindTexture(GL_TEXTURE_2D, " << index << "): " << *tex << " simple\n"; + } + #ifdef OPENGLES GLenum internal_format = GL_BGRA; #else @@ -14711,7 +14883,7 @@ upload_simple_texture(CLP(TextureContext) *gtc) { } // Turn off mipmaps for the simple texture. - if (tex->uses_mipmaps() && _supports_texture_max_level) { + if (_supports_texture_max_level) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); } @@ -14727,13 +14899,13 @@ upload_simple_texture(CLP(TextureContext) *gtc) { external_format, component_type, image_ptr); gtc->_has_storage = true; - gtc->_simple_loaded = true; gtc->_immutable = false; - gtc->_uses_mipmaps = false; gtc->_internal_format = internal_format; gtc->_width = width; gtc->_height = height; gtc->_depth = 1; + gtc->_num_levels = 1; + gtc->_may_reload_with_mipmaps = false; gtc->update_data_size_bytes(width * height * 4); gtc->mark_loaded(); @@ -14783,7 +14955,7 @@ get_texture_memory_size(CLP(TextureContext) *gtc) { // Try to get the compressed size. GLint image_size; glGetTexLevelParameteriv(page_target, 0, - GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &image_size); + GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &image_size); GLenum error_code = gl_get_error(); if (error_code != GL_NO_ERROR) { @@ -14834,11 +15006,14 @@ get_texture_memory_size(CLP(TextureContext) *gtc) { #endif // OPENGLES size_t result = num_bytes * width * height * depth * scale; - if (gtc->_uses_mipmaps) { - result = (result * 4) / 3; + for (int n = 1; n < gtc->_num_levels; ++n) { + width = max(1, width << 1); + height = max(1, height << 1); + depth = max(1, depth << 1); + result += num_bytes * width * height * depth * scale; } - return result; + return result * gtc->_num_views; } /** @@ -14858,9 +15033,10 @@ check_nonresident_texture(BufferContextChain &chain) { size_t ti = 0; BufferContext *node = chain.get_first(); while (node != nullptr) { + // For now, we just check whether the first view is resident. CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), node); gtc_list[ti] = gtc; - texture_list[ti] = gtc->_index; + texture_list[ti] = gtc->get_view_index(0); node = node->get_next(); ++ti; } @@ -14884,9 +15060,11 @@ check_nonresident_texture(BufferContextChain &chain) { /** * The internal implementation of extract_texture_data(), given an already- * created TextureContext. + * + * Assumes that the texture is already bound. */ bool CLP(GraphicsStateGuardian):: -do_extract_texture_data(CLP(TextureContext) *gtc) { +do_extract_texture_data(CLP(TextureContext) *gtc, int view) { report_my_gl_errors(); GLenum target = gtc->_target; @@ -14903,18 +15081,6 @@ do_extract_texture_data(CLP(TextureContext) *gtc) { Texture *tex = gtc->get_texture(); - glBindTexture(target, gtc->_index); - if (GLCAT.is_spam()) { - GLCAT.spam() - << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *tex << "\n"; - } - -#ifndef OPENGLES - if (target == GL_TEXTURE_BUFFER) { - _glBindBuffer(GL_TEXTURE_BUFFER, gtc->_buffer); - } -#endif - GLint wrap_u, wrap_v, wrap_w; GLint minfilter, magfilter; @@ -14922,7 +15088,7 @@ do_extract_texture_data(CLP(TextureContext) *gtc) { GLfloat border_color[4]; #endif -#ifdef OPENGLES +#ifdef OPENGLES_1 if (true) { #else if (target != GL_TEXTURE_BUFFER) { @@ -15466,7 +15632,7 @@ do_extract_texture_data(CLP(TextureContext) *gtc) { tex->set_component_type(type); tex->set_format(format); -#ifdef OPENGLES +#ifdef OPENGLES_1 if (true) { #else if (target != GL_TEXTURE_BUFFER) { @@ -15500,29 +15666,20 @@ do_extract_texture_data(CLP(TextureContext) *gtc) { // existing content. PTA_uchar ram_image = tex->modify_ram_image(); nassertr(ram_image.size() == image.size() * num_views, false); - memcpy(ram_image.p() + image.size() * gtc->get_view(), image.p(), image.size()); + memcpy(ram_image.p() + image.size() * view, image.p(), image.size()); } - if (gtc->_uses_mipmaps) { - // Also get the mipmap levels. - GLint num_expected_levels = tex->get_expected_num_mipmap_levels(); - GLint highest_level = num_expected_levels; - - if (_supports_texture_max_level) { - glGetTexParameteriv(target, GL_TEXTURE_MAX_LEVEL, &highest_level); - highest_level = min(highest_level, num_expected_levels); + // Also get the mipmap levels. + for (int n = 1; n < gtc->_num_levels; ++n) { + if (!extract_texture_image(image, page_size, tex, target, page_target, + type, compression, n)) { + return false; } - for (int n = 1; n <= highest_level; ++n) { - if (!extract_texture_image(image, page_size, tex, target, page_target, - type, compression, n)) { - return false; - } - if (num_views == 1) { - tex->set_ram_mipmap_image(n, image, page_size); - } else { - PTA_uchar ram_mipmap_image = tex->modify_ram_mipmap_image(n); - memcpy(ram_mipmap_image.p() + image.size() * gtc->get_view(), image.p(), image.size()); - } + if (num_views == 1) { + tex->set_ram_mipmap_image(n, image, page_size); + } else { + PTA_uchar ram_mipmap_image = tex->modify_ram_mipmap_image(n); + memcpy(ram_mipmap_image.p() + image.size() * view, image.p(), image.size()); } } @@ -15583,7 +15740,7 @@ extract_texture_image(PTA_uchar &image, size_t &page_size, } } -#ifndef OPENGLES +#ifndef OPENGLES_1 } else if (target == GL_TEXTURE_BUFFER) { // In the case of a buffer texture, we need to get it from the buffer. image = PTA_uchar::empty_array(tex->get_expected_ram_mipmap_view_size(n)); diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 92db648eb1..dc44c68d61 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -143,6 +143,7 @@ typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); +typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); #ifndef OPENGLES_1 // GLSL shader functions @@ -343,7 +344,7 @@ public: void issue_memory_barrier(GLbitfield barrier); #endif - virtual TextureContext *prepare_texture(Texture *tex, int view); + virtual TextureContext *prepare_texture(Texture *tex); virtual bool update_texture(TextureContext *tc, bool force); virtual void release_texture(TextureContext *tc); virtual void release_textures(const pvector &contexts); @@ -632,12 +633,12 @@ protected: #endif // NDEBUG bool specify_texture(CLP(TextureContext) *gtc, const SamplerState &sampler); - bool apply_texture(CLP(TextureContext) *gtc); - bool apply_sampler(GLuint unit, const SamplerState &sampler, CLP(TextureContext) *gtc); + bool apply_texture(CLP(TextureContext) *gtc, int view); + bool apply_sampler(GLuint unit, const SamplerState &sampler, + CLP(TextureContext) *gtc, int view); bool upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps); - bool upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload, - bool uses_mipmaps, int mipmap_bias, - GLenum texture_target, + bool upload_texture_image(CLP(TextureContext) *gtc, int view, + bool needs_reload, int mipmap_bias, int num_levels, GLint internal_format, GLint external_format, GLenum component_type, Texture::CompressionMode image_compression); @@ -646,7 +647,7 @@ protected: size_t get_texture_memory_size(CLP(TextureContext) *gtc); void check_nonresident_texture(BufferContextChain &chain); - bool do_extract_texture_data(CLP(TextureContext) *gtc); + bool do_extract_texture_data(CLP(TextureContext) *gtc, int view); bool extract_texture_image(PTA_uchar &image, size_t &page_size, Texture *tex, GLenum target, GLenum page_target, Texture::ComponentType type, @@ -833,7 +834,7 @@ public: PFNGLTEXSTORAGE2DPROC _glTexStorage2D; PFNGLTEXSTORAGE3DPROC _glTexStorage3D; -#ifndef OPENGLES +#ifndef OPENGLES_1 PFNGLTEXBUFFERPROC _glTexBuffer; #endif @@ -967,7 +968,12 @@ public: #ifndef OPENGLES bool _supports_dsa; + PFNGLCREATETEXTURESPROC _glCreateTextures; + PFNGLTEXTURESTORAGE2DPROC _glTextureStorage2D; + PFNGLTEXTURESUBIMAGE2DPROC _glTextureSubImage2D; + PFNGLTEXTUREPARAMETERIPROC _glTextureParameteri; PFNGLGENERATETEXTUREMIPMAPPROC _glGenerateTextureMipmap; + PFNGLBINDTEXTUREUNITPROC _glBindTextureUnit; #endif #ifndef OPENGLES_1 diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index 077e9e1d79..c3af8f63fa 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -1461,6 +1461,15 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) { if (param_size == 1) { // A single uniform (not an array, or an array of size 1). switch (param_type) { +#ifndef OPENGLES + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_SAMPLER_1D: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_1D_SHADOW: +#endif case GL_INT_SAMPLER_2D: case GL_INT_SAMPLER_3D: case GL_INT_SAMPLER_2D_ARRAY: @@ -1472,22 +1481,13 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) { case GL_SAMPLER_CUBE_SHADOW: case GL_SAMPLER_2D_ARRAY: case GL_SAMPLER_2D_ARRAY_SHADOW: -#ifndef OPENGLES - case GL_INT_SAMPLER_1D: - case GL_INT_SAMPLER_1D_ARRAY: case GL_INT_SAMPLER_BUFFER: case GL_INT_SAMPLER_CUBE_MAP_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_1D: - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: case GL_UNSIGNED_INT_SAMPLER_BUFFER: case GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY: - case GL_SAMPLER_1D: - case GL_SAMPLER_1D_ARRAY: - case GL_SAMPLER_1D_SHADOW: case GL_SAMPLER_BUFFER: case GL_SAMPLER_CUBE_MAP_ARRAY: case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW: -#endif // !OPENGLES case GL_SAMPLER_2D: case GL_SAMPLER_2D_SHADOW: case GL_SAMPLER_3D: @@ -1679,18 +1679,22 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) { case GL_UNSIGNED_INT_IMAGE_2D_ARRAY: #ifndef OPENGLES case GL_IMAGE_1D: + case GL_INT_IMAGE_1D: + case GL_UNSIGNED_INT_IMAGE_1D: +#endif case GL_IMAGE_CUBE_MAP_ARRAY: case GL_IMAGE_BUFFER: - case GL_INT_IMAGE_1D: case GL_INT_IMAGE_CUBE_MAP_ARRAY: case GL_INT_IMAGE_BUFFER: - case GL_UNSIGNED_INT_IMAGE_1D: case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY: case GL_UNSIGNED_INT_IMAGE_BUFFER: -#endif // This won't really change at runtime, so we might as well bind once // and then forget about it. + // Note that OpenGL ES doesn't support changing this at runtime, so we + // rely on the shader using a layout declaration. +#ifndef OPENGLES _glgsg->_glUniform1i(p, _glsl_img_inputs.size()); +#endif { ImageInput input; input._name = InternalName::make(param_name); @@ -1881,7 +1885,6 @@ get_sampler_texture_type(int &out, GLenum param_type) { return false; } -#ifndef OPENGLES case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW: if (!_glgsg->_supports_shadow_filter) { GLCAT.error() @@ -1913,7 +1916,6 @@ get_sampler_texture_type(int &out, GLenum param_type) { << "GLSL shader uses buffer texture, which is unsupported by the driver.\n"; return false; } -#endif // !OPENGLES default: GLCAT.error() @@ -2598,57 +2600,26 @@ disable_shader_texture_bindings() { DO_PSTATS_STUFF(_glgsg->_texture_state_pcollector.add_level(1)); - for (size_t i = 0; i < _shader->_tex_spec.size(); ++i) { #ifndef OPENGLES - // Check if bindless was used, if so, there's nothing to unbind. - if (_glgsg->_supports_bindless_texture) { - GLint p = _shader->_tex_spec[i]._id._seqno; + if (_glgsg->_supports_multi_bind) { + _glgsg->_glBindTextures(0, _shader->_tex_spec.size(), nullptr); + } + else if (_glgsg->_supports_dsa) { + for (size_t i = 0; i < _shader->_tex_spec.size(); ++i) { + _glgsg->_glBindTextureUnit(i, 0); + } + } + else +#endif + { + for (size_t i = 0; i < _shader->_tex_spec.size(); ++i) { + _glgsg->set_active_texture_stage(i); - if (_glsl_uniform_handles.count(p) > 0) { - continue; + GLenum target = _glgsg->get_texture_target((Texture::TextureType)_shader->_tex_spec[i]._desired_type); + if (target != GL_NONE) { + glBindTexture(target, 0); } } - - if (_glgsg->_supports_multi_bind) { - // There are non-bindless textures to unbind, and we're lazy, so let's - // go and unbind everything after this point using one multi-bind call, - // and then break out of the loop. - _glgsg->_glBindTextures(i, _shader->_tex_spec.size() - i, nullptr); - break; - } -#endif - - _glgsg->set_active_texture_stage(i); - - switch (_shader->_tex_spec[i]._desired_type) { - case Texture::TT_1d_texture: -#ifndef OPENGLES - glBindTexture(GL_TEXTURE_1D, 0); -#endif - break; - - case Texture::TT_2d_texture: - glBindTexture(GL_TEXTURE_2D, 0); - break; - - case Texture::TT_3d_texture: - glBindTexture(GL_TEXTURE_3D, 0); - break; - - case Texture::TT_2d_texture_array: - glBindTexture(GL_TEXTURE_2D_ARRAY, 0); - break; - - case Texture::TT_cube_map: - glBindTexture(GL_TEXTURE_CUBE_MAP, 0); - break; - - case Texture::TT_buffer_texture: -#ifndef OPENGLES - glBindTexture(GL_TEXTURE_BUFFER, 0); -#endif - break; - } } // Now unbind all the image units. Not sure if we *have* to do this. @@ -2738,14 +2709,14 @@ update_shader_texture_bindings(ShaderContext *prev) { CLP(TextureContext) *gtc; if (tex != nullptr) { - int view = _glgsg->get_current_tex_view_offset(); - - gtc = DCAST(CLP(TextureContext), tex->prepare_now(view, _glgsg->_prepared_objects, _glgsg)); + gtc = DCAST(CLP(TextureContext), tex->prepare_now(_glgsg->_prepared_objects, _glgsg)); if (gtc != nullptr) { input._gtc = gtc; _glgsg->update_texture(gtc, true); - gl_tex = gtc->_index; + + int view = _glgsg->get_current_tex_view_offset(); + gl_tex = gtc->get_view_index(view); #ifndef OPENGLES if (gtc->needs_barrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)) { @@ -2880,7 +2851,7 @@ update_shader_texture_bindings(ShaderContext *prev) { // enabled. } - CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tex->prepare_now(view, _glgsg->_prepared_objects, _glgsg)); + CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tex->prepare_now(_glgsg->_prepared_objects, _glgsg)); if (gtc == nullptr) { if (multi_bind) { textures[i] = 0; @@ -2890,49 +2861,13 @@ update_shader_texture_bindings(ShaderContext *prev) { } #ifndef OPENGLES - GLint p = spec._id._seqno; - // If it was recently written to, we will have to issue a memory barrier // soon. if (gtc->needs_barrier(GL_TEXTURE_FETCH_BARRIER_BIT)) { barriers |= GL_TEXTURE_FETCH_BARRIER_BIT; } - - // Try bindless texturing first, if supported. - if (gl_use_bindless_texture && _glgsg->_supports_bindless_texture) { - // We demand the real texture, since we won't be able to change the - // texture properties after this point. - if (multi_bind) { - textures[i] = 0; - samplers[i] = 0; - } - if (!_glgsg->update_texture(gtc, true)) { - continue; - } - - GLuint64 handle = gtc->get_handle(); - if (handle != 0) { - gtc->make_handle_resident(); - gtc->set_active(true); - - // Check if we have already specified this texture handle. If so, no - // need to call glUniformHandle again. - pmap::const_iterator it; - it = _glsl_uniform_handles.find(p); - if (it != _glsl_uniform_handles.end() && it->second == handle) { - // Already specified. - continue; - } else { - _glgsg->_glUniformHandleui64(p, handle); - _glsl_uniform_handles[p] = handle; - } - continue; - } - } #endif - // Bindless texturing wasn't supported or didn't work, so let's just bind - // the texture normally. // Note that simple RAM images are always 2-D for now, so to avoid errors, // we must load the real texture if this is not for a sampler2D. bool force = (spec._desired_type != Texture::TT_2d_texture); @@ -2943,7 +2878,7 @@ update_shader_texture_bindings(ShaderContext *prev) { textures[i] = 0; } else { gtc->set_active(true); - textures[i] = gtc->_index; + textures[i] = gtc->get_view_index(view); } SamplerContext *sc = sampler.prepare_now(_glgsg->get_prepared_objects(), _glgsg); @@ -2962,8 +2897,8 @@ update_shader_texture_bindings(ShaderContext *prev) { if (!_glgsg->update_texture(gtc, force)) { continue; } - _glgsg->apply_texture(gtc); - _glgsg->apply_sampler(i, sampler, gtc); + _glgsg->apply_texture(gtc, view); + _glgsg->apply_sampler(i, sampler, gtc, view); } } diff --git a/panda/src/glstuff/glShaderContext_src.h b/panda/src/glstuff/glShaderContext_src.h index 493da7681d..878941c114 100644 --- a/panda/src/glstuff/glShaderContext_src.h +++ b/panda/src/glstuff/glShaderContext_src.h @@ -93,9 +93,6 @@ private: GLsizei _slider_table_size; GLint _frame_number_loc; GLint _frame_number; -#ifndef OPENGLES - pmap _glsl_uniform_handles; -#endif #ifndef OPENGLES struct StorageBlock { diff --git a/panda/src/glstuff/glTextureContext_src.I b/panda/src/glstuff/glTextureContext_src.I index 51db7538ba..bde6a4d977 100644 --- a/panda/src/glstuff/glTextureContext_src.I +++ b/panda/src/glstuff/glTextureContext_src.I @@ -16,26 +16,46 @@ */ INLINE CLP(TextureContext):: CLP(TextureContext)(CLP(GraphicsStateGuardian) *glgsg, - PreparedGraphicsObjects *pgo, Texture *tex, int view) : - TextureContext(pgo, tex, view) + PreparedGraphicsObjects *pgo, Texture *tex) : + TextureContext(pgo, tex), + _num_views(0) { _glgsg = glgsg; - glGenTextures(1, &_index); - _buffer = 0; + _index = 0; + _indices = &_index; + + _buffer = 0; + _buffers = nullptr; -#ifndef OPENGLES - _handle = 0; - _handle_resident = false; -#endif _has_storage = false; - _simple_loaded = false; _immutable = false; - _uses_mipmaps = false; + _may_reload_with_mipmaps = false; _generate_mipmaps = false; _internal_format = 0; _width = 0; _height = 0; _depth = 0; + _num_levels = 0; _target = GL_NONE; } + +/** + * Returns the index for the given view of the texture. + */ +INLINE GLuint CLP(TextureContext):: +get_view_index(int view) const { + return _indices[std::min(std::max(view, 0), _num_views - 1)]; +} + +/** + * Returns the buffer index for the given view of the texture. + */ +INLINE GLuint CLP(TextureContext):: +get_view_buffer(int view) const { + if (_buffers != nullptr) { + return _buffers[std::min(std::max(view, 0), _num_views - 1)]; + } else { + return 0; + } +} diff --git a/panda/src/glstuff/glTextureContext_src.cxx b/panda/src/glstuff/glTextureContext_src.cxx index 37bad3ae3a..7572ef8a4f 100644 --- a/panda/src/glstuff/glTextureContext_src.cxx +++ b/panda/src/glstuff/glTextureContext_src.cxx @@ -37,53 +37,29 @@ void CLP(TextureContext):: evict_lru() { dequeue_lru(); -#ifndef OPENGLES - if (_handle != 0) { - if (_handle_resident) { - _glgsg->_glMakeTextureHandleNonResident(_handle); - } - _handle_resident = false; - } else -#endif - { - reset_data(); - } - + reset_data(_target); update_data_size_bytes(0); mark_unloaded(); } /** * Resets the texture object to a new one so a new GL texture object can be - * uploaded. + * uploaded. This call also allows the texture target to be changed. */ void CLP(TextureContext):: -reset_data() { -#ifndef OPENGLES - if (_handle != 0 && _handle_resident) { - _glgsg->_glMakeTextureHandleNonResident(_handle); - } -#endif - +reset_data(GLenum target, int num_views) { // Free the texture resources. - glDeleteTextures(1, &_index); + set_num_views(0); - if (_buffer != 0) { - _glgsg->_glDeleteBuffers(1, &_buffer); - _buffer = 0; - } + _target = target; // We still need a valid index number, though, in case we want to re-load // the texture later. - glGenTextures(1, &_index); + set_num_views(num_views); -#ifndef OPENGLES - _handle = 0; - _handle_resident = false; -#endif _has_storage = false; - _simple_loaded = false; _immutable = false; + _may_reload_with_mipmaps = false; #ifndef OPENGLES_1 // Mark the texture as coherent. @@ -118,41 +94,77 @@ get_native_buffer_id() const { } /** - * + * Changes the number of views in the texture. */ -#ifndef OPENGLES void CLP(TextureContext):: -make_handle_resident() { - if (_handle != 0) { - if (!_handle_resident) { - _glgsg->_glMakeTextureHandleResident(_handle); - _handle_resident = true; +set_num_views(int num_views) { + if (_num_views > num_views) { + glDeleteTextures(_num_views - num_views, _indices + _num_views); + + if (_buffers != nullptr) { + _glgsg->_glDeleteBuffers(_num_views - num_views, _buffers + num_views); } - set_resident(true); - } -} + + if (num_views <= 1) { + _index = _indices[0]; + if (_indices != &_index) { + delete[] _indices; + _indices = &_index; + } + +#ifndef OPENGLES_1 + if (_buffers != nullptr) { + _buffer = _buffers[0]; + if (_buffers != &_buffer) { + delete[] _buffers; + _buffers = &_buffer; + } + if (num_views == 0) { + _buffers = nullptr; + } + } #endif - -/** - * Returns a handle for this texture. Once this has been created, the texture - * data may still be updated, but its properties may not. - */ -#ifndef OPENGLES -INLINE GLuint64 CLP(TextureContext):: -get_handle() { - return 0; - if (!_glgsg->_supports_bindless_texture) { - return false; + } } + else if (_num_views == 0 && num_views == 1) { + glGenTextures(1, &_index); + _indices = &_index; - if (_handle == 0) { - _handle = _glgsg->_glGetTextureHandle(_index); - } - - _immutable = true; - return _handle; -} +#ifndef OPENGLES_1 + if (_target == GL_TEXTURE_BUFFER) { + _glgsg->_glGenBuffers(1, &_buffer); + _buffers = &_buffer; + } #endif + } + else if (_num_views < num_views) { + GLuint *new_indices = new GLuint[num_views]; + memcpy(new_indices, _indices, sizeof(GLuint) * _num_views); + glGenTextures(num_views - _num_views, new_indices + _num_views); + if (_indices != &_index) { + delete[] _indices; + } + _indices = new_indices; + +#ifndef OPENGLES_1 + if (_target == GL_TEXTURE_BUFFER) { + GLuint *new_buffers = new GLuint[num_views]; + if (_buffers != nullptr) { + memcpy(new_buffers, _buffers, sizeof(GLuint) * _num_views); + _glgsg->_glGenBuffers(num_views - _num_views, new_buffers + _num_views); + if (_buffers != &_buffer) { + delete[] _buffers; + } + } else { + _glgsg->_glGenBuffers(num_views, new_buffers); + } + _buffers = new_buffers; + } +#endif + } + + _num_views = num_views; +} #ifndef OPENGLES_1 /** diff --git a/panda/src/glstuff/glTextureContext_src.h b/panda/src/glstuff/glTextureContext_src.h index 41a89cb32b..c424488471 100644 --- a/panda/src/glstuff/glTextureContext_src.h +++ b/panda/src/glstuff/glTextureContext_src.h @@ -25,21 +25,20 @@ class CLP(SamplerContext); class EXPCL_GL CLP(TextureContext) : public TextureContext { public: INLINE CLP(TextureContext)(CLP(GraphicsStateGuardian) *glgsg, - PreparedGraphicsObjects *pgo, - Texture *tex, int view); + PreparedGraphicsObjects *pgo, Texture *tex); ALLOC_DELETED_CHAIN(CLP(TextureContext)); virtual ~CLP(TextureContext)(); virtual void evict_lru(); - void reset_data(); + void reset_data(GLenum target, int num_views = 1); virtual uint64_t get_native_id() const; virtual uint64_t get_native_buffer_id() const; -#ifndef OPENGLES - void make_handle_resident(); - GLuint64 get_handle(); -#endif + void set_num_views(int num_views); + + INLINE GLuint get_view_index(int view) const; + INLINE GLuint get_view_buffer(int view) const; #ifdef OPENGLES_1 static constexpr bool needs_barrier(GLbitfield barrier) { return false; }; @@ -48,30 +47,32 @@ public: void mark_incoherent(bool wrote); #endif +private: // This is the GL "name" of the texture object. GLuint _index; // This is only used for buffer textures. GLuint _buffer; -#ifndef OPENGLES - // This is the bindless "handle" to the texture object. - GLuint64 _handle; - bool _handle_resident; -#endif +public: + // Multiview textures have multiple of the above. For a single-view texture, + // these are simply pointers to the above fields. + int _num_views; + GLuint *_indices; + GLuint *_buffers; // These are the parameters that we specified with the last glTexImage2D() // or glTexStorage2D() call. If none of these have changed, we can reload // the texture image with a glTexSubImage2D(). bool _has_storage; - bool _simple_loaded; bool _immutable; - bool _uses_mipmaps; + bool _may_reload_with_mipmaps; bool _generate_mipmaps; GLint _internal_format; GLsizei _width; GLsizei _height; GLsizei _depth; + int _num_levels; GLenum _target; SamplerState _active_sampler; diff --git a/panda/src/glstuff/glmisc_src.cxx b/panda/src/glstuff/glmisc_src.cxx index 2d6db9f654..f223ef266e 100644 --- a/panda/src/glstuff/glmisc_src.cxx +++ b/panda/src/glstuff/glmisc_src.cxx @@ -250,16 +250,6 @@ ConfigVariableBool gl_immutable_texture_storage "for each texture. This improves runtime performance, but " "changing the size or type of a texture will be slower.")); -ConfigVariableBool gl_use_bindless_texture - ("gl-use-bindless-texture", false, - PRC_DESC("Set this to let Panda use OpenGL's bindless texture " - "extension for all textures passed to shaders, for improved " - "performance. This is an experimental feature and comes " - "with a few caveats; for one, it requires that all sampler " - "uniforms have a layout(bindless_sampler) qualifier, and " - "it also requires that the texture properties are not " - "modified after the texture handle has been initialized.")); - ConfigVariableBool gl_enable_memory_barriers ("gl-enable-memory-barriers", true, PRC_DESC("If this is set, Panda will make sure that every write " diff --git a/panda/src/glstuff/glmisc_src.h b/panda/src/glstuff/glmisc_src.h index cb387d81e3..6568c1b2ef 100644 --- a/panda/src/glstuff/glmisc_src.h +++ b/panda/src/glstuff/glmisc_src.h @@ -74,7 +74,6 @@ extern ConfigVariableBool gl_cube_map_seamless; extern ConfigVariableBool gl_dump_compiled_shaders; extern ConfigVariableBool gl_validate_shaders; extern ConfigVariableBool gl_immutable_texture_storage; -extern ConfigVariableBool gl_use_bindless_texture; extern ConfigVariableBool gl_enable_memory_barriers; extern ConfigVariableBool gl_vertex_array_objects; extern ConfigVariableBool gl_fixed_vertex_attrib_locations; diff --git a/panda/src/gobj/preparedGraphicsObjects.cxx b/panda/src/gobj/preparedGraphicsObjects.cxx index 93a5f5c655..b76b052476 100644 --- a/panda/src/gobj/preparedGraphicsObjects.cxx +++ b/panda/src/gobj/preparedGraphicsObjects.cxx @@ -242,7 +242,7 @@ void PreparedGraphicsObjects:: release_texture(TextureContext *tc) { ReMutexHolder holder(_lock); - tc->get_texture()->clear_prepared(tc->get_view(), this); + tc->get_texture()->clear_prepared(this); // We have to set the Texture pointer to NULL at this point, since the // Texture itself might destruct at any time after it has been released. @@ -275,7 +275,7 @@ release_all_textures() { int num_textures = (int)_prepared_textures.size() + (int)_enqueued_textures.size(); for (TextureContext *tc : _prepared_textures) { - tc->get_texture()->clear_prepared(tc->get_view(), this); + tc->get_texture()->clear_prepared(this); tc->_object = nullptr; _released_textures.push_back(tc); @@ -332,13 +332,13 @@ get_num_prepared_textures() const { * the TextureContext will be deleted. */ TextureContext *PreparedGraphicsObjects:: -prepare_texture_now(Texture *tex, int view, GraphicsStateGuardianBase *gsg) { +prepare_texture_now(Texture *tex, GraphicsStateGuardianBase *gsg) { ReMutexHolder holder(_lock); // Ask the GSG to create a brand new TextureContext. There might be several // GSG's sharing the same set of textures; if so, it doesn't matter which of // them creates the context (since they're all shared anyway). - TextureContext *tc = gsg->prepare_texture(tex, view); + TextureContext *tc = gsg->prepare_texture(tex); if (tc != nullptr) { bool prepared = _prepared_textures.insert(tc).second; @@ -1513,13 +1513,11 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) { qti != _enqueued_textures.end(); ++qti) { Texture *tex = qti->first; - for (int view = 0; view < tex->get_num_views(); ++view) { - TextureContext *tc = tex->prepare_now(view, this, gsg); - if (tc != nullptr) { - gsg->update_texture(tc, true); - if (view == 0 && qti->second != nullptr) { - qti->second->set_result(tc); - } + TextureContext *tc = tex->prepare_now(this, gsg); + if (tc != nullptr) { + gsg->update_texture(tc, true); + if (qti->second != nullptr) { + qti->second->set_result(tc); } } } diff --git a/panda/src/gobj/preparedGraphicsObjects.h b/panda/src/gobj/preparedGraphicsObjects.h index 6e117733c1..fac1ab630d 100644 --- a/panda/src/gobj/preparedGraphicsObjects.h +++ b/panda/src/gobj/preparedGraphicsObjects.h @@ -82,7 +82,7 @@ PUBLISHED: int get_num_queued_textures() const; int get_num_prepared_textures() const; - TextureContext *prepare_texture_now(Texture *tex, int view, + TextureContext *prepare_texture_now(Texture *tex, GraphicsStateGuardianBase *gsg); void enqueue_sampler(const SamplerState &sampler); diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index 27adaa3e27..59ddcdc61e 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -1513,6 +1513,39 @@ get_image_modified_pages(UpdateSeq since, int n) const { return result; } + if (n > 0 && cdata->_texture_type == Texture::TT_3d_texture) { + // Don't bother handling this special case, just consider all mipmap pages + // modified. + result.set_range(0, do_get_expected_mipmap_num_pages(cdata, n)); + return result; + } + + size_t num_pages = cdata->_z_size * cdata->_num_views; + for (const ModifiedPageRange &range : cdata->_modified_pages) { + if (range._z_begin >= num_pages) { + break; + } + if (since < range._modified) { + result.set_range(range._z_begin, std::min(range._z_end, num_pages) - range._z_begin); + } + } + + return result; +} + +/** + * Like get_image_modified_pages, but returns the result for a particular view. + */ +SparseArray Texture:: +get_view_modified_pages(UpdateSeq since, int view, int n) const { + CDReader cdata(_cycler); + + SparseArray result; + if (since == cdata->_image_modified) { + // Early-out since no range is more recent than _image_modified. + return result; + } + if (n > 0 && cdata->_texture_type == Texture::TT_3d_texture) { // Don't bother handling this special case, just consider all mipmap pages // modified. @@ -1520,12 +1553,17 @@ get_image_modified_pages(UpdateSeq since, int n) const { return result; } + size_t offset = cdata->_z_size * view; for (const ModifiedPageRange &range : cdata->_modified_pages) { - if (range._z_begin >= cdata->_z_size) { + if (range._z_end <= offset) { + continue; + } + if (range._z_begin >= offset + (size_t)cdata->_z_size) { break; } if (since < range._modified) { - result.set_range(range._z_begin, std::min(range._z_end, (size_t)cdata->_z_size) - range._z_begin); + size_t begin = std::max(range._z_begin, offset) - offset; + result.set_range(begin, std::min(range._z_end - offset, (size_t)cdata->_z_size) - begin); } } @@ -1553,9 +1591,9 @@ prepare(PreparedGraphicsObjects *prepared_objects) { bool Texture:: is_prepared(PreparedGraphicsObjects *prepared_objects) const { MutexHolder holder(_lock); - PreparedViews::const_iterator pvi; - pvi = _prepared_views.find(prepared_objects); - if (pvi != _prepared_views.end()) { + Contexts::const_iterator ci; + ci = _contexts.find(prepared_objects); + if (ci != _contexts.end()) { return true; } return prepared_objects->is_texture_queued(this); @@ -1569,24 +1607,11 @@ is_prepared(PreparedGraphicsObjects *prepared_objects) const { bool Texture:: was_image_modified(PreparedGraphicsObjects *prepared_objects) const { MutexHolder holder(_lock); - CDReader cdata(_cycler); - - PreparedViews::const_iterator pvi; - pvi = _prepared_views.find(prepared_objects); - if (pvi != _prepared_views.end()) { - const Contexts &contexts = (*pvi).second; - for (int view = 0; view < cdata->_num_views; ++view) { - Contexts::const_iterator ci; - ci = contexts.find(view); - if (ci == contexts.end()) { - return true; - } - TextureContext *tc = (*ci).second; - if (tc->was_image_modified()) { - return true; - } - } - return false; + Contexts::const_iterator ci; + ci = _contexts.find(prepared_objects); + if (ci != _contexts.end()) { + TextureContext *tc = (*ci).second; + return tc->was_image_modified(); } return true; } @@ -1601,24 +1626,13 @@ was_image_modified(PreparedGraphicsObjects *prepared_objects) const { size_t Texture:: get_data_size_bytes(PreparedGraphicsObjects *prepared_objects) const { MutexHolder holder(_lock); - CDReader cdata(_cycler); - - PreparedViews::const_iterator pvi; - size_t total_size = 0; - pvi = _prepared_views.find(prepared_objects); - if (pvi != _prepared_views.end()) { - const Contexts &contexts = (*pvi).second; - for (int view = 0; view < cdata->_num_views; ++view) { - Contexts::const_iterator ci; - ci = contexts.find(view); - if (ci != contexts.end()) { - TextureContext *tc = (*ci).second; - total_size += tc->get_data_size_bytes(); - } - } + Contexts::const_iterator ci; + ci = _contexts.find(prepared_objects); + if (ci != _contexts.end()) { + TextureContext *tc = (*ci).second; + return tc->get_data_size_bytes(); } - - return total_size; + return 0; } /** @@ -1628,24 +1642,13 @@ get_data_size_bytes(PreparedGraphicsObjects *prepared_objects) const { bool Texture:: get_active(PreparedGraphicsObjects *prepared_objects) const { MutexHolder holder(_lock); - CDReader cdata(_cycler); - - PreparedViews::const_iterator pvi; - pvi = _prepared_views.find(prepared_objects); - if (pvi != _prepared_views.end()) { - const Contexts &contexts = (*pvi).second; - for (int view = 0; view < cdata->_num_views; ++view) { - Contexts::const_iterator ci; - ci = contexts.find(view); - if (ci != contexts.end()) { - TextureContext *tc = (*ci).second; - if (tc->get_active()) { - return true; - } - } - } + Contexts::const_iterator ci; + ci = _contexts.find(prepared_objects); + if (ci != _contexts.end()) { + TextureContext *tc = (*ci).second; + return tc->get_active(); } - return false; + return 0; } /** @@ -1655,24 +1658,13 @@ get_active(PreparedGraphicsObjects *prepared_objects) const { bool Texture:: get_resident(PreparedGraphicsObjects *prepared_objects) const { MutexHolder holder(_lock); - CDReader cdata(_cycler); - - PreparedViews::const_iterator pvi; - pvi = _prepared_views.find(prepared_objects); - if (pvi != _prepared_views.end()) { - const Contexts &contexts = (*pvi).second; - for (int view = 0; view < cdata->_num_views; ++view) { - Contexts::const_iterator ci; - ci = contexts.find(view); - if (ci != contexts.end()) { - TextureContext *tc = (*ci).second; - if (tc->get_resident()) { - return true; - } - } - } + Contexts::const_iterator ci; + ci = _contexts.find(prepared_objects); + if (ci != _contexts.end()) { + TextureContext *tc = (*ci).second; + return tc->get_resident(); } - return false; + return 0; } /** @@ -1681,20 +1673,15 @@ get_resident(PreparedGraphicsObjects *prepared_objects) const { */ bool Texture:: release(PreparedGraphicsObjects *prepared_objects) { - MutexHolder holder(_lock); - PreparedViews::iterator pvi; - pvi = _prepared_views.find(prepared_objects); - if (pvi != _prepared_views.end()) { - Contexts temp; - temp.swap((*pvi).second); - Contexts::iterator ci; - for (ci = temp.begin(); ci != temp.end(); ++ci) { - TextureContext *tc = (*ci).second; - if (tc != nullptr) { - prepared_objects->release_texture(tc); - } + Contexts::iterator ci; + ci = _contexts.find(prepared_objects); + if (ci != _contexts.end()) { + TextureContext *tc = (*ci).second; + if (tc != nullptr) { + prepared_objects->release_texture(tc); + } else { + _contexts.erase(ci); } - _prepared_views.erase(pvi); } // Maybe it wasn't prepared yet, but it's about to be. @@ -1709,25 +1696,19 @@ int Texture:: release_all() { MutexHolder holder(_lock); - // We have to traverse a copy of the _prepared_views list, because the + // We have to traverse a copy of the _contexts list, because the // PreparedGraphicsObjects object will call clear_prepared() in response to // each release_texture(), and we don't want to be modifying the - // _prepared_views list while we're traversing it. - PreparedViews temp; - temp.swap(_prepared_views); - int num_freed = (int)temp.size(); + // _contexts list while we're traversing it. + Contexts temp = _contexts; + int num_freed = (int)_contexts.size(); - PreparedViews::iterator pvi; - for (pvi = temp.begin(); pvi != temp.end(); ++pvi) { - PreparedGraphicsObjects *prepared_objects = (*pvi).first; - Contexts temp; - temp.swap((*pvi).second); - Contexts::iterator ci; - for (ci = temp.begin(); ci != temp.end(); ++ci) { - TextureContext *tc = (*ci).second; - if (tc != nullptr) { - prepared_objects->release_texture(tc); - } + Contexts::const_iterator ci; + for (ci = temp.begin(); ci != temp.end(); ++ci) { + PreparedGraphicsObjects *prepared_objects = (*ci).first; + TextureContext *tc = (*ci).second; + if (tc != nullptr) { + prepared_objects->release_texture(tc); } } @@ -2088,29 +2069,31 @@ set_orig_file_size(int x, int y, int z) { * be rendered. */ TextureContext *Texture:: -prepare_now(int view, - PreparedGraphicsObjects *prepared_objects, +prepare_now(PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg) { MutexHolder holder(_lock); - CDReader cdata(_cycler); - // Don't exceed the actual number of views. - view = max(min(view, cdata->_num_views - 1), 0); - - // Get the list of PreparedGraphicsObjects for this view. - Contexts &contexts = _prepared_views[prepared_objects]; - Contexts::const_iterator pvi; - pvi = contexts.find(view); - if (pvi != contexts.end()) { - return (*pvi).second; + Contexts::const_iterator ci; + ci = _contexts.find(prepared_objects); + if (ci != _contexts.end()) { + return (*ci).second; } - TextureContext *tc = prepared_objects->prepare_texture_now(this, view, gsg); - contexts[view] = tc; + TextureContext *tc = prepared_objects->prepare_texture_now(this, gsg); + _contexts[prepared_objects] = tc; return tc; } +/** + * @deprecated See prepare_now() without a view parameter. + */ +TextureContext *Texture:: +prepare_now(int view, PreparedGraphicsObjects *prepared_objects, + GraphicsStateGuardianBase *gsg) { + return prepare_now(prepared_objects, gsg); +} + /** * Returns the smallest power of 2 greater than or equal to value. */ @@ -9277,20 +9260,11 @@ read_dds_level_bc5(Texture *tex, CData *cdata, const DDSHeader &header, int n, i * never be called by user code. */ void Texture:: -clear_prepared(int view, PreparedGraphicsObjects *prepared_objects) { - PreparedViews::iterator pvi; - pvi = _prepared_views.find(prepared_objects); - if (pvi != _prepared_views.end()) { - Contexts &contexts = (*pvi).second; - Contexts::iterator ci; - ci = contexts.find(view); - if (ci != contexts.end()) { - contexts.erase(ci); - } - - if (contexts.empty()) { - _prepared_views.erase(pvi); - } +clear_prepared(PreparedGraphicsObjects *prepared_objects) { + Contexts::iterator ci; + ci = _contexts.find(prepared_objects); + if (ci != _contexts.end()) { + _contexts.erase(ci); } } diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index d1e3b1ecb4..f6ec5eb1f2 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -528,6 +528,7 @@ PUBLISHED: MAKE_PROPERTY(image_modified, get_image_modified); SparseArray get_image_modified_pages(UpdateSeq since, int n = 0) const; + SparseArray get_view_modified_pages(UpdateSeq since, int view, int n = 0) const; INLINE bool has_auto_texture_scale() const; INLINE AutoTextureScale get_auto_texture_scale() const; @@ -597,6 +598,8 @@ PUBLISHED: MAKE_PROPERTY(post_load_store_cache, get_post_load_store_cache, set_post_load_store_cache); + TextureContext *prepare_now(PreparedGraphicsObjects *prepared_objects, + GraphicsStateGuardianBase *gsg); TextureContext *prepare_now(int view, PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg); @@ -859,7 +862,7 @@ private: const DDSHeader &header, int n, std::istream &in); - void clear_prepared(int view, PreparedGraphicsObjects *prepared_objects); + void clear_prepared(PreparedGraphicsObjects *prepared_objects); static void consider_downgrade(PNMImage &pnmimage, int num_channels, const std::string &name); @@ -1066,9 +1069,8 @@ protected: // conversely keeps a list (a set) of all the Textures that have been // prepared there. When either destructs, it removes itself from the // other's list. - typedef pmap Contexts; - typedef pmap PreparedViews; - PreparedViews _prepared_views; + typedef pmap Contexts; + Contexts _contexts; // It is common, when using normal maps, specular maps, gloss maps, and // such, to use a file naming convention where the filenames of the special diff --git a/panda/src/gobj/textureContext.I b/panda/src/gobj/textureContext.I index c7994a4bbc..f65caa239a 100644 --- a/panda/src/gobj/textureContext.I +++ b/panda/src/gobj/textureContext.I @@ -15,10 +15,9 @@ * */ INLINE TextureContext:: -TextureContext(PreparedGraphicsObjects *pgo, Texture *tex, int view) : +TextureContext(PreparedGraphicsObjects *pgo, Texture *tex) : BufferContext(&pgo->_texture_residency, tex), - AdaptiveLruPage(0), - _view(view) + AdaptiveLruPage(0) { } @@ -31,15 +30,13 @@ get_texture() const { } /** - * Returns the specific view of a multiview texture this context represents. - * In the usual case, with a non-multiview texture, this will be 0. + * @deprecated since 1.11.0: always returns 0. */ INLINE int TextureContext:: get_view() const { - return _view; + return 0; } - /** * Returns true if the texture properties or image have been modified since * the last time mark_loaded() was called. @@ -103,6 +100,15 @@ get_image_modified_pages(int n) const { return get_texture()->get_image_modified_pages(_image_modified, n); } +/** + * Returns a SparseArray indicating which pages of the texture have been + * modified since the last call to mark_loaded(). + */ +INLINE SparseArray TextureContext:: +get_view_modified_pages(int view, int n) const { + return get_texture()->get_view_modified_pages(_image_modified, view, n); +} + /** * Should be called (usually by a derived class) when the on-card size of this * object has changed. diff --git a/panda/src/gobj/textureContext.h b/panda/src/gobj/textureContext.h index de828781e2..765509ec65 100644 --- a/panda/src/gobj/textureContext.h +++ b/panda/src/gobj/textureContext.h @@ -32,7 +32,7 @@ */ class EXPCL_PANDA_GOBJ TextureContext : public BufferContext, public AdaptiveLruPage { public: - INLINE TextureContext(PreparedGraphicsObjects *pgo, Texture *tex, int view); + INLINE TextureContext(PreparedGraphicsObjects *pgo, Texture *tex); PUBLISHED: INLINE Texture *get_texture() const; @@ -49,6 +49,7 @@ PUBLISHED: INLINE UpdateSeq get_image_modified() const; INLINE SparseArray get_image_modified_pages(int n = 0) const; + INLINE SparseArray get_view_modified_pages(int view, int n = 0) const; public: INLINE void update_data_size_bytes(size_t new_data_size_bytes); @@ -60,7 +61,6 @@ public: virtual void write(std::ostream &out, int indent_level) const; private: - int _view; UpdateSeq _properties_modified; UpdateSeq _image_modified; diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.h b/panda/src/gsgbase/graphicsStateGuardianBase.h index e2da6cc2d6..80d31f5c3c 100644 --- a/panda/src/gsgbase/graphicsStateGuardianBase.h +++ b/panda/src/gsgbase/graphicsStateGuardianBase.h @@ -147,7 +147,7 @@ public: virtual PreparedGraphicsObjects *get_prepared_objects()=0; #endif - virtual TextureContext *prepare_texture(Texture *tex, int view)=0; + virtual TextureContext *prepare_texture(Texture *tex)=0; virtual bool update_texture(TextureContext *tc, bool force)=0; virtual void release_texture(TextureContext *tc)=0; virtual void release_textures(const pvector &contexts)=0; diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx index 17b7ac24d4..5e723cece8 100644 --- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx +++ b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx @@ -1354,12 +1354,12 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, tex->setup_2d_texture(w, h, Texture::T_unsigned_byte, Texture::F_rgba); - TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this); + TextureContext *tc = tex->prepare_now(get_prepared_objects(), this); nassertr(tc != nullptr, false); TinyTextureContext *gtc = DCAST(TinyTextureContext, tc); GLTexture *gltex = >c->_gltex; - if (!setup_gltex(gltex, tex->get_x_size(), tex->get_y_size(), 1)) { + if (!setup_gltex(gltex, tex->get_x_size(), tex->get_y_size(), tex->get_num_views(), 1)) { return false; } LColor border_color = tex->get_border_color(); @@ -1369,7 +1369,7 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, gltex->border_color.v[2] = border_color[2]; gltex->border_color.v[3] = border_color[3]; - PIXEL *ip = gltex->levels[0].pixmap + gltex->xsize * gltex->ysize; + PIXEL *ip = gltex->views[view].levels[0].pixmap + gltex->xsize * gltex->ysize; PIXEL *fo = _c->zb->pbuf + xo + yo * _c->zb->linesize / PSZB; for (int y = 0; y < gltex->ysize; ++y) { ip -= gltex->xsize; @@ -1602,7 +1602,7 @@ set_state_and_transform(const RenderState *target, * call Texture::prepare(). */ TextureContext *TinyGraphicsStateGuardian:: -prepare_texture(Texture *tex, int view) { +prepare_texture(Texture *tex) { switch (tex->get_texture_type()) { case Texture::TT_1d_texture: case Texture::TT_2d_texture: @@ -1629,7 +1629,7 @@ prepare_texture(Texture *tex, int view) { } */ - TinyTextureContext *gtc = new TinyTextureContext(_prepared_objects, tex, view); + TinyTextureContext *gtc = new TinyTextureContext(_prepared_objects, tex); return gtc; } @@ -1652,7 +1652,7 @@ update_texture(TextureContext *tc, bool force) { GLTexture *gltex = >c->_gltex; - if (gtc->was_image_modified() || gltex->num_levels == 0) { + if (gtc->was_image_modified() || gltex->num_views == 0 || gltex->num_levels == 0) { // If the texture image was modified, reload the texture. Texture *tex = gtc->get_texture(); bool okflag = upload_texture(gtc, force, tex->uses_mipmaps()); @@ -1678,7 +1678,7 @@ update_texture(TextureContext *tc, bool force) { * (and if get_incomplete_render() is true). */ bool TinyGraphicsStateGuardian:: -update_texture(TextureContext *tc, bool force, int stage_index, bool uses_mipmaps) { +update_texture(TextureContext *tc, int view, bool force, int stage_index, bool uses_mipmaps) { if (!update_texture(tc, force)) { return false; } @@ -1699,8 +1699,15 @@ update_texture(TextureContext *tc, bool force, int stage_index, bool uses_mipmap _c->current_textures[stage_index] = gltex; + if (view < 0) { + view = 0; + } + if (view >= gltex->num_views) { + view = gltex->num_views - 1; + } + ZTextureDef *texture_def = &_c->zb->current_textures[stage_index]; - texture_def->levels = gltex->levels; + texture_def->levels = gltex->views[view].levels; texture_def->s_max = gltex->s_max; texture_def->t_max = gltex->t_max; @@ -2155,7 +2162,7 @@ do_issue_texture() { nassertv(texture != nullptr); int view = get_current_tex_view_offset() + stage->get_tex_view_offset(); - TextureContext *tc = texture->prepare_now(view, _prepared_objects, this); + TextureContext *tc = texture->prepare_now(_prepared_objects, this); if (tc == nullptr) { // Something wrong with this texture; skip it. return; @@ -2165,7 +2172,7 @@ do_issue_texture() { const SamplerState &sampler = _target_texture->get_on_sampler(stage); // Then, turn on the current texture mode. - if (!update_texture(tc, false, si, sampler.uses_mipmaps())) { + if (!update_texture(tc, view, false, si, sampler.uses_mipmaps())) { return; } @@ -2418,7 +2425,7 @@ upload_texture(TinyTextureContext *gtc, bool force, bool uses_mipmaps) { << num_levels << ", uses_mipmaps = " << uses_mipmaps << "\n"; } - if (!setup_gltex(gltex, tex->get_x_size(), tex->get_y_size(), num_levels)) { + if (!setup_gltex(gltex, tex->get_x_size(), tex->get_y_size(), tex->get_num_views(), num_levels)) { return false; } LColor border_color = tex->get_border_color(); @@ -2432,78 +2439,80 @@ upload_texture(TinyTextureContext *gtc, bool force, bool uses_mipmaps) { int xsize = gltex->xsize; int ysize = gltex->ysize; - for (int level = 0; level < gltex->num_levels; ++level) { - ZTextureLevel *dest = &gltex->levels[level]; + for (int view = 0; view < gltex->num_views; ++view) { + for (int level = 0; level < gltex->num_levels; ++level) { + ZTextureLevel *dest = &gltex->views[view].levels[level]; - if (tex->has_ram_mipmap_image(level)) { - switch (tex->get_format()) { - case Texture::F_rgb: - case Texture::F_rgb5: - case Texture::F_rgb8: - case Texture::F_rgb12: - case Texture::F_rgb332: - copy_rgb_image(dest, xsize, ysize, gtc, level); - break; + if (tex->has_ram_mipmap_image(level)) { + switch (tex->get_format()) { + case Texture::F_rgb: + case Texture::F_rgb5: + case Texture::F_rgb8: + case Texture::F_rgb12: + case Texture::F_rgb332: + copy_rgb_image(dest, xsize, ysize, gtc, view, level); + break; - case Texture::F_rgba: - case Texture::F_rgbm: - case Texture::F_rgba4: - case Texture::F_rgba5: - case Texture::F_rgba8: - case Texture::F_rgba12: - case Texture::F_rgba16: - case Texture::F_rgba32: - copy_rgba_image(dest, xsize, ysize, gtc, level); - break; + case Texture::F_rgba: + case Texture::F_rgbm: + case Texture::F_rgba4: + case Texture::F_rgba5: + case Texture::F_rgba8: + case Texture::F_rgba12: + case Texture::F_rgba16: + case Texture::F_rgba32: + copy_rgba_image(dest, xsize, ysize, gtc, view, level); + break; - case Texture::F_luminance: - copy_lum_image(dest, xsize, ysize, gtc, level); - break; + case Texture::F_luminance: + copy_lum_image(dest, xsize, ysize, gtc, view, level); + break; - case Texture::F_red: - copy_one_channel_image(dest, xsize, ysize, gtc, level, 0); - break; + case Texture::F_red: + copy_one_channel_image(dest, xsize, ysize, gtc, view, level, 0); + break; - case Texture::F_green: - copy_one_channel_image(dest, xsize, ysize, gtc, level, 1); - break; + case Texture::F_green: + copy_one_channel_image(dest, xsize, ysize, gtc, view, level, 1); + break; - case Texture::F_blue: - copy_one_channel_image(dest, xsize, ysize, gtc, level, 2); - break; + case Texture::F_blue: + copy_one_channel_image(dest, xsize, ysize, gtc, view, level, 2); + break; - case Texture::F_alpha: - copy_alpha_image(dest, xsize, ysize, gtc, level); - break; + case Texture::F_alpha: + copy_alpha_image(dest, xsize, ysize, gtc, view, level); + break; - case Texture::F_luminance_alphamask: - case Texture::F_luminance_alpha: - copy_la_image(dest, xsize, ysize, gtc, level); - break; + case Texture::F_luminance_alphamask: + case Texture::F_luminance_alpha: + copy_la_image(dest, xsize, ysize, gtc, view, level); + break; - default: - tinydisplay_cat.error() - << "Unsupported texture format " - << tex->get_format() << "!\n"; - return false; - } - } else { - // Fill the mipmap with a solid color. - LColor scaled = tex->get_clear_color().fmin(LColor(1)).fmax(LColor::zero()); - scaled *= 255; - unsigned int clear = RGBA8_TO_PIXEL((int)scaled[0], (int)scaled[1], - (int)scaled[2], (int)scaled[3]); - unsigned int *dpix = (unsigned int *)dest->pixmap; - int pixel_count = xsize * ysize; - while (pixel_count-- > 0) { - *dpix = clear; - ++dpix; + default: + tinydisplay_cat.error() + << "Unsupported texture format " + << tex->get_format() << "!\n"; + return false; + } + } else { + // Fill the mipmap with a solid color. + LColor scaled = tex->get_clear_color().fmin(LColor(1)).fmax(LColor::zero()); + scaled *= 255; + unsigned int clear = RGBA8_TO_PIXEL((int)scaled[0], (int)scaled[1], + (int)scaled[2], (int)scaled[3]); + unsigned int *dpix = (unsigned int *)dest->pixmap; + int pixel_count = xsize * ysize; + while (pixel_count-- > 0) { + *dpix = clear; + ++dpix; + } } + + bytecount += xsize * ysize * 4; + xsize = max(xsize >> 1, 1); + ysize = max(ysize >> 1, 1); } - - bytecount += xsize * ysize * 4; - xsize = max(xsize >> 1, 1); - ysize = max(ysize >> 1, 1); } gtc->update_data_size_bytes(bytecount); @@ -2545,7 +2554,7 @@ upload_simple_texture(TinyTextureContext *gtc) { << "loading simple image for " << tex->get_name() << "\n"; } - if (!setup_gltex(gltex, width, height, 1)) { + if (!setup_gltex(gltex, width, height, 1, 1)) { return false; } LColor border_color = tex->get_border_color(); @@ -2555,7 +2564,7 @@ upload_simple_texture(TinyTextureContext *gtc) { gltex->border_color.v[2] = border_color[2]; gltex->border_color.v[3] = border_color[3]; - ZTextureLevel *dest = &gltex->levels[0]; + ZTextureLevel *dest = &gltex->views[0].levels[0]; memcpy(dest->pixmap, image_ptr, image_size); gtc->mark_loaded(); @@ -2569,7 +2578,7 @@ upload_simple_texture(TinyTextureContext *gtc) { * texture is a valid size, false otherwise. */ bool TinyGraphicsStateGuardian:: -setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) { +setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_views, int num_levels) { if (x_size == 0 || y_size == 0) { // A texture without pixels gets turned into a 1x1 texture. x_size = 1; @@ -2588,6 +2597,7 @@ setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) { return false; } + num_views = max(num_views, 1); num_levels = min(num_levels, MAX_MIPMAP_LEVELS); gltex->xsize = x_size; @@ -2601,7 +2611,7 @@ setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) { // We allocate one big buffer, large enough to include all the mipmap // levels, and index into that buffer for each level. This cuts down on the // number of individual alloc calls we have to make for each texture. - int total_bytecount = 0; + int view_size = 0; // Count up the total bytes required for all mipmap levels. { @@ -2609,12 +2619,14 @@ setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) { int y = y_size; for (int level = 0; level < num_levels; ++level) { int bytecount = x * y * 4; - total_bytecount += bytecount; + view_size += bytecount; x = max((x >> 1), 1); y = max((y >> 1), 1); } } + int total_bytecount = view_size * num_views; + if (gltex->total_bytecount != total_bytecount) { if (gltex->allocated_buffer != nullptr) { TinyTextureContext::get_class_type().deallocate_array(gltex->allocated_buffer); @@ -2623,37 +2635,48 @@ setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) { gltex->total_bytecount = total_bytecount; } + if (num_views != gltex->num_views) { + if (gltex->views != nullptr) { + TinyTextureContext::get_class_type().deallocate_array(gltex->views); + } + + gltex->views = (ZTextureView *)TinyTextureContext::get_class_type().allocate_array(sizeof(ZTextureView) * num_views); + gltex->num_views = num_views; + } + char *next_buffer = (char *)gltex->allocated_buffer; char *end_of_buffer = next_buffer + total_bytecount; - int level = 0; - ZTextureLevel *dest = nullptr; - while (level < num_levels) { - dest = &gltex->levels[level]; - int bytecount = x_size * y_size * 4; - dest->pixmap = (PIXEL *)next_buffer; - next_buffer += bytecount; - nassertr(next_buffer <= end_of_buffer, false); + for (int view = 0; view < num_views; ++view) { + int level = 0; + ZTextureLevel *dest = nullptr; + while (level < num_levels) { + dest = &gltex->views[view].levels[level]; + int bytecount = x_size * y_size * 4; + dest->pixmap = (PIXEL *)next_buffer; + next_buffer += bytecount; + nassertr(next_buffer <= end_of_buffer, false); - dest->s_mask = ((1 << (s_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level; - dest->t_mask = ((1 << (t_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level; - dest->s_shift = (ZB_POINT_ST_FRAC_BITS + level); - dest->t_shift = (ZB_POINT_ST_FRAC_BITS - s_bits + level); + dest->s_mask = ((1 << (s_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level; + dest->t_mask = ((1 << (t_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level; + dest->s_shift = (ZB_POINT_ST_FRAC_BITS + level); + dest->t_shift = (ZB_POINT_ST_FRAC_BITS - s_bits + level); - x_size = max((x_size >> 1), 1); - y_size = max((y_size >> 1), 1); - s_bits = max(s_bits - 1, 0); - t_bits = max(t_bits - 1, 0); + x_size = max((x_size >> 1), 1); + y_size = max((y_size >> 1), 1); + s_bits = max(s_bits - 1, 0); + t_bits = max(t_bits - 1, 0); - ++level; - } + ++level; + } - // Fill out the remaining mipmap arrays with copies of the last level, so we - // don't have to be concerned with running off the end of this array while - // scanning out triangles. - while (level < MAX_MIPMAP_LEVELS) { - gltex->levels[level] = *dest; - ++level; + // Fill out the remaining mipmap arrays with copies of the last level, so we + // don't have to be concerned with running off the end of this array while + // scanning out triangles. + while (level < MAX_MIPMAP_LEVELS) { + gltex->views[view].levels[level] = *dest; + ++level; + } } return true; @@ -2682,7 +2705,7 @@ get_tex_shift(int orig_size) { * indicated ZTexture pixmap. */ void TinyGraphicsStateGuardian:: -copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) { +copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level) { Texture *tex = gtc->get_texture(); nassertv(tex->get_num_components() == 1); nassertv(tex->get_expected_mipmap_x_size(level) == xsize && @@ -2692,7 +2715,7 @@ copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gt nassertv(!src_image.is_null()); const unsigned char *src = src_image.p(); size_t view_size = tex->get_ram_mipmap_view_size(level); - src += view_size * gtc->get_view(); + src += view_size * view; // Component width, and offset to the high-order byte. int cw = tex->get_component_width(); @@ -2720,7 +2743,7 @@ copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gt * indicated ZTexture pixmap. */ void TinyGraphicsStateGuardian:: -copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) { +copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level) { Texture *tex = gtc->get_texture(); nassertv(tex->get_num_components() == 1); @@ -2728,7 +2751,7 @@ copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext * nassertv(!src_image.is_null()); const unsigned char *src = src_image.p(); size_t view_size = tex->get_ram_mipmap_view_size(level); - src += view_size * gtc->get_view(); + src += view_size * view; // Component width, and offset to the high-order byte. int cw = tex->get_component_width(); @@ -2756,7 +2779,7 @@ copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext * * green, or blue) from the texture into the indicated ZTexture pixmap. */ void TinyGraphicsStateGuardian:: -copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level, int channel) { +copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level, int channel) { Texture *tex = gtc->get_texture(); nassertv(tex->get_num_components() == 1); @@ -2764,7 +2787,7 @@ copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureCon nassertv(!src_image.is_null()); const unsigned char *src = src_image.p(); size_t view_size = tex->get_ram_mipmap_view_size(level); - src += view_size * gtc->get_view(); + src += view_size * view; // Component width, and offset to the high-order byte. int cw = tex->get_component_width(); @@ -2821,7 +2844,7 @@ copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureCon * into the indicated ZTexture pixmap. */ void TinyGraphicsStateGuardian:: -copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) { +copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level) { Texture *tex = gtc->get_texture(); nassertv(tex->get_num_components() == 2); @@ -2829,7 +2852,7 @@ copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc nassertv(!src_image.is_null()); const unsigned char *src = src_image.p(); size_t view_size = tex->get_ram_mipmap_view_size(level); - src += view_size * gtc->get_view(); + src += view_size * view; // Component width, and offset to the high-order byte. int cw = tex->get_component_width(); @@ -2858,7 +2881,7 @@ copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc * indicated ZTexture pixmap. */ void TinyGraphicsStateGuardian:: -copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) { +copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level) { Texture *tex = gtc->get_texture(); nassertv(tex->get_num_components() == 3); @@ -2866,7 +2889,7 @@ copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gt nassertv(!src_image.is_null()); const unsigned char *src = src_image.p(); size_t view_size = tex->get_ram_mipmap_view_size(level); - src += view_size * gtc->get_view(); + src += view_size * view; // Component width, and offset to the high-order byte. int cw = tex->get_component_width(); @@ -2895,7 +2918,7 @@ copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gt * indicated ZTexture pixmap. */ void TinyGraphicsStateGuardian:: -copy_rgba_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) { +copy_rgba_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level) { Texture *tex = gtc->get_texture(); nassertv(tex->get_num_components() == 4); @@ -2903,7 +2926,7 @@ copy_rgba_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *g nassertv(!src_image.is_null()); const unsigned char *src = src_image.p(); size_t view_size = tex->get_ram_mipmap_view_size(level); - src += view_size * gtc->get_view(); + src += view_size * view; // Component width, and offset to the high-order byte. int cw = tex->get_component_width(); diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.h b/panda/src/tinydisplay/tinyGraphicsStateGuardian.h index 5f569fc1c5..3a7d3374c0 100644 --- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.h +++ b/panda/src/tinydisplay/tinyGraphicsStateGuardian.h @@ -84,9 +84,9 @@ public: virtual void set_state_and_transform(const RenderState *state, const TransformState *transform); - virtual TextureContext *prepare_texture(Texture *tex, int view); + virtual TextureContext *prepare_texture(Texture *tex); virtual bool update_texture(TextureContext *tc, bool force); - bool update_texture(TextureContext *tc, bool force, int stage_index, bool uses_mipmaps); + bool update_texture(TextureContext *tc, int view, bool force, int stage_index, bool uses_mipmaps); virtual void release_texture(TextureContext *tc); virtual void do_issue_light(); @@ -112,15 +112,15 @@ private: bool apply_texture(TextureContext *tc); bool upload_texture(TinyTextureContext *gtc, bool force, bool uses_mipmaps); bool upload_simple_texture(TinyTextureContext *gtc); - bool setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels); + bool setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_views, int num_levels); int get_tex_shift(int orig_size); - static void copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level); - static void copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level); - static void copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level, int channel); - static void copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level); - static void copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level); - static void copy_rgba_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level); + static void copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level); + static void copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level); + static void copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level, int channel); + static void copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level); + static void copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level); + static void copy_rgba_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level); void setup_material(GLMaterial *gl_material, const Material *material); void do_auto_rescale_normal(); diff --git a/panda/src/tinydisplay/tinyTextureContext.I b/panda/src/tinydisplay/tinyTextureContext.I index 348c748228..f14097ebf9 100644 --- a/panda/src/tinydisplay/tinyTextureContext.I +++ b/panda/src/tinydisplay/tinyTextureContext.I @@ -15,9 +15,11 @@ * */ INLINE TinyTextureContext:: -TinyTextureContext(PreparedGraphicsObjects *pgo, Texture *tex, int view) : - TextureContext(pgo, tex, view) +TinyTextureContext(PreparedGraphicsObjects *pgo, Texture *tex) : + TextureContext(pgo, tex) { + _gltex.views = nullptr; + _gltex.num_views = 0; _gltex.num_levels = 0; _gltex.allocated_buffer = nullptr; _gltex.total_bytecount = 0; diff --git a/panda/src/tinydisplay/tinyTextureContext.cxx b/panda/src/tinydisplay/tinyTextureContext.cxx index d0207a1379..66fd8e4b3e 100644 --- a/panda/src/tinydisplay/tinyTextureContext.cxx +++ b/panda/src/tinydisplay/tinyTextureContext.cxx @@ -31,6 +31,14 @@ TinyTextureContext:: } else { nassertv(gltex->num_levels == 0); } + if (gltex->views != nullptr) { + nassertv(gltex->num_views != 0); + get_class_type().deallocate_array(gltex->views); + gltex->views = nullptr; + gltex->num_views = 0; + } else { + nassertv(gltex->num_views == 0); + } } /** diff --git a/panda/src/tinydisplay/tinyTextureContext.h b/panda/src/tinydisplay/tinyTextureContext.h index d321ae4ab8..6cb760c653 100644 --- a/panda/src/tinydisplay/tinyTextureContext.h +++ b/panda/src/tinydisplay/tinyTextureContext.h @@ -24,7 +24,7 @@ */ class EXPCL_TINYDISPLAY TinyTextureContext : public TextureContext { public: - INLINE TinyTextureContext(PreparedGraphicsObjects *pgo, Texture *tex, int view); + INLINE TinyTextureContext(PreparedGraphicsObjects *pgo, Texture *tex); ALLOC_DELETED_CHAIN(TinyTextureContext); ~TinyTextureContext(); diff --git a/panda/src/tinydisplay/zbuffer.h b/panda/src/tinydisplay/zbuffer.h index ced08b5e31..622de5372e 100644 --- a/panda/src/tinydisplay/zbuffer.h +++ b/panda/src/tinydisplay/zbuffer.h @@ -252,7 +252,7 @@ int texcoord_mirror_once(int coord, int max_coord); /* linesize is in BYTES */ void ZB_copyFrameBuffer(const ZBuffer *zb,void *buf,int linesize); void ZB_copyFrameBufferNoAlpha(const ZBuffer *zb,void *buf,int linesize); -void ZB_zoomFrameBuffer(ZBuffer *dest, int dest_xmin, int dest_ymin, +void ZB_zoomFrameBuffer(ZBuffer *dest, int dest_xmin, int dest_ymin, int dest_xsize, int dest_ysize, const ZBuffer *source, int source_xmin, int source_ymin, int source_xsize, int source_ysize); diff --git a/panda/src/tinydisplay/zgl.h b/panda/src/tinydisplay/zgl.h index c18cafa8b7..749c200b03 100644 --- a/panda/src/tinydisplay/zgl.h +++ b/panda/src/tinydisplay/zgl.h @@ -119,9 +119,14 @@ typedef struct GLVertex { /* textures */ +typedef struct ZTextureView { + ZTextureLevel levels[MAX_MIPMAP_LEVELS]; +} ZTextureView; + /* The combination of all mipmap levels: one complete texture. */ typedef struct GLTexture { - ZTextureLevel levels[MAX_MIPMAP_LEVELS]; + ZTextureView *views; + int num_views; int num_levels; int xsize, ysize; int s_max, t_max; diff --git a/panda/src/wgldisplay/wglGraphicsBuffer.cxx b/panda/src/wgldisplay/wglGraphicsBuffer.cxx index 207fa7eeaf..61edb9643c 100644 --- a/panda/src/wgldisplay/wglGraphicsBuffer.cxx +++ b/panda/src/wgldisplay/wglGraphicsBuffer.cxx @@ -170,7 +170,7 @@ bind_texture_to_pbuffer() { tex->set_format(Texture::F_rgb); } } - TextureContext *tc = tex->prepare_now(0, _gsg->get_prepared_objects(), _gsg); + TextureContext *tc = tex->prepare_now(_gsg->get_prepared_objects(), _gsg); nassertv(tc != nullptr); CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc); GLenum target = wglgsg->get_texture_target(tex->get_texture_type()); @@ -180,7 +180,7 @@ bind_texture_to_pbuffer() { cdataw->_textures[tex_index]._rtm_mode = RTM_copy_texture; return; } - GLP(BindTexture)(target, gtc->_index); + GLP(BindTexture)(target, gtc->get_view_index(0)); if (_fb_properties.is_single_buffered()) { wglgsg->_wglBindTexImageARB(_pbuffer, WGL_FRONT_LEFT_ARB); } else {