OpenCPN Partial API docs
Loading...
Searching...
No Matches
gl_tex_cache.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2014 by David S. Register *
3 * Copyright (C) 2014 Sean D'Epagnier *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
17 ***************************************************************************/
18
25#include <stdint.h>
26
27#include <wx/wxprec.h>
28#include <wx/tokenzr.h>
29#include <wx/filename.h>
30#include <wx/wx.h>
31
32#include "config.h"
33
34#include "mipmap/mipmap.h"
35#include "model/base_platform.h"
36#include "model/config_vars.h"
37#include "model/gui_vars.h"
38
39#include "chartbase.h"
40#include "chartdb.h"
41#include "chartimg.h"
42#include "chcanv.h"
43#include "dychart.h"
44#include "gl_chart_canvas.h"
45#include "gl_tex_cache.h"
46#include "gl_texture_descr.h"
47#include "lz4.h"
48#include "lz4hc.h"
49#include "ocpn_platform.h"
50#include "quilt.h"
51#include "squish.h"
52#include "viewport.h"
53
54#ifndef GL_ETC1_RGB8_OES
55#define GL_ETC1_RGB8_OES 0x8D64
56#endif
57
58// Correct some deficincies in MacOS OpenGL include files
59#ifdef __WXOSX__
60typedef void (*PFNGLGENBUFFERSPROC)(GLsizei n, GLuint *buffers);
61typedef void (*PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer);
62typedef void (*PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint *buffers);
63typedef void (*PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname,
64 GLint *params);
65typedef void (*PFNGLDELETERENDERBUFFERSEXTPROC)(GLsizei n,
66 const GLuint *renderbuffers);
67typedef void (*PFNGLDELETEFRAMEBUFFERSEXTPROC)(GLsizei n,
68 const GLuint *framebuffers);
69typedef void (*PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level,
70 GLint xoffset, GLsizei width,
71 GLenum format,
72 GLsizei imageSize,
73 const GLvoid *data);
74typedef void (*PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level,
75 GLvoid *img);
76typedef GLenum (*PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)(GLenum target);
77typedef void (*PFNGLBINDRENDERBUFFEREXTPROC)(GLenum target,
78 GLuint renderbuffer);
79typedef void (*PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size,
80 const GLvoid *data, GLenum usage);
81typedef void (*PFNGLGENFRAMEBUFFERSEXTPROC)(GLsizei n, GLuint *framebuffers);
82typedef void (*PFNGLGENRENDERBUFFERSEXTPROC)(GLsizei n, GLuint *renderbuffers);
83typedef void (*PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)(GLenum target,
84 GLenum attachment,
85 GLenum textarget,
86 GLuint texture, GLint level);
87typedef void (*PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level,
88 GLenum internalformat,
89 GLsizei width, GLsizei height,
90 GLint border, GLsizei imageSize,
91 const GLvoid *data);
92typedef void (*PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)(GLenum target,
93 GLenum attachment,
94 GLenum renderbuffertarget,
95 GLuint renderbuffer);
96typedef void (*PFNGLRENDERBUFFERSTORAGEEXTPROC)(GLenum target,
97 GLenum internalformat,
98 GLsizei width, GLsizei height);
99typedef void (*PFNGLBINDFRAMEBUFFEREXTPROC)(GLenum target, GLuint framebuffer);
100#endif
101
102extern ColorScheme global_color_scheme;
103
104extern ocpnGLOptions g_GLOptions;
105
107
108extern wxString CompressedCachePath(wxString path);
109
110// CatalogEntry implementation
111CatalogEntry::CatalogEntry() {}
112
113CatalogEntry::~CatalogEntry() {}
114
115CatalogEntry::CatalogEntry(int level, int x0, int y0, ColorScheme colorscheme) {
116 k.mip_level = level;
117 k.x = x0;
118 k.y = y0;
119 k.tcolorscheme = colorscheme;
120}
121
122int CatalogEntry::GetSerialSize() { return CATALOG_ENTRY_SERIAL_SIZE; }
123
124void CatalogEntry::Serialize(unsigned char *t) {
125 uint32_t *p = (uint32_t *)t;
126
127 *p++ = k.mip_level;
128 *p++ = k.x;
129 *p++ = k.y;
130 *p++ = k.tcolorscheme;
131 *p++ = v.texture_offset;
132 *p++ = v.compressed_size;
133}
134
135void CatalogEntry::DeSerialize(unsigned char *t) {
136 uint32_t *p = (uint32_t *)t;
137
138 k.mip_level = *p++;
139 k.x = *p++;
140 k.y = *p++;
141 k.tcolorscheme = (ColorScheme)*p++;
142 v.texture_offset = *p++;
143 v.compressed_size = *p++;
144}
145
146// glTexFactory Implementation
147enum TextureDataType { COMPRESSED_BUFFER_OK, MAP_BUFFER_OK };
148
149glTexFactory::glTexFactory(ChartBase *chart, int raster_format) {
150 // m_pchart = chart;
151 n_catalog_entries = 0;
152 m_catalog_offset = sizeof(CompressedCacheHeader);
153 wxDateTime ed = chart->GetEditionDate();
154 m_chart_date_binary = (uint32_t)ed.IsValid() ? ed.GetTicks() : 0;
155 m_chartfile_date_binary = ::wxFileModificationTime(chart->GetFullPath());
156 m_chartfile_size =
157 (uint32_t)wxFileName::GetSize(chart->GetFullPath()).GetLo();
158 m_ChartPath = chart->GetFullPath();
159
160 m_CompressedCacheFilePath = CompressedCachePath(chart->GetFullPath());
161 m_hdrOK = false;
162 m_catalogOK = false;
163 m_newCatalog = true;
164
165 m_catalogCorrupted = false;
166
167 m_fs = 0;
168 m_LRUtime = 0;
169 m_ntex = 0;
170 m_tiles = NULL;
171 for (int i = 0; i < N_COLOR_SCHEMES; i++) {
172 for (int j = 0; j < MAX_TEX_LEVEL; j++) {
173 m_cache[i][j] = NULL;
174 }
175 }
176 // Initialize the TextureDescriptor array
177 ChartBaseBSB *pBSBChart = dynamic_cast<ChartBaseBSB *>(chart);
178
179 if (!pBSBChart) return;
180
181 m_size_X = pBSBChart->GetSize_X();
182 m_size_Y = pBSBChart->GetSize_Y();
183
184 // Calculate the number of textures needed
185 m_tex_dim = g_GLOptions.m_iTextureDimension;
186 m_nx_tex = (m_size_X / m_tex_dim) + ((m_size_X % m_tex_dim) == 0 ? 0 : 1);
187 m_ny_tex = (m_size_Y / m_tex_dim) + ((m_size_Y % m_tex_dim) == 0 ? 0 : 1);
188
189 m_stride = m_nx_tex;
190 m_ntex = m_nx_tex * m_ny_tex;
191 m_td_array =
192 (glTextureDescriptor **)calloc(m_ntex, sizeof(glTextureDescriptor *));
193
194 m_prepared_projection_type = 0;
195}
196
197glTexFactory::~glTexFactory() {
198 delete m_fs;
199
200 PurgeBackgroundCompressionPool();
201 DeleteAllTextures();
202 DeleteAllDescriptors();
203
204 for (int i = 0; i < N_COLOR_SCHEMES; i++) {
205 for (int j = 0; j < MAX_TEX_LEVEL; j++) {
206 CatalogEntryValue *v = m_cache[i][j];
207 if (v) {
208 free(v);
209 }
210 }
211 }
212
213 free(m_td_array); // array is empty
214
215 if (m_tiles)
216 for (int i = 0; i < m_ntex; i++) delete m_tiles[i];
217 delete[] m_tiles;
218}
219
220glTextureDescriptor *glTexFactory::GetpTD(wxRect &rect) {
221 int array_index = ArrayIndex(rect.x, rect.y);
222 return m_td_array[array_index];
223}
224
225bool glTexFactory::OnTimer() {
226 for (int i = 0; i < m_ntex; i++) {
227 glTextureDescriptor *ptd = m_td_array[i];
228 // sometimes compressed data is produced but by the time
229 // it arrives it is no longer needed, so with a timeout
230 // of 5 seconds free this memory to avoid ram use buildup
231 if (ptd && ptd->compdata_ticks) {
232 ptd->compdata_ticks--;
233 ptd->FreeComp();
234 }
235 }
236
237#if 0 // this is proven unreliable and slow
238 // if we have the data in the catalog of level 0 or doubly compressed
239 // for an entire row of tiles, then we can free rows from the linebuffer
240 if(g_GLOptions.m_bTextureCompression) {
241 ChartBase *pChart = ChartData->OpenChartFromDB( m_ChartPath, FULL_INIT );
242 ChartBaseBSB *pBSBChart = dynamic_cast<ChartBaseBSB*>( pChart );
243
244 if(pBSBChart) {
245 for(int y = 0; y<m_ny_tex; y++) {
246 int dim = g_GLOptions.m_iTextureDimension;
247
248 if(!pBSBChart->HaveLineCacheRow(y*dim))
249 continue;
250
251 for(int x = 0; x<m_nx_tex; x++) {
252 int i = ArrayIndex(x, y);
253 glTextureDescriptor *ptd = m_td_array[i];
254
255 if( !ptd )
256 goto keeplines;
257
258 if( ptd->compcomp_array[0] )
259 continue; // ok
260
261 CatalogEntryValue *p = GetCacheEntryValue(0, x*dim, y*dim, ptd->m_colorscheme);
262 if(!p)
263 goto keeplines;
264 }
265
266 pBSBChart->FreeLineCacheRows(y*dim, (y+1)*dim);
267 }
268 keeplines:;
269 }
270 }
271#endif
272
273 // write doubly compressed data to disk
274 if (g_GLOptions.m_bTextureCompressionCaching)
275 for (int i = 0; i < m_ntex; i++) {
276 glTextureDescriptor *ptd = m_td_array[i];
277 if (ptd && ptd->IsCompCompArrayComplete(0)) {
278 int dim = g_GLOptions.m_iTextureDimension;
279 UpdateCacheAllLevels(wxRect(ptd->x, ptd->y, dim, dim),
280 ptd->m_colorscheme, ptd->compcomp_array,
281 ptd->compcomp_size);
282
283 // no longer need to store the compressed compressed data
284 ptd->FreeCompComp();
285 // return true;
286 }
287 }
288
289 return false;
290}
291
292#if 0
293#ifdef __ANDROID__
294 // delete any uncompressed textures if texture memory is more than 30
295 // on android?? Maybe this should be removed now
296 bool bGLMemCrunch = g_tex_mem_used > 30/*g_GLOptions.m_iTextureMemorySize*/ * 1024 * 1024;
297
298 if( bGLMemCrunch ){
299 for(wxTextureListNode *node = m_texture_list.GetFirst(); node;
300 node = node->GetNext()) {
301 glTextureDescriptor *ptd = node->GetData();
302 if(ptd->nGPU_compressed == GPU_TEXTURE_UNCOMPRESSED){
303 DeleteSingleTexture(ptd);
304 }
305 }
306 }
307#endif
308#endif
309
310void glTexFactory::AccumulateMemStatistics(int &map_size, int &comp_size,
311 int &compcomp_size) {
312 for (int i = 0; i < m_ntex; i++) {
313 glTextureDescriptor *ptd = m_td_array[i];
314 if (ptd) {
315 map_size += ptd->GetMapArrayAlloc();
316 comp_size += ptd->GetCompArrayAlloc();
317 compcomp_size += ptd->GetCompCompArrayAlloc();
318 }
319 }
320}
321
322void glTexFactory::DeleteTexture(const wxRect &rect) {
323 // Is this texture tile defined?
324 int array_index = ArrayIndex(rect.x, rect.y);
325 glTextureDescriptor *ptd = m_td_array[array_index];
326
327 if (ptd && ptd->tex_name > 0) {
328 DeleteSingleTexture(ptd);
329 }
330}
331
332void glTexFactory::DeleteAllTextures() {
333 // iterate over all the textures presently loaded
334 // and delete the OpenGL texture from the GPU
335 // but keep the private texture descriptor for now
336
337 for (int i = 0; i < m_ntex; i++) {
338 glTextureDescriptor *ptd = m_td_array[i];
339
340 if (ptd) {
341 // if(ptd->tex_name && bthread_debug)
342 // printf("DAT::Delete Texture %d resulting
343 // g_tex_mem_used, mb: %ld\n", ptd->tex_name,
344 // g_tex_mem_used/(1024 * 1024));
345
346 DeleteSingleTexture(ptd);
347 }
348 }
349}
350
351void glTexFactory::DeleteSomeTextures(long target) {
352 // iterate over all the textures presently loaded
353 // and delete the OpenGL texture from the GPU
354 // until the target g_tex_mem_used is reached
355 // but keep the private texture descriptor for now
356
357 for (int i = 0; i < m_ntex; i++) {
358 glTextureDescriptor *ptd = m_td_array[i];
359
360 if (ptd) {
361 // if(ptd->tex_name && bthread_debug)
362 // printf("DSoT::Delete Some Texture %d resulting
363 // g_tex_mem_used, mb: %ld\n", ptd->tex_name,
364 // g_tex_mem_used/(1024 * 1024));
365
366 if (ptd->tex_name) DeleteSingleTexture(ptd);
367
368 if (g_tex_mem_used <= target) break;
369 }
370 }
371}
372
373void glTexFactory::FreeSome(long target) {
374 for (int i = 0; i < m_ntex; i++) {
375 glTextureDescriptor *ptd = m_td_array[i];
376
377 if (ptd) ptd->FreeMap();
378 }
379}
380
381void glTexFactory::DeleteAllDescriptors() {
382 // iterate over all the texture descriptors
383
384 for (int i = 0; i < m_ntex; i++) {
385 glTextureDescriptor *ptd = m_td_array[i];
386 delete ptd;
387 m_td_array[i] = 0;
388 }
389}
390
391bool glTexFactory::BackgroundCompressionAsJob() const {
392 return g_glTextureManager->AsJob(m_ChartPath);
393}
394
395void glTexFactory::PurgeBackgroundCompressionPool() {
396 // Purge the "todo" list, and allow any running jobs to complete normally
397 g_glTextureManager->PurgeJobList(m_ChartPath);
398}
399
400void glTexFactory::DeleteSingleTexture(glTextureDescriptor *ptd) {
401 if (!ptd->tex_name) return;
402
403 g_tex_mem_used -= ptd->tex_mem_used;
404 ptd->level_min = g_mipmap_max_level + 1; // default, nothing loaded
405
406 glDeleteTextures(1, &ptd->tex_name);
407 ptd->tex_name = 0;
408 ptd->tex_mem_used = 0;
409 ptd->nGPU_compressed = GPU_TEXTURE_UNKNOWN;
410}
411
412void glTexFactory::ArrayXY(wxRect *r, int index) const {
413 r->y = (index / m_stride) * m_tex_dim;
414 r->x = (index - ((r->y / m_tex_dim) * m_stride)) * m_tex_dim;
415}
416
417CatalogEntryValue *glTexFactory::GetCacheEntryValue(int level, int x, int y,
418 ColorScheme color_scheme) {
419 if (level < 0 || level >= MAX_TEX_LEVEL) return 0;
420
421 // Look in the cache
422 LoadCatalog();
423
424 CatalogEntryValue *v = m_cache[color_scheme][level];
425 if (v == 0) return 0;
426
427 int array_index = ArrayIndex(x, y);
428 if (array_index >= m_ntex) return 0;
429
430 CatalogEntryValue *r = &v[array_index];
431 if (r->compressed_size == 0) return 0;
432
433 return r;
434}
435
436bool glTexFactory::IsLevelInCache(int level, const wxRect &rect,
437 ColorScheme color_scheme) {
438 bool b_ret = false;
439
440 if (g_GLOptions.m_bTextureCompression &&
441 g_GLOptions.m_bTextureCompressionCaching) {
442 // Search for the requested texture
443 if (GetCacheEntryValue(level, rect.x, rect.y, color_scheme) != 0)
444 b_ret = true;
445 }
446
447 return b_ret;
448}
449
450glTextureDescriptor *glTexFactory::GetOrCreateTD(const wxRect &rect) {
451 int array_index = ArrayIndex(rect.x, rect.y);
452 if (!m_td_array[array_index]) {
454
455 p->x = rect.x;
456 p->y = rect.y;
457 p->level_min = g_mipmap_max_level + 1; // default, nothing loaded
458 p->m_colorscheme = global_color_scheme;
459 m_td_array[array_index] = p;
460 }
461 return m_td_array[array_index];
462}
463
464static void CreateTexture(GLuint &tex_name, bool b_use_mipmaps) {
465 glGenTextures(1, &tex_name);
466
467 // printf("gentex %d rect: %d %d index %d\n", ptd->tex_name,
468 // rect.x, rect.y, array_index);
469 glBindTexture(GL_TEXTURE_2D, tex_name);
470
471 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
472 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
473 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
474
475 if (b_use_mipmaps)
476 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
477 GL_LINEAR_MIPMAP_LINEAR);
478 else
479 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
480
481#ifdef __ANDROID__
482 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
483 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
484#endif
485}
486
487bool glTexFactory::BuildTexture(glTextureDescriptor *ptd, int base_level,
488 const wxRect &rect) {
489 bool busy_shown = false;
490
491 // the quality is only slightly worse because linear_mipmap_linear
492 // is impossible, but still replace the texture data with the
493 // correct level data and using only texture level 0
494
495#ifdef ocpnUSE_GLES
496 bool b_use_compressed_mipmaps = false;
497 bool b_use_uncompressed_mipmaps = false;
498#else
499 bool b_use_compressed_mipmaps = true; // best possible quality
500 // don't use uncompressed mipmaps as they are temporary
501 // we upload only the correct level so the image quality is good anyways
502 bool b_use_uncompressed_mipmaps = !g_GLOptions.m_bTextureCompression;
503#endif
504
505 // on systems with little memory we can go up a mipmap level
506 // here so that the uncompressed size without mipmaps (level+1)
507 // is nearly the compressed size with all the compressed mipmaps
508 // this way we won't require 5x more video memory than normal while we
509 // are generating the compressed textures, when the cache is complete the
510 // image becomes clearer as it is replaces with the higher resolution
511 // compressed version
512 bool b_lowmem =
513 false; // maybe instead decide based on how much texture memory we have
514#ifdef ocpnUSE_GLES
515 b_lowmem = g_GLOptions.m_bTextureCompression;
516#endif
517 if (g_GLOptions.m_bTextureCompression &&
518 ptd->nGPU_compressed == GPU_TEXTURE_UNCOMPRESSED) {
519 // if compressed data became available we blow away the texture
520 if (ptd->comp_array[base_level]) DeleteSingleTexture(ptd);
521 }
522
523 // we are done if the data is already in the texture
524 if (base_level == ptd->level_min) return false;
525
526 if (base_level > ptd->level_min) {
527 // if we already have the mipmaps then we can return,
528 // but if we need memory we should free the texture and
529 // re-upload just the higher levels needed
530 bool b_use_mipmaps = ptd->nGPU_compressed == GPU_TEXTURE_COMPRESSED
531 ? b_use_compressed_mipmaps
532 : b_use_uncompressed_mipmaps;
533 if (b_use_mipmaps) {
534 double factor = 0.5; // we should free uncompressed textures earliest
535 bool bGLMemCrunch =
536 g_tex_mem_used >
537 (double)(g_GLOptions.m_iTextureMemorySize * 1024 * 1024) * factor;
538 if (!bGLMemCrunch) return false; // we already have the data in vram
539 }
540 }
541
542 int status = GetTextureLevel(ptd, rect, base_level, ptd->m_colorscheme);
543
544 bool b_use_mipmaps = COMPRESSED_BUFFER_OK == status
545 ? b_use_compressed_mipmaps
546 : b_use_uncompressed_mipmaps;
547
548 DeleteSingleTexture(ptd);
549 CreateTexture(ptd->tex_name, b_use_mipmaps);
550 ptd->nGPU_compressed = COMPRESSED_BUFFER_OK == status
551 ? GPU_TEXTURE_COMPRESSED
552 : GPU_TEXTURE_UNCOMPRESSED;
553
554 if (COMPRESSED_BUFFER_OK == status) {
555 int texture_level = 0;
556 for (int level = base_level; level < ptd->level_min; level++) {
557 int size = TextureTileSize(level, true);
558 int status = GetTextureLevel(ptd, rect, level, ptd->m_colorscheme);
559 int dim = TextureDim(level);
560 glCompressedTexImage2D(GL_TEXTURE_2D, texture_level, g_raster_format, dim,
561 dim, 0, size, ptd->comp_array[level]);
562
563 ptd->tex_mem_used += size;
564 g_tex_mem_used += size;
565 texture_level++;
566
567 if (!b_use_mipmaps) break;
568 }
569
570 // Free bitmap memory that has already been uploaded to the GPU
571 ptd->FreeMap();
572 ptd->FreeComp();
573 } else { // COMPRESSED_BUFFER_OK == status
574 if (m_newCatalog) {
575 // it's an empty catalog or it's not used, odds it's going to be slow
576 BasePlatform::ShowBusySpinner();
577 busy_shown = true;
578 m_newCatalog = false;
579 }
580
581 // This level has not been compressed yet, and is not in the cache
582#if 1 // perhaps we should eliminate this case
583 // and build compressed fxt1 textures one per tick
584 if (GL_COMPRESSED_RGB_FXT1_3DFX == g_raster_format &&
585 g_GLOptions.m_bTextureCompression) {
586 // this version avoids re-uploading the data
587 g_glTextureManager->ScheduleJob(this, rect, base_level, true, false, true,
588 true);
589 ptd->FreeMap();
590 ptd->nGPU_compressed = GPU_TEXTURE_COMPRESSED;
591 b_use_mipmaps = b_use_compressed_mipmaps;
592 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
593 GL_LINEAR_MIPMAP_LINEAR);
594 } else
595#endif
596 {
597 int uc_base_level = base_level;
598 if (b_lowmem) uc_base_level++;
599 int texture_level = 0;
600 for (int level = uc_base_level; level < ptd->level_min + b_lowmem;
601 level++) {
602 int status = GetTextureLevel(ptd, rect, level, ptd->m_colorscheme);
603 int dim = TextureDim(level);
604 glTexImage2D(GL_TEXTURE_2D, texture_level, GL_RGB, dim, dim, 0,
605 FORMAT_BITS, GL_UNSIGNED_BYTE, ptd->map_array[level]);
606 int size = TextureTileSize(level, false);
607 ptd->tex_mem_used += size;
608 g_tex_mem_used += size;
609 texture_level++;
610
611 if (!b_use_mipmaps) break;
612 }
613 }
614 }
615
616 ptd->level_min = base_level;
617
618 // free all mipmaps more than a level less than this
619 for (int i = 0; i < base_level - 1; i++) {
620 free(ptd->map_array[i]);
621 ptd->map_array[i] = 0;
622 }
623
624 if (busy_shown) AbstractPlatform::HideBusySpinner();
625
626 return true;
627}
628
629bool glTexFactory::PrepareTexture(int base_level, const wxRect &rect,
630 ColorScheme color_scheme, int mem_used) {
631 glTextureDescriptor *ptd = NULL;
632
633 try {
634 ptd = GetOrCreateTD(rect);
635
636 ptd->m_colorscheme = color_scheme;
637
638 // glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); // why?
639
640 if (!BuildTexture(ptd, base_level, rect))
641 glBindTexture(GL_TEXTURE_2D, ptd->tex_name);
642
643 // should we schedule compression?
644 if (g_GLOptions.m_bTextureCompression &&
645 ptd->nGPU_compressed == GPU_TEXTURE_UNCOMPRESSED) {
646 // scheduling at base_level reduces vram usage but is slower overall
647 // probably shouldn't be used for caching until it can cache each level
648 g_glTextureManager->ScheduleJob(this, rect, 0 /*base_level*/, true, false,
649 true, false);
650 if (GL_COMPRESSED_RGB_FXT1_3DFX == g_raster_format)
651 glBindTexture(GL_TEXTURE_2D, ptd->tex_name); // reset texture binding
652
653 // Free the map in ram
654 ptd->FreeMap();
655 }
656
657#if 0
658 /* this is not a good place to call GetMemoryStatus
659 function as implemented takes for me 1 millisecond to execute.
660 In the worst case zoomed out charts can reach thousands of textures and several seconds to render one frame instead of 20-30fps disabling this here
661
662 Memory is already freed in glTextureManager::FactoryCrunch(double factor)
663 so the below is probably not needed.*/
664
665 // If global memory is getting short, we can crunch here.
666 // All mipmaps >= ptd->level_min have been uploaded to the GPU,
667 // so there is no reason to save the bits forever.
668 // Of course, this means that if the texture is deleted elsewhere, then the bits will need to be
669 // regenerated. The price to pay for memory limits....
670 if (g_memCacheLimit > 0) {
671 // GetMemoryStatus is slow on linux
672 if(mem_used > g_memCacheLimit * 7 / 10)
673 ptd->FreeMap();
674
675 if(mem_used > g_memCacheLimit * 9 / 10)
676 ptd->FreeAll();
677 }
678#endif
679
680 // g_Platform->HideBusySpinner();
681
682 return true;
683 } // try
684
685 catch (...) {
686 // Clean up
687 ptd->FreeAll();
688 DeleteSingleTexture(ptd);
689 return false;
690 }
691}
692
693void glTexFactory::PrepareTiles(const ViewPort &vp, bool use_norm_vp,
694 ChartBase *chart) {
695 ChartBaseBSB *pChartBSB = dynamic_cast<ChartBaseBSB *>(chart);
696 if (!pChartBSB) return;
697
698 // detect changing north/south polar
699 if (vp.m_projection_type == PROJECTION_POLAR) {
700 bool north = vp.clat > 0;
701 if (m_north != north) m_prepared_projection_type = 0;
702 m_north = north;
703 }
704
705 if (vp.m_projection_type == m_prepared_projection_type) return;
706
707 m_prepared_projection_type = vp.m_projection_type;
708
709 double native_scale;
710
711 native_scale = pChartBSB->GetNativeScale();
712
713 if (m_tiles)
714 for (int i = 0; i < m_ntex; i++) delete m_tiles[i];
715 delete[] m_tiles;
716 m_tiles = new glTexTile *[m_ntex];
717
718 int tex_dim = g_GLOptions.m_iTextureDimension;
719
720 // split cells for accuracy, much more with larger charts, and toward poles
721 // depending on projection of viewport and chart
722 // This is a very simplistic algorithm to determine split count, could be
723 // greatly improved
724
725 double xsplits, ysplits;
726 switch (vp.m_projection_type) {
727 case PROJECTION_POLAR:
728 case PROJECTION_STEREOGRAPHIC:
729 case PROJECTION_ORTHOGRAPHIC:
730 case PROJECTION_GNOMONIC:
731 case PROJECTION_POLYCONIC:
732 xsplits = native_scale / 1000000000.0 * tex_dim; // todo: fix this
733 // xsplits /= (1 << base_level); // split less zoomed out
734
735 // split more near poles
736 if (vp.m_projection_type == PROJECTION_ORTHOGRAPHIC) {
737 Extent e;
738 pChartBSB->GetChartExtent(&e);
739 xsplits = xsplits * wxMax(fabsf(e.NLAT), fabsf(e.SLAT)) / 90;
740 }
741
742 xsplits = round(xsplits);
743 ysplits = 2 * xsplits;
744
745 xsplits = wxMin(wxMax(xsplits, 1), 8);
746 ysplits = wxMin(wxMax(ysplits, 1), 8);
747 break;
748 case PROJECTION_EQUIRECTANGULAR:
749 // needed for skewed charts?
750 // xsplits = ysplits = 4;
751 // break;
752 default:
753 xsplits = ysplits =
754 1; // TODO: is this good enough in all cases to reproject
755 // non-mercator charts or even SM_ECC mercator charts in all cases?
756 }
757
758 ViewPort nvp;
759 if (use_norm_vp) {
760 pChartBSB->chartpix_to_latlong(m_size_X / 2, m_size_Y / 2, &m_clat,
761 &m_clon);
762 nvp = glChartCanvas::NormalizedViewPort(vp, m_clat, m_clon);
763 }
764
765 // Using a 2D loop, iterate thru the texture tiles of the chart
766 wxRect rect;
767 rect.y = 0;
768 for (int i = 0; i < m_ny_tex; i++) {
769 rect.height = tex_dim;
770 rect.x = 0;
771 for (int j = 0; j < m_nx_tex; j++) {
772 rect.width = tex_dim;
773
774 glTexTile *tile = m_tiles[i * m_nx_tex + j] = new glTexTile;
775 tile->rect = rect;
776
777 double lat, lon;
778 float ll[8];
779 int x[4] = {rect.x, rect.x, rect.x + rect.width, rect.x + rect.width};
780 int y[4] = {rect.y + rect.height, rect.y, rect.y, rect.y + rect.height};
781
782 for (int k = 0; k < 4; k++) {
783 pChartBSB->chartpix_to_latlong(x[k], y[k], &lat, &lon);
784 ll[2 * k + 0] = lon, ll[2 * k + 1] = lat;
785 }
786
787 // resolve idl
788 float lonmin = ll[0], lonmax = ll[0];
789 float latmin = ll[1], latmax = ll[1];
790 for (int i = 2; i < 8; i += 2) {
791 lonmin = wxMin(lonmin, ll[i]), lonmax = wxMax(lonmax, ll[i]);
792 latmin = wxMin(latmin, ll[i + 1]), latmax = wxMax(latmax, ll[i + 1]);
793 }
794
795 if (fabsf(lonmin - lonmax) > 180) {
796 lonmin = 540, lonmax = 0;
797 for (int i = 0; i < 8; i += 2) {
798 float lon = ll[i] < 0 ? ll[i] + 360 : ll[i];
799 lonmin = wxMin(lonmin, lon), lonmax = wxMax(lonmax, lon);
800 }
801 }
802
803 tile->box.Set(latmin, lonmin, latmax, lonmax);
804
805 double xs = rect.width / xsplits;
806 double ys = rect.height / ysplits;
807 double x1 = rect.x, u1 = 0;
808
809 int maxncoords = 4 * xsplits * ysplits;
810 tile->m_coords = new float[2 * maxncoords];
811 tile->m_texcoords = new float[2 * maxncoords];
812
813 tile->m_ncoords = 0;
814
815 int end = 0; // should be 1<<base_level but we have no way to know now
816
817 for (int x = 0; x < xsplits; x++) {
818 double x2 = wxMin(x1 + xs, m_size_X - end);
819 double u2 = (x2 - rect.x) / rect.width;
820
821 double y1 = rect.y, v1 = 0;
822 for (int y = 0; y < ysplits; y++) {
823 double y2 = wxMin(y1 + ys, m_size_Y - end);
824 double v2 = (y2 - rect.y) / rect.height;
825
826 // todo avoid extra calls per loop and also caching from above
827 double xc[4] = {x1, x1, x2, x2}, yc[4] = {y2, y1, y1, y2};
828 double lat[4], lon[4];
829 for (int k = 0; k < 4; k++) {
830 pChartBSB->chartpix_to_latlong(xc[k], yc[k], lat + k, lon + k);
831 }
832
833 double u[4] = {u1, u1, u2, u2}, v[4] = {v2, v1, v1, v2};
834 for (int j = 0; j < 4; j++) {
835 int idx = 2 * tile->m_ncoords;
836 tile->m_texcoords[idx + 0] = u[j];
837 tile->m_texcoords[idx + 1] = v[j];
838
839 if (use_norm_vp) {
840 wxPoint2DDouble p = nvp.GetDoublePixFromLL(lat[j], lon[j]);
841 tile->m_coords[idx + 0] = p.m_x;
842 tile->m_coords[idx + 1] = p.m_y;
843 } else {
844 tile->m_coords[idx + 0] = lat[j];
845 tile->m_coords[idx + 1] = lon[j];
846 }
847 tile->m_ncoords++;
848 }
849
850 if (y1 + ys > m_size_Y - end) break;
851
852 v1 = v2;
853 y1 = y2;
854 }
855 if (x1 + xs > m_size_X - end) break;
856
857 u1 = u2;
858 x1 = x2;
859 }
860 rect.x += rect.width;
861 }
862 rect.y += rect.height;
863 }
864}
865
866bool glTexFactory::UpdateCacheLevel(const wxRect &rect, int level,
867 ColorScheme color_scheme,
868 unsigned char *data, int size) {
869 if (!g_GLOptions.m_bTextureCompressionCaching) return false;
870
871 if (!data) return false;
872
873 // Search for the requested texture
874 // Search the catalog for this particular texture
876 GetCacheEntryValue(level, rect.x, rect.y, color_scheme);
877
878 // This texture is already done
879 if (v != 0) return false;
880
881 return UpdateCachePrecomp(data, size, rect, level, color_scheme);
882}
883
884bool glTexFactory::UpdateCacheAllLevels(const wxRect &rect,
885 ColorScheme color_scheme,
886 unsigned char **compcomp_array,
887 int *compcomp_size) {
888 if (!g_GLOptions.m_bTextureCompressionCaching) return false;
889
890 bool work = false;
891
892 for (int level = 0; level < g_mipmap_max_level + 1; level++)
893 work |= UpdateCacheLevel(rect, level, color_scheme, compcomp_array[level],
894 compcomp_size[level]);
895 if (work) {
896 WriteCatalogAndHeader();
897 }
898
899 return work;
900}
901
902int glTexFactory::GetTextureLevel(glTextureDescriptor *ptd, const wxRect &rect,
903 int level, ColorScheme color_scheme) {
904 // Already available in the texture descriptor?
905 if (g_GLOptions.m_bTextureCompression) {
906 if (ptd->comp_array[level]) return COMPRESSED_BUFFER_OK;
907 if (ptd->compcomp_array[level]) {
908 // If we have the compcomp bits in ram decompress them
909 int size = TextureTileSize(level, true);
910 unsigned char *cb = (unsigned char *)malloc(size);
911 LZ4_decompress_fast((char *)ptd->compcomp_array[level], (char *)cb, size);
912 ptd->comp_array[level] = cb;
913 return COMPRESSED_BUFFER_OK;
914 } else if (g_GLOptions.m_bTextureCompressionCaching) {
915 // If cacheing compressed textures, look in the cache
916 // Search for the requested texture
917 // Search the catalog for this particular texture
919 GetCacheEntryValue(level, rect.x, rect.y, color_scheme);
920
921 // Requested texture level is found in the cache
922 // so go load it
923 if (p != 0) {
924 int size = TextureTileSize(level, true);
925
926 if (m_fs->IsOpened()) {
927 m_fs->Seek(p->texture_offset);
928 ptd->comp_array[level] = (unsigned char *)malloc(size);
929 int max_compressed_size = LZ4_COMPRESSBOUND(g_tile_size);
930 char *compressed_data = (char *)malloc(p->compressed_size);
931 m_fs->Read(compressed_data, p->compressed_size);
932 LZ4_decompress_fast(compressed_data, (char *)ptd->comp_array[level],
933 size);
934 free(compressed_data);
935 }
936
937 return COMPRESSED_BUFFER_OK;
938 }
939 }
940 }
941
942 // Requested Texture level is not in cache, and not already built
943 // So go build it
944 if (!ptd->map_array[level]) GetFullMap(ptd, rect, m_ChartPath, level);
945
946 return MAP_BUFFER_OK;
947}
948
949// return not used
950// false? never
951// true
952bool glTexFactory::LoadHeader() {
953 if (m_hdrOK) return true;
954
955 bool need_new = false;
956
957 if (wxFileName::FileExists(m_CompressedCacheFilePath)) {
958 m_fs = new wxFFile(m_CompressedCacheFilePath, "rb+");
959 if (m_fs->IsOpened()) {
961
962 // Header is located at the end of the file
963 wxFileOffset hdr_offset = m_fs->Length() - sizeof(hdr);
964 hdr_offset = m_fs->Seek(hdr_offset);
965
966 if (sizeof(hdr) == m_fs->Read(&hdr, sizeof(hdr))) {
967 if (hdr.magic != COMPRESSED_CACHE_MAGIC ||
968 hdr.chartdate != m_chart_date_binary ||
969 hdr.chartfile_date != m_chartfile_date_binary ||
970 hdr.chartfile_size != m_chartfile_size ||
971 hdr.format != g_raster_format) {
972 // Bad header signature
973 delete m_fs;
974 need_new = true;
975 } else { // good header
976 n_catalog_entries = hdr.m_nentries;
977 m_catalog_offset = hdr.catalog_offset;
978 }
979 } else { // file exists, and is empty
980 n_catalog_entries = 0;
981 m_catalog_offset = 0;
982 WriteCatalogAndHeader();
983 }
984 } // is open
985
986 else { // some problem opening file, probably permissions on Win7
987 delete m_fs;
988 need_new = true;
989 wxRemoveFile(m_CompressedCacheFilePath);
990 }
991
992 } // exists
993
994 else { // File does not exist
995 wxFileName fn(m_CompressedCacheFilePath);
996 if (!fn.DirExists()) fn.Mkdir();
997 need_new = true;
998 }
999
1000 if (need_new) {
1001 // Create new file, with empty catalog, and correct header
1002 m_fs = new wxFFile(m_CompressedCacheFilePath, "wb");
1003 n_catalog_entries = 0;
1004 m_catalog_offset = 0;
1005 WriteCatalogAndHeader();
1006 delete m_fs;
1007
1008 m_fs = new wxFFile(m_CompressedCacheFilePath, "rb+");
1009 }
1010 m_hdrOK = true;
1011 return true;
1012}
1013
1014bool glTexFactory::AddCacheEntryValue(const CatalogEntry &p) {
1015 if ((int)p.k.tcolorscheme < 0 || p.k.tcolorscheme >= N_COLOR_SCHEMES)
1016 return false;
1017
1018 if (p.k.mip_level < 0 || p.k.mip_level >= MAX_TEX_LEVEL) return false;
1019
1020 int array_index = ArrayIndex(p.k.x, p.k.y);
1021 if (array_index < 0 || array_index >= m_ntex) return false;
1022
1023 if (m_cache[p.k.tcolorscheme][p.k.mip_level] == 0)
1024 m_cache[p.k.tcolorscheme][p.k.mip_level] =
1025 (CatalogEntryValue *)calloc(m_ntex, sizeof(CatalogEntryValue));
1026
1027 CatalogEntryValue *v = m_cache[p.k.tcolorscheme][p.k.mip_level];
1028 CatalogEntryValue *r = &v[array_index];
1029 *r = p.v;
1030 return true;
1031}
1032
1033bool glTexFactory::LoadCatalog() {
1034 m_newCatalog = false;
1035 if (m_catalogOK) return true;
1036
1037 if (!LoadHeader()) return false;
1038
1039 if (n_catalog_entries == 0) {
1040 // new empty header
1041 m_catalogOK = true;
1042 m_newCatalog = true;
1043 return true;
1044 }
1045
1046 m_fs->Seek(m_catalog_offset);
1047
1048 CatalogEntry ps;
1049 int buf_size = ps.GetSerialSize();
1050 unsigned char *buf = (unsigned char *)malloc(buf_size);
1051
1052 CatalogEntry p;
1053 bool bad = false;
1054 for (int i = 0; i < n_catalog_entries; i++) {
1055 m_fs->Read(buf, buf_size);
1056 p.DeSerialize(buf);
1057 if (!AddCacheEntryValue(p)) bad = true;
1058 }
1059
1060 free(buf);
1061 if (bad && !m_catalogCorrupted) {
1062 wxLogMessage("Bad cache catalog %s %s", m_ChartPath.c_str(),
1063 m_CompressedCacheFilePath.c_str());
1064 m_catalogCorrupted = true;
1065 }
1066 m_catalogOK = true;
1067 return true;
1068}
1069
1070bool glTexFactory::WriteCatalogAndHeader() {
1071 if (m_fs && m_fs->IsOpened()) {
1072 m_fs->Seek(m_catalog_offset);
1073
1074 CatalogEntry ps;
1075 int buf_size = ps.GetSerialSize();
1076 unsigned char buf[CATALOG_ENTRY_SERIAL_SIZE]; // MSVC requires constant
1077 // stack array size...
1078 int new_n_catalog_entries = 0;
1079 CatalogEntry p;
1080 wxRect rect;
1081 for (int i = 0; i < N_COLOR_SCHEMES; i++) {
1082 p.k.tcolorscheme = (ColorScheme)i;
1083 for (int j = 0; j < MAX_TEX_LEVEL; j++) {
1084 CatalogEntryValue *v = m_cache[i][j];
1085 if (!v) continue;
1086 p.k.mip_level = j;
1087 for (int k = 0; k < m_ntex; k++) {
1088 ArrayXY(&rect, k);
1089 p.k.y = rect.y;
1090 p.k.x = rect.x;
1091 CatalogEntryValue *r = &v[k];
1092 if (r->compressed_size == 0) continue;
1093 p.v = *r;
1094 new_n_catalog_entries++;
1095 p.Serialize(buf);
1096 m_fs->Write(buf, buf_size);
1097 }
1098 }
1099 }
1100
1101 n_catalog_entries = new_n_catalog_entries;
1102 // Write header at file end
1104 hdr.magic = COMPRESSED_CACHE_MAGIC;
1105 hdr.format = g_raster_format;
1106 hdr.m_nentries = n_catalog_entries;
1107 hdr.catalog_offset = m_catalog_offset;
1108 hdr.chartdate = m_chart_date_binary;
1109 hdr.chartfile_date = m_chartfile_date_binary;
1110 hdr.chartfile_size = m_chartfile_size;
1111
1112 m_fs->Write(&hdr, sizeof(hdr));
1113 m_fs->Flush();
1114
1115 return true;
1116 } else
1117 return false;
1118}
1119
1120bool glTexFactory::UpdateCachePrecomp(unsigned char *data, int data_size,
1121 const wxRect &rect, int level,
1122 ColorScheme color_scheme,
1123 bool write_catalog) {
1124 if (level < 0 || level >= MAX_TEX_LEVEL) return false; // XXX BUG
1125
1126 // Search the catalog for this particular texture
1127 if (GetCacheEntryValue(level, rect.x, rect.y, color_scheme) != 0)
1128 return false;
1129
1130 // Make sure the file exists
1131 wxASSERT(m_fs != 0);
1132
1133 if (!m_fs->IsOpened()) return false;
1134
1135 // Create a new catalog entry
1136 CatalogEntry p(level, rect.x, rect.y, color_scheme);
1137
1138 // Write the compressed data to disk
1139 p.v.texture_offset = m_catalog_offset;
1140
1141 p.v.compressed_size = data_size;
1142 AddCacheEntryValue(p);
1143 n_catalog_entries++;
1144
1145 // We write the new data at the current catalog offset, overwriting the
1146 // old catalog
1147 m_fs->Seek(m_catalog_offset);
1148 m_fs->Write(data, data_size);
1149
1150 // Write the catalog and Header (which follows the catalog at the end of
1151 // the file
1152 m_catalog_offset += data_size;
1153 if (write_catalog) WriteCatalogAndHeader();
1154
1155 return true;
1156}
Basic platform specific support utilities without GUI deps.
General chart base definitions.
ChartDB * ChartData
Global instance.
Definition chartdb.cpp:70
Charts database management
BSB chart management.
Generic Chart canvas base.
Base class for BSB (Maptech/NOS) format nautical charts.
Definition chartimg.h:128
Base class for all chart types.
Definition chartbase.h:125
ViewPort - Core geographic projection and coordinate transformation engine.
Definition viewport.h:56
wxPoint2DDouble GetDoublePixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates with double precision.
Definition viewport.cpp:133
double clat
Center latitude of the viewport in degrees.
Definition viewport.h:197
Global variables stored in configuration file.
GLuint g_raster_format
Global instance.
OpenGL chart rendering canvas.
glTextureManager * g_glTextureManager
Global instance.
OpenGL texture cache.
OpenGL texture container.
int g_mipmap_max_level
Global instance.
Definition gui_vars.cpp:85
Miscellaneous globals primarely used by gui layer, not persisted in configuration file.
OpenCPN Platform specific support utilities.
Chart quilt support.
Geographic projection and coordinate transformations.