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