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 {