30#include <wx/progdlg.h>
34#if defined(__OCPN__ANDROID__)
36#elif defined(__WXQT__) || defined(__WXGTK__)
42#include "glTexCache.h"
43#include "glTextureDescriptor.h"
46#include "glChartCanvas.h"
51#include "OCPNPlatform.h"
53#include "mipmap/mipmap.h"
55#include "ocpn_frame.h"
56#include "model/own_ship.h"
58#ifndef GL_ETC1_RGB8_OES
59#define GL_ETC1_RGB8_OES 0x8D64
66#include <wx/listimpl.cpp>
68using JobList = std::list<JobTicket *>;
70WX_DEFINE_ARRAY_PTR(
ChartCanvas *, arrayofCanvasPtr);
72extern int g_mipmap_max_level;
73extern GLuint g_raster_format;
74extern int g_memCacheLimit;
77extern long g_tex_mem_used;
78extern int g_tile_size;
79extern int g_uncompressed_tile_size;
80extern int g_nCPUCount;
82extern bool b_inCompressAllCharts;
84extern arrayofCanvasPtr g_canvasArray;
87extern ColorScheme global_color_scheme;
89extern bool GetMemoryStatus(
int *mem_total,
int *mem_used);
92bool g_throttle_squish;
98wxString CompressedCachePath(wxString path) {
100 int colon = path.find(
':', 0);
101 if (colon != wxNOT_FOUND) path.Remove(colon, 1);
105 wxChar separator = wxFileName::GetPathSeparator();
106 for (
unsigned int pos = 0; pos < path.size(); pos = path.find(separator, pos))
107 path.replace(pos, 1,
"!");
111 wxCharBuffer buf = path.ToUTF8();
112 unsigned char sha1_out[20];
113 sha1((
unsigned char *)buf.data(), strlen(buf.data()), sha1_out);
116 for (
unsigned int i = 0; i < 20; i++) {
118 s.Printf(
"%02X", sha1_out[i]);
126int g_mipmap_max_level = 4;
129OCPN_CompressProgressEvent::OCPN_CompressProgressEvent(wxEventType commandType,
int id)
130:wxEvent(id, commandType)
134OCPN_CompressProgressEvent::~OCPN_CompressProgressEvent()
138wxEvent* OCPN_CompressProgressEvent::Clone()
const
140 OCPN_CompressProgressEvent *newevent=
new OCPN_CompressProgressEvent(*
this);
141 newevent->m_string=this->m_string;
142 newevent->count=this->count;
143 newevent->thread=this->thread;
148static double chart_dist(
int index) {
154 if (cte.GetBBox().Contains(gLat, gLon))
159 clon = (cte.GetLonMax() + cte.GetLonMin()) / 2;
160 d = DistGreatCircle(cte.GetLatMax(), clon, gLat, gLon);
161 t = DistGreatCircle(cte.GetLatMin(), clon, gLat, gLon);
164 clat = (cte.GetLatMax() + cte.GetLatMin()) / 2;
165 t = DistGreatCircle(clat, cte.GetLonMin(), gLat, gLon);
167 t = DistGreatCircle(clat, cte.GetLonMax(), gLat, gLon);
173WX_DEFINE_SORTED_ARRAY_INT(
int, MySortedArrayInt);
174int CompareInts(
int n1,
int n2) {
175 double d1 = chart_dist(n1);
176 double d2 = chart_dist(n2);
177 return (
int)(d1 - d2);
180static MySortedArrayInt idx_sorted_by_distance(CompareInts);
188#include <wx/arrimpl.cpp>
193JobTicket::JobTicket() {
194 for (
int i = 0; i < 10; i++) {
195 compcomp_size_array[i] = 0;
196 comp_bits_array[i] = NULL;
197 compcomp_bits_array[i] = NULL;
207void FlattenColorsForCompression(
unsigned char *data,
int dim,
bool swap_colors=
true)
212 for(
int i = 0; i<dim*dim; i++) {
214 unsigned char t = data[off + 0];
215 data[off + 0] = data[off + 2] & 0xfc;
216 data[off + 1] &= 0xf8;
217 data[off + 2] = t & 0xfc;
221 for(
int i = 0; i<dim*dim; i++) {
223 data[off + 0] &= 0xfc;
224 data[off + 1] &= 0xf8;
225 data[off + 2] &= 0xfc;
231static void CompressDataETC(
const unsigned char *data,
int dim,
int size,
232 unsigned char *tex_data,
volatile bool &b_abort) {
233 wxASSERT(dim * dim == 2 * size || (dim < 4 && size == 8));
234 uint64_t *tex_data64 = (uint64_t *)tex_data;
236 int mbrow = wxMin(4, dim), mbcol = wxMin(4, dim);
237 uint8_t
block[48] = {};
238 for (
int row = 0; row < dim; row += 4) {
239 for (
int col = 0; col < dim; col += 4) {
240 for (
int brow = 0; brow < mbrow; brow++)
241 for (
int bcol = 0; bcol < mbcol; bcol++)
242 memcpy(
block + (bcol * 4 + brow) * 3,
243 data + ((row + brow) * dim + col + bcol) * 3, 3);
245 extern uint64_t ProcessRGB(
const uint8_t *src);
246 *tex_data64++ = ProcessRGB(
block);
252static bool CompressUsingGPU(
const unsigned char *data,
int dim,
int size,
253 unsigned char *tex_data,
int level,
bool inplace) {
254#ifndef USE_ANDROID_GLES2
258 glGenTextures(1, &comp_tex);
259 glBindTexture(GL_TEXTURE_2D, comp_tex);
260 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
261 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
265 glTexImage2D(GL_TEXTURE_2D, level, g_raster_format, dim, dim, 0, GL_RGB,
266 GL_UNSIGNED_BYTE, data);
269 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_ARB,
272 if (compressed == GL_TRUE) {
274 GLint compressedSize;
275 glGetTexLevelParameteriv(GL_TEXTURE_2D, level,
276 GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &compressedSize);
278 if (compressedSize != size)
return false;
281 glGetCompressedTexImage(GL_TEXTURE_2D, level, tex_data);
284 if (!inplace) glDeleteTextures(1, &comp_tex);
293 wxString &chart_path) {
296 ptd->map_array[0] = 0;
301 (
unsigned char *)calloc(ncrect.width * ncrect.height * 4, 1);
309 unsigned char *t_buf =
310 (
unsigned char *)malloc(ncrect.width * ncrect.height * 4);
311 pBSBChart->GetChartBits(ncrect, t_buf, 1);
314 ptd->map_array[0] = t_buf;
317 (
unsigned char *)calloc(ncrect.width * ncrect.height * 4, 1);
323 wxString chart_path,
int level) {
326 if (ptd->map_array[level])
return;
330 for (first_level = level; first_level; first_level--)
331 if (ptd->map_array[first_level - 1])
break;
335 GetLevel0Map(ptd, rect, chart_path);
339 int dim = g_GLOptions.m_iTextureDimension;
340 for (
int i = 0; i <= level; i++) {
341 if (i >= first_level) {
342 ptd->map_array[i] = (
unsigned char *)malloc(dim * dim * 3);
343 MipMap_24(2 * dim, 2 * dim, ptd->map_array[i - 1], ptd->map_array[i]);
349int TextureDim(
int level) {
350 int dim = g_GLOptions.m_iTextureDimension;
351 for (
int i = 0; i < level; i++) dim /= 2;
355int TextureTileSize(
int level,
bool compressed) {
356 if (level == g_mipmap_max_level + 1)
return 0;
361 for (
int i = 0; i < level; i++) {
363 if (size < 8) size = 8;
366 size = g_uncompressed_tile_size;
367 for (
int i = 0; i < level; i++) size /= 4;
373bool JobTicket::DoJob() {
374 if (!m_rect.IsEmpty())
return DoJob(m_rect);
378 if (!pchart)
return false;
381 if (!pBSBChart)
return false;
383 int size_X = pBSBChart->GetSize_X();
384 int size_Y = pBSBChart->GetSize_Y();
386 int dim = g_GLOptions.m_iTextureDimension;
388 int nx_tex = ceil((
float)size_X / dim);
389 int ny_tex = ceil((
float)size_Y / dim);
395 for (
int y = 0; y < ny_tex; y++) {
396 if (pthread && pthread->m_pMessageTarget) {
399 Nevent.nstat_max = ny_tex;
401 Nevent.SetTicket(
this);
402 pthread->m_pMessageTarget->AddPendingEvent(Nevent);
406 for (
int x = 0; x < nx_tex; x++) {
407 if (!DoJob(rect))
return false;
409 pFact->UpdateCacheAllLevels(rect, global_color_scheme,
410 compcomp_bits_array, compcomp_size_array);
412 for (
int i = 0; i < g_mipmap_max_level + 1; i++) {
413 free(comp_bits_array[i]), comp_bits_array[i] = 0;
414 free(compcomp_bits_array[i]), compcomp_bits_array[i] = 0;
417 rect.x += rect.width;
419 rect.y += rect.height;
431 void Start() { clock_gettime(CLOCK_REALTIME, &tp); }
435 clock_gettime(CLOCK_REALTIME, &tp_end);
436 return (tp_end.tv_sec - tp.tv_sec) * 1.e3 + (tp_end.tv_nsec - tp.tv_nsec) / 1.e6;
446static void throttle_func(
void *data) {
447 if (!wxThread::IsMain()) {
449 if (sww->Time() > 1) {
456static wxMutex s_mutexProtectingChartBitRead;
458bool JobTicket::DoJob(
const wxRect &rect) {
459 unsigned char *bit_array[10];
460 for (
int i = 0; i < 10; i++) bit_array[i] = 0;
464 bit_array[0] = level0_bits;
475 wxMutexLocker lock(s_mutexProtectingChartBitRead);
477 index =
ChartData->FinddbIndex(m_ChartPath);
478 pchart =
ChartData->OpenChartFromDBAndLock(index, FULL_INIT);
480 if (pchart &&
ChartData->IsChartLocked(index)) {
484 (
unsigned char *)malloc(ncrect.width * ncrect.height * 4);
485 pBSBChart->GetChartBits(ncrect, bit_array[0], 1);
495 if (!bit_array[0])
return false;
498 dim = g_GLOptions.m_iTextureDimension;
500 for (
int i = 1; i < g_mipmap_max_level + 1; i++) {
501 size_t nmalloc = wxMax(dim * dim * 3, 4 * 4 * 3);
502 bit_array[i] = (
unsigned char *)malloc(nmalloc);
503 MipMap_24(2 * dim, 2 * dim, bit_array[i - 1], bit_array[i]);
507 int texture_level = 0;
508 for (
int level = level_min_request; level < g_mipmap_max_level + 1; level++) {
509 int dim = TextureDim(level);
510 int size = TextureTileSize(level,
true);
511 unsigned char *tex_data = (
unsigned char *)malloc(size);
512 if (g_raster_format == GL_COMPRESSED_RGB_S3TC_DXT1_EXT) {
514 int flags = squish::kDxt1 | squish::kColourRangeFit;
516 if (g_GLOptions.m_bTextureCompressionCaching) {
520 flags = squish::kDxt1 | squish::kColourClusterFit;
524 squish::CompressImageRGBpow2_Flatten_Throttle_Abort(
525 bit_array[level], dim, dim, tex_data, flags,
true,
526 b_throttle ? throttle_func : 0, &sww, b_abort);
528 }
else if (g_raster_format == GL_ETC1_RGB8_OES)
529 CompressDataETC(bit_array[level], dim, size, tex_data, b_abort);
530 else if (g_raster_format == GL_COMPRESSED_RGB_FXT1_3DFX) {
531 if (!CompressUsingGPU(bit_array[level], dim, size, tex_data,
532 texture_level, binplace)) {
537 if (binplace) g_tex_mem_used += size;
541 comp_bits_array[level] = tex_data;
544 for (
int i = 0; i < g_mipmap_max_level + 1; i++) {
553 for (
int i = 0; i < g_mipmap_max_level + 1; i++) {
558 if (b_throttle) wxThread::Sleep(1);
560 if (b_abort)
return false;
562 if (bpost_zip_compress) {
563 int max_compressed_size = LZ4_COMPRESSBOUND(g_tile_size);
564 for (
int level = level_min_request; level < g_mipmap_max_level + 1;
566 if (b_abort)
return false;
568 unsigned char *compressed_data =
569 (
unsigned char *)malloc(max_compressed_size);
570 int csize = TextureTileSize(level,
true);
572 char *src = (
char *)comp_bits_array[level];
573 int compressed_size =
574 LZ4_compressHC2(src, (
char *)compressed_data, csize, 4);
579 (
unsigned char *)realloc(compressed_data, compressed_size);
580 compcomp_bits_array[level] = compressed_data;
581 compcomp_size_array[level] = compressed_size;
604 SE_Exception(
unsigned int n) : nSE(n) {}
606 unsigned int getSeNumber() {
return nSE; }
609void my_translate(
unsigned int code, _EXCEPTION_POINTERS *ep) {
610 throw SE_Exception();
614OCPN_CompressionThreadEvent::OCPN_CompressionThreadEvent(
615 wxEventType commandType,
int id)
616 : wxEvent(id, commandType) {
620OCPN_CompressionThreadEvent::~OCPN_CompressionThreadEvent() {}
622wxEvent *OCPN_CompressionThreadEvent::Clone()
const {
625 newevent->m_ticket = this->m_ticket;
626 newevent->type = this->type;
627 newevent->nstat = this->nstat;
628 newevent->nstat_max = this->nstat_max;
660CompressionPoolThread::CompressionPoolThread(
JobTicket *ticket,
661 wxEvtHandler *message_target) {
662 m_pMessageTarget = message_target;
668void *CompressionPoolThread::Entry() {
670 _set_se_translator(my_translate);
680 SetPriority(WXTHREAD_MIN_PRIORITY);
682 if (!m_ticket->DoJob()) m_ticket->b_isaborted =
true;
684 if (m_pMessageTarget) {
686 Nevent.SetTicket(m_ticket);
688 m_pMessageTarget->QueueEvent(Nevent.Clone());
696 catch (SE_Exception e) {
697 if (m_pMessageTarget) {
699 m_ticket->b_isaborted =
true;
700 Nevent.SetTicket(m_ticket);
702 m_pMessageTarget->QueueEvent(Nevent.Clone());
713glTextureManager::glTextureManager() {
716 int nCPU = wxMax(1, wxThread::GetCPUCount());
717 if (g_nCPUCount > 0) nCPU = g_nCPUCount;
723 m_max_jobs = wxMax(nCPU, 1);
726 if (bthread_debug) printf(
" nCPU: %d m_max_jobs :%d\n", nCPU, m_max_jobs);
730 for (
int i = 0; i < m_max_jobs; i++) progList.push_back(
new ProgressInfoItem);
735 wxEVT_OCPN_COMPRESSIONTHREAD,
736 (wxObjectEventFunction)(wxEventFunction)&glTextureManager::OnEvtThread);
743 m_timer.Connect(wxEVT_TIMER, wxTimerEventHandler(glTextureManager::OnTimer),
748glTextureManager::~glTextureManager() {
751 for (
int i = 0; i < m_max_jobs; i++) {
752 auto it = progList.begin();
757 for (
auto hash : m_chart_texfactory_hash) {
760 m_chart_texfactory_hash.clear();
763#define NBAR_LENGTH 40
768 if (event.type == 1) {
776 for (
auto tnode = progList.begin(); tnode != progList.end(); tnode++) {
778 if (item->file_path == ticket->m_ChartPath) {
786 for (
auto tnode = progList.begin(); tnode != progList.end(); tnode++) {
788 if (item->file_path.IsEmpty()) {
790 item->file_path = ticket->m_ChartPath;
799 int bar_length = NBAR_LENGTH;
800 if (m_bcompact) bar_length = 20;
803 wxString
block = wxString::Format(
"%c", 0x2588);
805 if (event.nstat_max != 0)
806 cutoff = ((
event.nstat + 1) / (
float)
event.nstat_max) * bar_length;
807 for (
int i = 0; i < bar_length; i++) {
817 msgy.Printf(
" [%3d/%3d] ", event.nstat + 1, event.nstat_max);
820 wxFileName fn(ticket->m_ChartPath);
821 msgx += fn.GetFullName();
824 msgx.Printf(
"\n %3d/%3d", event.nstat + 1, event.nstat_max);
831 for (
auto tnode = progList.begin(); tnode != progList.end(); tnode++) {
833 msg += item->msgx +
"\n";
836 if (m_skipout) m_progMsg =
"Skipping, please wait...\n\n";
838 if (!m_progDialog->Update(m_jcnt, m_progMsg + msg, &m_skip)) m_skip =
true;
839 if (m_skip) m_skipout =
true;
843 if (ticket->b_isaborted || ticket->b_abort) {
844 for (
int i = 0; i < g_mipmap_max_level + 1; i++) {
845 free(ticket->comp_bits_array[i]);
846 free(ticket->compcomp_bits_array[i]);
851 " Abort job: %08X Jobs running: %d Job count: %lu "
853 ticket->ident, GetRunningJobCount(), (
unsigned long)todo_list.size());
854 }
else if (!ticket->b_inCompressAll) {
858 for (
int i = 0; i < g_mipmap_max_level + 1; i++)
859 ptd->comp_array[i] = ticket->comp_bits_array[i];
861 if (ticket->bpost_zip_compress) {
862 for (
int i = 0; i < g_mipmap_max_level + 1; i++) {
863 ptd->compcomp_array[i] = ticket->compcomp_bits_array[i];
864 ptd->compcomp_size[i] = ticket->compcomp_size_array[i];
871 gFrame->InvalidateAllGL();
872 ptd->compdata_ticks = 10;
877 " Finished job: %08X Jobs running: %d Job count: %lu "
879 ticket->ident, GetRunningJobCount(), (
unsigned long)todo_list.size());
883 if (ticket->b_inCompressAll) {
885 ChartData->OpenChartFromDB(ticket->m_ChartPath, FULL_INIT);
887 delete ticket->pFact;
890 for (
auto tnode = progList.begin(); tnode != progList.end(); ++tnode) {
892 if (item->file_path == ticket->m_ChartPath) item->file_path =
"";
895 if (g_raster_format != GL_COMPRESSED_RGB_FXT1_3DFX) {
896 auto found = std::find(running_list.begin(), running_list.end(), ticket);
897 if (found != running_list.end()) running_list.erase(found);
904void glTextureManager::OnTimer(wxTimerEvent &event) {
910 if (g_GLOptions.m_bTextureCompression) {
911 for (ChartPathHashTexfactType::iterator itt =
912 m_chart_texfactory_hash.begin();
913 itt != m_chart_texfactory_hash.end(); ++itt) {
915 if (ptf && ptf->OnTimer()) {
922 if((m_ticks % 4) == 0){
925 int mem_total, mem_used;
926 GetMemoryStatus(&mem_total, &mem_used);
930 int compcomp_size = 0;
932 for(ChartPathHashTexfactType::iterator itt = m_chart_texfactory_hash.begin();
933 itt != m_chart_texfactory_hash.end(); ++itt ) {
936 ptf->AccumulateMemStatistics(map_size, comp_size, compcomp_size);
939 int m1 = 1024 * 1024;
941 printf(
"%6d %6ld Map: %10d Comp:%10d CompComp: %10d \n", mem_used/1024, g_tex_mem_used/m1, map_size, comp_size, compcomp_size);
948bool glTextureManager::ScheduleJob(
glTexFactory *client,
const wxRect &rect,
949 int level,
bool b_throttle_thread,
950 bool b_nolimit,
bool b_postZip,
952 wxString chart_path = client->GetChartPath();
954 if (todo_list.size() >= 50) {
956 auto node = todo_list.rbegin();
958 auto found = std::find(todo_list.begin(), todo_list.end(), ticket);
959 todo_list.erase(found);
965 for (
auto node = todo_list.begin(); node != todo_list.end(); ++node) {
967 if ((ticket->m_ChartPath == chart_path) && (ticket->m_rect == rect)) {
969 auto found = std::find(todo_list.begin(), todo_list.end(), ticket);
970 if (found != todo_list.end()) todo_list.erase(found);
971 todo_list.insert(todo_list.begin(), ticket);
972 ticket->level_min_request = level;
978 for (
auto node = todo_list.begin(); node != todo_list.end(); ++node) {
980 if (ticket->m_rect == rect && ticket->m_ChartPath == chart_path) {
989 pt->level_min_request = level;
991 pt->ident = (ptd->tex_name << 16) + level;
992 pt->b_throttle = b_throttle_thread;
993 pt->m_ChartPath = chart_path;
995 pt->level0_bits = NULL;
997 pt->b_isaborted =
false;
998 pt->bpost_zip_compress = b_postZip;
999 pt->binplace = b_inplace;
1000 pt->b_inCompressAll = b_inCompressAllCharts;
1012 if (g_raster_format != GL_COMPRESSED_RGB_FXT1_3DFX) {
1013 todo_list.insert(todo_list.begin(), pt);
1014 if (bthread_debug) {
1016 GetMemoryStatus(0, &mem_used);
1017 printf(
"Adding job: %08X Job Count: %lu mem_used %d\n", pt->ident,
1018 (
unsigned long)todo_list.size(), mem_used);
1024 pt->level0_bits = ptd->map_array[0];
1025 ptd->map_array[0] = NULL;
1031 Nevent.SetTicket(pt);
1032 ProcessEventLocally(Nevent);
1038bool glTextureManager::StartTopJob() {
1039 auto node = todo_list.begin();
1040 if (node == todo_list.end())
return false;
1045 if (GetRunningJobCount() >= wxMax(m_max_jobs - ticket->b_throttle, 1))
1047 auto found = std::find(todo_list.begin(), todo_list.end(), ticket);
1048 if (found != todo_list.end()) todo_list.erase(found);
1052 if (ptd->comp_array[0]) {
1054 return StartTopJob();
1057 if (ptd->map_array[0]) {
1058 if (ticket->level_min_request == 0) {
1060 ticket->level0_bits = ptd->map_array[0];
1061 ptd->map_array[0] = NULL;
1064 int size = TextureTileSize(0,
false);
1065 ticket->level0_bits = (
unsigned char *)malloc(size);
1066 memcpy(ticket->level0_bits, ptd->map_array[0], size);
1070 running_list.push_back(ticket);
1071 DoThreadJob(ticket);
1076bool glTextureManager::DoThreadJob(
JobTicket *pticket) {
1078 printf(
" Starting job: %08X Jobs running: %d Jobs left: %lu\n",
1079 pticket->ident, GetRunningJobCount(),
1080 (
unsigned long)todo_list.size());
1085 pticket->pthread = t;
1092bool glTextureManager::AsJob(wxString
const &chart_path)
const {
1093 if (chart_path.Len()) {
1094 for (
auto node = todo_list.begin(); node != todo_list.end(); ++node) {
1096 if (ticket->m_ChartPath.IsSameAs(chart_path)) {
1104void glTextureManager::PurgeJobList(wxString chart_path) {
1105 if (chart_path.Len()) {
1107 for (
auto node = todo_list.begin(); node != todo_list.end(); ++node) {
1109 if (ticket->m_ChartPath.IsSameAs(chart_path)) {
1111 printf(
"Pool: Purge pending job for purged chart\n");
1112 auto found = std::find(todo_list.begin(), todo_list.end(), ticket);
1113 if (found != todo_list.end()) todo_list.erase(found);
1118 for (
auto node = todo_list.begin(); node != todo_list.end(); ++node) {
1120 if (ticket->m_ChartPath.IsSameAs(chart_path)) {
1121 ticket->b_abort =
true;
1126 printf(
"Pool: Purge, todo count: %lu\n",
1127 (
long unsigned)todo_list.size());
1129 for (
auto node = todo_list.begin(); node != todo_list.end(); ++node) {
1135 for (
auto node = running_list.begin(); node != running_list.end(); ++node) {
1137 ticket->b_abort =
true;
1142void glTextureManager::ClearJobList() {
1143 for (
auto node = todo_list.begin(); node != todo_list.end(); ++node) {
1150void glTextureManager::ClearAllRasterTextures(
void) {
1152 ChartPathHashTexfactType::iterator itt;
1153 for (itt = m_chart_texfactory_hash.begin();
1154 itt != m_chart_texfactory_hash.end(); ++itt) {
1159 m_chart_texfactory_hash.clear();
1161 if (g_tex_mem_used != 0)
1162 wxLogMessage(
"Texture memory use calculation error\n");
1165bool glTextureManager::PurgeChartTextures(
ChartBase *pc,
bool b_purge_factory) {
1167 ChartPathHashTexfactType::iterator ittf =
1168 m_chart_texfactory_hash.find(pc->GetHashKey());
1171 if (ittf != m_chart_texfactory_hash.end()) {
1175 if (b_purge_factory) {
1176 m_chart_texfactory_hash.erase(ittf);
1183 m_chart_texfactory_hash.erase(ittf);
1190bool glTextureManager::TextureCrunch(
double factor) {
1191 double hysteresis = 0.90;
1195 (double)(g_GLOptions.m_iTextureMemorySize * 1024 * 1024) * factor;
1196 if (!bGLMemCrunch)
return false;
1198 ChartPathHashTexfactType::iterator it0;
1199 for (it0 = m_chart_texfactory_hash.begin();
1200 it0 != m_chart_texfactory_hash.end(); ++it0) {
1203 wxString chart_full_path = ptf->GetChartPath();
1205 bGLMemCrunch = g_tex_mem_used >
1206 (double)(g_GLOptions.m_iTextureMemorySize * 1024 * 1024) *
1207 factor * hysteresis;
1208 if (!bGLMemCrunch)
break;
1211 for (
unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
1214 if (cc->GetVP().b_quilt)
1216 if (cc->m_pQuilt && cc->m_pQuilt->IsComposed() &&
1217 !cc->m_pQuilt->IsChartInQuilt(chart_full_path)) {
1218 ptf->DeleteSomeTextures(g_GLOptions.m_iTextureMemorySize * 1024 *
1219 1024 * factor * hysteresis);
1223 if (!cc->m_singleChart->GetFullPath().IsSameAs(chart_full_path)) {
1224 ptf->DeleteSomeTextures(g_GLOptions.m_iTextureMemorySize * 1024 *
1225 1024 * factor * hysteresis);
1235#define MAX_CACHE_FACTORY 50
1236bool glTextureManager::FactoryCrunch(
double factor) {
1237 if (m_chart_texfactory_hash.size() == 0) {
1243 GetMemoryStatus(0, &mem_used);
1244 double hysteresis = 0.90;
1245 ChartPathHashTexfactType::iterator it0;
1249 ((mem_used > (double)(g_memCacheLimit)*factor * hysteresis &&
1250 mem_used > (double)(m_prevMemUsed)*factor * hysteresis) ||
1251 (m_chart_texfactory_hash.size() > MAX_CACHE_FACTORY)));
1253 if (!bMemCrunch)
return false;
1257 int lru_oldest = 2147483647;
1260 for (it0 = m_chart_texfactory_hash.begin();
1261 it0 != m_chart_texfactory_hash.end(); ++it0) {
1264 wxString chart_full_path = ptf->GetChartPath();
1270 for (
unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
1273 if (cc->GetVP().b_quilt)
1275 if (cc->m_pQuilt && cc->m_pQuilt->IsComposed() &&
1276 !cc->m_pQuilt->IsChartInQuilt(chart_full_path)) {
1277 int lru = ptf->GetLRUTime();
1278 if (lru < lru_oldest && !ptf->BackgroundCompressionAsJob()) {
1284 if (!cc->m_singleChart->GetFullPath().IsSameAs(chart_full_path)) {
1285 int lru = ptf->GetLRUTime();
1286 if (lru < lru_oldest && !ptf->BackgroundCompressionAsJob()) {
1297 if (!ptf_oldest)
return false;
1299 ptf_oldest->FreeSome(g_memCacheLimit * factor * hysteresis);
1301 GetMemoryStatus(0, &mem_used);
1303 bMemCrunch = (g_memCacheLimit &&
1304 ((mem_used > (double)(g_memCacheLimit)*factor * hysteresis &&
1305 mem_used > (double)(m_prevMemUsed)*factor * hysteresis) ||
1306 (m_chart_texfactory_hash.size() > MAX_CACHE_FACTORY)));
1308 if (!bMemCrunch)
return false;
1312 m_chart_texfactory_hash.erase(
1313 ptf_oldest->GetHashKey());
1320void glTextureManager::BuildCompressedCache() {
1321 idx_sorted_by_distance.Clear();
1329 for (
int i = 0; i <
ChartData->GetChartTableEntries(); i++) {
1332 ChartTypeEnum chart_type = (ChartTypeEnum)cte.GetChartType();
1333 if (chart_type == CHART_TYPE_PLUGIN) {
1334 if (cte.GetChartFamily() != CHART_FAMILY_RASTER)
continue;
1336 if (chart_type != CHART_TYPE_KAP)
continue;
1339 wxString CompressedCacheFilePath =
1340 CompressedCachePath(
ChartData->GetDBChartFileName(i));
1341 wxFileName fn(CompressedCacheFilePath);
1345 idx_sorted_by_distance.Add(i);
1350 if (count == 0)
return;
1352 wxLogMessage(wxString::Format(
"BuildCompressedCache() count = %d", count));
1356 if (GetRunningJobCount()) {
1357 wxLogMessage(
"Starting compressor pool drain");
1358 wxDateTime now = wxDateTime::Now();
1359 time_t stall = now.GetTicks();
1360#define THREAD_WAIT_SECONDS 5
1361 time_t end = stall + THREAD_WAIT_SECONDS;
1364 while (stall < end) {
1365 wxDateTime later = wxDateTime::Now();
1366 stall = later.GetTicks();
1369 msg.Printf(
"Time: %d Job Count: %d", n_comploop, GetRunningJobCount());
1371 if (!GetRunningJobCount())
break;
1377 fmsg.Printf(
"Finished compressor pool drain..Time: %d Job Count: %d",
1378 n_comploop, GetRunningJobCount());
1381 ClearAllRasterTextures();
1382 b_inCompressAllCharts =
true;
1388 ArrayOfCompressTargets ct_array;
1389 for (
unsigned int j = 0; j < idx_sorted_by_distance.GetCount(); j++) {
1390 int i = idx_sorted_by_distance[j];
1393 double distance = chart_dist(i);
1395 wxString filename = cte.GetFullSystemPath();
1398 pct->distance = distance;
1399 pct->chart_path = filename;
1405 long style = wxPD_SMOOTH | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME |
1406 wxPD_REMAINING_TIME | wxPD_CAN_ABORT;
1416 "longgggggggggggggggggggggggggggggggggggggggggggg\ngggggggggggggggggg"
1417 "gggggggggggggggggggggggggg top line ";
1420 for (
int i = 0; i < m_max_jobs + 1; i++)
1423 m_progDialog =
new wxGenericProgressDialog();
1426 int fontSize = qFont->GetPointSize();
1428 wxSize csz = gFrame->GetClientSize();
1429 if (csz.x < 500 || csz.y < 500)
1431 10, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1435 wxFONTWEIGHT_NORMAL);
1437 m_progDialog->SetFont(*sFont);
1442 sdc.GetTextExtent(
"[WWWWWWWWWWWWWWWWWWWWWWWWWWWWWW]", &width, &height, NULL,
1444 if (width > (csz.x / 2)) m_bcompact =
true;
1446 m_progDialog->Create(_(
"OpenCPN Compressed Cache Update"), msg0, count + 1,
1450 m_progDialog->Hide();
1451 wxSize sz = m_progDialog->GetSize();
1452 sz.x = csz.x * 9 / 10;
1453 m_progDialog->SetSize(sz);
1455 m_progDialog->Layout();
1456 wxSize sza = m_progDialog->GetSize();
1458 m_progDialog->Centre();
1459 m_progDialog->Show();
1460 m_progDialog->Raise();
1466 for (m_jcnt = 0; m_jcnt < ct_array.GetCount(); m_jcnt++) {
1467 wxString filename = ct_array[m_jcnt].chart_path;
1468 wxString CompressedCacheFilePath = CompressedCachePath(filename);
1469 double distance = ct_array[m_jcnt].distance;
1476 g_glTextureManager->PurgeChartTextures(pchart,
true);
1479 if (pBSBChart == 0)
continue;
1483 m_progMsg.Printf(_(
"Distance from Ownship: %4.0f NMi"), distance);
1485 m_progMsg.Prepend(
"Preparing RNC Cache...\n");
1488 g_glTextureManager->PurgeJobList();
1494 int size_X = pBSBChart->GetSize_X();
1495 int size_Y = pBSBChart->GetSize_Y();
1497 int tex_dim = g_GLOptions.m_iTextureDimension;
1499 int nx_tex = ceil((
float)size_X / tex_dim);
1500 int ny_tex = ceil((
float)size_Y / tex_dim);
1504 rect.width = tex_dim;
1505 rect.height = tex_dim;
1506 for (
int y = 0; y < ny_tex; y++) {
1508 for (
int x = 0; x < nx_tex; x++) {
1509 for (
int level = 0; level < g_mipmap_max_level + 1; level++) {
1510 if (!tex_fact->IsLevelInCache(level, rect, global_color_scheme)) {
1514 rect.x += rect.width;
1516 rect.y += rect.height;
1526 if (!m_progDialog->Update(m_jcnt)) {
1537 ScheduleJob(tex_fact, wxRect(), 0,
false,
true,
true,
false);
1540 int cnt = GetJobCount() - GetRunningJobCount();
1546 g_glTextureManager->PurgeJobList();
1553 while (GetRunningJobCount()) {
1558 b_inCompressAllCharts =
false;
1561 delete m_progDialog;
1562 m_progDialog =
nullptr;
General chart base definitions.
ChartDB * ChartData
Global instance.
Charts database management
Generic Chart canvas base.
Base class for BSB (Maptech/NOS) format nautical charts.
Base class for all chart types.
ChartCanvas - Main chart display and interaction component.
Manages the chart database and provides access to chart data.
wxFont * FindOrCreateFont(int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underline=false, const wxString &facename=wxEmptyString, wxFontEncoding encoding=wxFONTENCODING_DEFAULT)
Creates or finds a matching font in the font cache.
wxFont * GetOCPNScaledFont(wxString item, int default_size)
Retrieves a font from FontMgr, optionally scaled for physical readability.
General purpose GUI support.
Represents an entry in the chart table, containing information about a single chart.
Runtime representation of a plugin block.