From e452a4305c850065ecc14d53032e4a505e3979dc Mon Sep 17 00:00:00 2001 From: klayoutmatthias Date: Thu, 18 Oct 2018 09:41:38 +0200 Subject: [PATCH 1/9] setup.py changes for MSVC builds - Using "Library" rather than "Extension" for building shared libs (does not try to export extension-specific symbols) - Linking against import libs - Using os.path.join consistently for backslash/slash difference --- setup.py | 195 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 118 insertions(+), 77 deletions(-) diff --git a/setup.py b/setup.py index 78cc876c2..d060f76bf 100644 --- a/setup.py +++ b/setup.py @@ -54,7 +54,8 @@ and won't find them. So we need to take away the path with "-Wl,-soname" on Linux (see Config.link_args). """ -from setuptools import setup, Extension, Distribution +from setuptools import setup, Distribution +from setuptools.extension import Extension, Library import glob import os import platform @@ -137,6 +138,13 @@ class Config(object): build_cmd = Distribution().get_command_obj('build') build_cmd.finalize_options() self.build_platlib = build_cmd.build_platlib + self.build_temp = build_cmd.build_temp + if platform.system() == "Windows": + # Windows uses separate temp build folders for debug and release + if build_cmd.debug: + self.build_temp = os.path.join(self.build_temp, "Debug") + else: + self.build_temp = os.path.join(self.build_temp, "Release") self.ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") @@ -155,18 +163,30 @@ class Config(object): ext_suffix = ext_suffix.replace('.so', '.dylib') return mod + ext_suffix - def path_of(self, mod): + def path_of(self, mod, mod_src_path): """ Returns the build path of the library for a given module """ - return os.path.join(self.build_platlib, self.root, self.libname_of(mod)) + if platform.system() == "Windows": + # On Windows, the library to link is the import library + (dll_name, dll_ext) = os.path.splitext(self.libname_of(mod)) + return os.path.join(self.build_temp, mod_src_path, dll_name + ".lib") + else: + return os.path.join(self.build_platlib, self.root, self.libname_of(mod)) def compile_args(self, mod): """ Gets additional compiler arguments """ if platform.system() == "Windows": - return [] + bits = os.getenv("KLAYOUT_BITS") + if bits: + return ["\"-I" + os.path.join(bits, "zlib", "include") + "\"", + "\"-I" + os.path.join(bits, "ptw", "include") + "\"", + "\"-I" + os.path.join(bits, "expat", "include") + "\"", + "\"-I" + os.path.join(bits, "curl", "include") + "\""] + else: + return [] elif platform.system() == "Darwin": return [] else: @@ -174,12 +194,31 @@ class Config(object): "-std=c++11", # because we use unordered_map/unordered_set ] + def libraries(self, mod): + """ + Gets the libraries to add + """ + if platform.system() == "Windows": + if mod == "_tl": + return [ "libcurl", "expat", "pthreadVCE2", "zlib", "wsock32" ] + else: + if mod == "_tl": + ['curl', 'expat'], + return [] + def link_args(self, mod): """ Gets additional linker arguments """ if platform.system() == "Windows": - return [] + args = ["/DLL"] + bits = os.getenv("KLAYOUT_BITS") + if bits: + args += ["\"/LIBPATH:" + os.path.join(bits, "zlib", "libraries") + "\"", + "\"/LIBPATH:" + os.path.join(bits, "ptw", "libraries") + "\"", + "\"/LIBPATH:" + os.path.join(bits, "expat", "libraries") + "\"", + "\"/LIBPATH:" + os.path.join(bits, "curl", "libraries") + "\""] + return args elif platform.system() == "Darwin": # For the dependency modules, make sure we produce a dylib. # We can only link against such, but the bundles produced otherwise. @@ -222,7 +261,6 @@ config = Config() # _tl dependency library _tl_path = os.path.join("src", "tl", "tl") - _tl_sources = set(glob.glob(os.path.join(_tl_path, "*.cc"))) # Exclude sources which are compatible with Qt only @@ -233,41 +271,43 @@ _tl_sources.discard(os.path.join(_tl_path, "tlHttpStreamNoQt.cc")) _tl_sources.discard(os.path.join(_tl_path, "tlFileSystemWatcher.cc")) _tl_sources.discard(os.path.join(_tl_path, "tlDeferredExecutionQt.cc")) -_tl = Extension(config.root + '._tl', - define_macros=config.macros() + [('MAKE_TL_LIBRARY', 1)], - language='c++', - libraries=['curl', 'expat'], - extra_link_args=config.link_args('_tl'), - extra_compile_args=config.compile_args('_tl'), - sources=list(_tl_sources)) +_tl = Library(config.root + '._tl', + define_macros=config.macros() + [('MAKE_TL_LIBRARY', 1)], + language='c++', + libraries=config.libraries('_tl'), + extra_link_args=config.link_args('_tl'), + extra_compile_args=config.compile_args('_tl'), + sources=list(_tl_sources)) # ------------------------------------------------------------------ # _gsi dependency library -_gsi_sources = glob.glob("src/gsi/gsi/*.cc") +_gsi_path = os.path.join("src", "gsi", "gsi") +_gsi_sources = set(glob.glob(os.path.join(_gsi_path, "*.cc"))) -_gsi = Extension(config.root + '._gsi', - define_macros=config.macros() + [('MAKE_GSI_LIBRARY', 1)], - include_dirs=['src/tl/tl'], - extra_objects=[config.path_of('_tl')], - language='c++', - extra_link_args=config.link_args('_gsi'), - extra_compile_args=config.compile_args('_gsi'), - sources=_gsi_sources) +_gsi = Library(config.root + '._gsi', + define_macros=config.macros() + [('MAKE_GSI_LIBRARY', 1)], + include_dirs=[_tl_path], + extra_objects=[config.path_of('_tl', _tl_path)], + language='c++', + extra_link_args=config.link_args('_gsi'), + extra_compile_args=config.compile_args('_gsi'), + sources=list(_gsi_sources)) # ------------------------------------------------------------------ # _pya dependency library -_pya_sources = glob.glob("src/pya/pya/*.cc") +_pya_path = os.path.join("src", "pya", "pya") +_pya_sources = set(glob.glob(os.path.join(_pya_path, "*.cc"))) -_pya = Extension(config.root + '._pya', - define_macros=config.macros() + [('MAKE_PYA_LIBRARY', 1)], - include_dirs=['src/tl/tl', 'src/gsi/gsi'], - extra_objects=[config.path_of('_tl'), config.path_of('_gsi')], - language='c++', - extra_link_args=config.link_args('_pya'), - extra_compile_args=config.compile_args('_pya'), - sources=_pya_sources) +_pya = Library(config.root + '._pya', + define_macros=config.macros() + [('MAKE_PYA_LIBRARY', 1)], + include_dirs=[_tl_path, _gsi_path], + extra_objects=[config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path)], + language='c++', + extra_link_args=config.link_args('_pya'), + extra_compile_args=config.compile_args('_pya'), + sources=list(_pya_sources)) # ------------------------------------------------------------------ # _db dependency library @@ -280,93 +320,94 @@ _db_sources = set(glob.glob(os.path.join(_db_path, "*.cc"))) # not exist. So we need an error-free discard method instead of list's remove. _db_sources.discard(os.path.join(_db_path, "fonts.cc")) -_db = Extension(config.root + '._db', - define_macros=config.macros() + [('MAKE_DB_LIBRARY', 1)], - include_dirs=['src/tl/tl', 'src/gsi/gsi', 'src/db/db'], - extra_objects=[config.path_of('_tl'), config.path_of('_gsi')], - language='c++', - extra_link_args=config.link_args('_db'), - extra_compile_args=config.compile_args('_db'), - sources=list(_db_sources)) +_db = Library(config.root + '._db', + define_macros=config.macros() + [('MAKE_DB_LIBRARY', 1)], + include_dirs=[_tl_path, _gsi_path, _db_path], + extra_objects=[config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path)], + language='c++', + extra_link_args=config.link_args('_db'), + extra_compile_args=config.compile_args('_db'), + sources=list(_db_sources)) # ------------------------------------------------------------------ # _rdb dependency library -_rdb_sources = glob.glob("src/rdb/rdb/*.cc") +_rdb_path = os.path.join("src", "rdb", "rdb") +_rdb_sources = set(glob.glob(os.path.join(_rdb_path, "*.cc"))) -_rdb = Extension(config.root + '._rdb', - define_macros=config.macros() + [('MAKE_RDB_LIBRARY', 1)], - include_dirs=['src/db/db', 'src/tl/tl', 'src/gsi/gsi'], - extra_objects=[config.path_of('_tl'), config.path_of( - '_gsi'), config.path_of('_db')], - language='c++', - extra_link_args=config.link_args('_rdb'), - extra_compile_args=config.compile_args('_rdb'), - sources=_rdb_sources) +_rdb = Library(config.root + '._rdb', + define_macros=config.macros() + [('MAKE_RDB_LIBRARY', 1)], + include_dirs=[_db_path, _tl_path, _gsi_path], + extra_objects=[config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_db', _db_path)], + language='c++', + extra_link_args=config.link_args('_rdb'), + extra_compile_args=config.compile_args('_rdb'), + sources=list(_rdb_sources)) # ------------------------------------------------------------------ # dependency libraries from db_plugins db_plugins = [] -for pi in glob.glob("src/plugins/*/db_plugin") + glob.glob("src/plugins/*/*/db_plugin"): +dbpi_dirs = glob.glob(os.path.join("src", "plugins", "*", "db_plugin")) +dbpi_dirs += glob.glob(os.path.join("src", "plugins", "*", "*", "db_plugin")) + +for pi in dbpi_dirs: mod_name = "_" + os.path.split(os.path.split(pi)[-2])[-1] + "_dbpi" - pi_sources = glob.glob(pi + "/*.cc") + pi_sources = glob.glob(os.path.join(pi, "*.cc")) - pi_ext = Extension(config.root + '.db_plugins.' + mod_name, - define_macros=config.macros() + [('MAKE_DB_PLUGIN_LIBRARY', 1)], - include_dirs=['src/plugins/common', - 'src/db/db', 'src/tl/tl', 'src/gsi/gsi'], - extra_objects=[config.path_of('_tl'), config.path_of( - '_gsi'), config.path_of('_db')], - language='c++', - extra_link_args=config.link_args(mod_name), - extra_compile_args=config.compile_args(mod_name), - sources=pi_sources) + pi_ext = Library(config.root + '.db_plugins.' + mod_name, + define_macros=config.macros() + [('MAKE_DB_PLUGIN_LIBRARY', 1)], + include_dirs=[os.path.join("src", "plugins", "common"), + _db_path, _tl_path, _gsi_path], + extra_objects=[config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_db', _db_path)], + language='c++', + extra_link_args=config.link_args(mod_name), + extra_compile_args=config.compile_args(mod_name), + sources=pi_sources) db_plugins.append(pi_ext) # ------------------------------------------------------------------ # tl extension library -tl_sources = glob.glob("src/pymod/tl/*.cc") +tl_path = os.path.join("src", "pymod", "tl") +tl_sources = set(glob.glob(os.path.join(tl_path, "*.cc"))) tl = Extension(config.root + '.tl', define_macros=config.macros(), - include_dirs=['src/tl/tl', 'src/gsi/gsi', 'src/pya/pya'], - extra_objects=[config.path_of('_tl'), config.path_of( - '_gsi'), config.path_of('_pya')], + include_dirs=[_tl_path, _gsi_path, _pya_path], + extra_objects=[config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_pya', _pya_path)], extra_link_args=config.link_args('tl'), - sources=tl_sources) + sources=list(tl_sources)) # ------------------------------------------------------------------ # db extension library -db_sources = glob.glob("src/pymod/db/*.cc") +db_path = os.path.join("src", "pymod", "db") +db_sources = set(glob.glob(os.path.join(db_path, "*.cc"))) db = Extension(config.root + '.db', define_macros=config.macros(), - include_dirs=['src/db/db', 'src/tl/tl', 'src/gsi/gsi', 'src/pya/pya'], - extra_objects=[config.path_of('_db'), config.path_of( - '_tl'), config.path_of('_gsi'), config.path_of('_pya')], + include_dirs=[_db_path, _tl_path, _gsi_path, _pya_path], + extra_objects=[config.path_of('_db', _db_path), config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_pya', _pya_path)], extra_link_args=config.link_args('db'), - sources=db_sources) + sources=list(db_sources)) # ------------------------------------------------------------------ # rdb extension library -rdb_sources = glob.glob("src/pymod/rdb/*.cc") +rdb_path = os.path.join("src", "pymod", "rdb") +rdb_sources = set(glob.glob(os.path.join(rdb_path, "*.cc"))) rdb = Extension(config.root + '.rdb', define_macros=config.macros(), - include_dirs=['src/rdb/rdb', 'src/db/db', - 'src/tl/tl', 'src/gsi/gsi', 'src/pya/pya'], - extra_objects=[config.path_of('_rdb'), config.path_of( - '_gsi'), config.path_of('_pya')], + include_dirs=[_rdb_path, _db_path, _tl_path, _gsi_path, _pya_path], + extra_objects=[config.path_of('_rdb', _rdb_path), config.path_of('_tl', _tl_path), config.path_of('_gsi', _gsi_path), config.path_of('_pya', _pya_path)], extra_link_args=config.link_args('rdb'), - sources=rdb_sources) + sources=list(rdb_sources)) # ------------------------------------------------------------------ # Core setup function From a57b855e12bd4310610324151f6fba29390b0fca Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 18 Oct 2018 23:29:51 +0200 Subject: [PATCH 2/9] Updated version to check CD --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d060f76bf..53beff5eb 100644 --- a/setup.py +++ b/setup.py @@ -252,7 +252,7 @@ class Config(object): """ Gets the version string """ - return "0.26.0.dev4" + return "0.26.0.dev5" config = Config() From 46116f4828d513c9caea425cd3eb45c1175ca9a3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 29 Sep 2018 23:35:30 +0200 Subject: [PATCH 3/9] Basic performance improvement in the bitmap to image area Empty bitmaps are skipped now --- src/laybasic/laybasic/layBitmapsToImage.cc | 97 +++++++++++++--------- 1 file changed, 56 insertions(+), 41 deletions(-) diff --git a/src/laybasic/laybasic/layBitmapsToImage.cc b/src/laybasic/laybasic/layBitmapsToImage.cc index 57e547e4b..2c0ca533b 100644 --- a/src/laybasic/laybasic/layBitmapsToImage.cc +++ b/src/laybasic/laybasic/layBitmapsToImage.cc @@ -386,13 +386,18 @@ render_scanline_cross (const uint32_t *dp, unsigned int ds, const lay::Bitmap *p } } -static void create_precursor_bitmaps (const std::vector &view_ops_in, const std::vector &pbitmaps_in, const lay::LineStyles &ls, unsigned int width, unsigned int height, std::map &precursors, QMutex *mutex) +static void create_precursor_bitmaps (const std::vector &view_ops_in, const std::vector &vo_map, const std::vector &pbitmaps_in, const std::vector &bm_map, const lay::LineStyles &ls, unsigned int width, unsigned int height, std::map &precursors, QMutex *mutex) { + tl_assert (bm_map.size () == vo_map.size ()); + // Styled lines with width > 1 are not rendered directly, but through an intermediate step. // We prepare the necessary precursor bitmaps now - for (unsigned int i = 0; i < view_ops_in.size (); ++i) { + for (unsigned int i = 0; i < vo_map.size (); ++i) { - const ViewOp &op = view_ops_in [i]; + unsigned int vo_index = vo_map [i]; + unsigned int bm_index = bm_map [i]; + + const ViewOp &op = view_ops_in [vo_index]; if (op.width () > 1 && ls.style (op.line_style_index ()).width () > 0) { // lock bitmaps against change by the redraw thread @@ -400,12 +405,12 @@ static void create_precursor_bitmaps (const std::vector &view_ops_i mutex->lock (); } - lay::Bitmap &bp = precursors.insert (std::make_pair (i, lay::Bitmap (width, height, 1.0))).first->second; + lay::Bitmap &bp = precursors.insert (std::make_pair (bm_index, lay::Bitmap (width, height, 1.0))).first->second; LineStyleInfo ls_info = ls.style (op.line_style_index ()); ls_info.scale_pattern (op.width ()); for (unsigned int y = 0; y < height; y++) { - render_scanline_std_edge (ls_info.pattern (), ls_info.pattern_stride (), pbitmaps_in [i], y, width, height, bp.scanline (y)); + render_scanline_std_edge (ls_info.pattern (), ls_info.pattern_stride (), pbitmaps_in [bm_index], y, width, height, bp.scanline (y)); } if (mutex) { @@ -418,7 +423,7 @@ static void create_precursor_bitmaps (const std::vector &view_ops_i } static void -bitmaps_to_image_rgb (const std::vector &view_ops_in, +bitmaps_to_image_rgb (const std::vector &view_ops_in, const std::vector &pbitmaps_in, const lay::DitherPattern &dp, const lay::LineStyles &ls, @@ -427,29 +432,33 @@ bitmaps_to_image_rgb (const std::vector &view_ops_in, bool transparent, QMutex *mutex) { - unsigned int n_in = (unsigned int) view_ops_in.size (); - std::vector bm_map; std::vector vo_map; - vo_map.reserve (n_in); - for (unsigned int i = 0; i < n_in; ++i) { - vo_map.push_back (i); - } + vo_map.reserve (view_ops_in.size ()); + bm_map.reserve (view_ops_in.size ()); + unsigned int n_in = 0; - if (! use_bitmap_index) { - bm_map = vo_map; - } else { - bm_map.reserve (n_in); - for (unsigned int i = 0; i < n_in; ++i) { - bm_map.push_back (view_ops_in [i].bitmap_index () < 0 ? i : view_ops_in [i].bitmap_index ()); + // drop invisible and empty bitmaps, build bitmap mask + for (unsigned int i = 0; i < view_ops_in.size (); ++i) { + + const lay::ViewOp &vop = view_ops_in [i]; + + unsigned int bi = (use_bitmap_index && vop.bitmap_index () >= 0) ? (unsigned int) vop.bitmap_index () : i; + const lay::Bitmap *pb = bi < pbitmaps_in.size () ? pbitmaps_in [bi] : 0; + + if ((vop.ormask () | ~vop.andmask ()) != 0 && pb && ! pb->empty ()) { + vo_map.push_back (i); + bm_map.push_back (bi); + ++n_in; } + } // Styled lines with width > 1 are not rendered directly, but through an intermediate step. // We prepare the necessary precursor bitmaps now std::map precursors; - create_precursor_bitmaps (view_ops_in, pbitmaps_in, ls, width, height, precursors, mutex); + create_precursor_bitmaps (view_ops_in, vo_map, pbitmaps_in, bm_map, ls, width, height, precursors, mutex); std::vector view_ops; std::vector pbitmaps; @@ -489,18 +498,19 @@ bitmaps_to_image_rgb (const std::vector &view_ops_in, unsigned int w = vop.width (); const lay::Bitmap *pb = 0; + unsigned int bm_index = bm_map[i]; if (bm_map [i] < pbitmaps_in.size ()) { if (w > 1 && ls.style (vop.line_style_index ()).width () > 0) { - tl_assert (precursors.find (i) != precursors.end ()); - pb = &precursors [i]; + tl_assert (precursors.find (bm_index) != precursors.end ()); + pb = &precursors [bm_index]; } else { - pb = pbitmaps_in [bm_map[i]]; + pb = pbitmaps_in [bm_index]; } } if (pb != 0 && w > 0 - && ((pb->first_scanline () < y + slice && pb->last_scanline () > y) || w > 1) + && (pb->first_scanline () + w - 1 < y + slice && pb->last_scanline () > y + w - 1) && (vop.ormask () | ~vop.andmask ()) != 0) { uint32_t non_empty_sl = 0; @@ -660,29 +670,33 @@ bitmaps_to_image_mono (const std::vector &view_ops_in, bool use_bitmap_index, QMutex *mutex) { - unsigned int n_in = (unsigned int) view_ops_in.size (); - std::vector bm_map; std::vector vo_map; - vo_map.reserve (n_in); - for (unsigned int i = 0; i < n_in; ++i) { - vo_map.push_back (i); - } + vo_map.reserve (view_ops_in.size ()); + bm_map.reserve (view_ops_in.size ()); + unsigned int n_in = 0; - if (! use_bitmap_index) { - bm_map = vo_map; - } else { - bm_map.reserve (n_in); - for (unsigned int i = 0; i < n_in; ++i) { - bm_map.push_back (view_ops_in [i].bitmap_index () < 0 ? i : view_ops_in [i].bitmap_index ()); + // drop invisible and empty bitmaps, build bitmap mask + for (unsigned int i = 0; i < view_ops_in.size (); ++i) { + + const lay::ViewOp &vop = view_ops_in [i]; + + unsigned int bi = (use_bitmap_index && vop.bitmap_index () >= 0) ? (unsigned int) vop.bitmap_index () : i; + const lay::Bitmap *pb = bi < pbitmaps_in.size () ? pbitmaps_in [bi] : 0; + + if ((vop.ormask () | ~vop.andmask ()) != 0 && pb && ! pb->empty ()) { + vo_map.push_back (i); + bm_map.push_back (bi); + ++n_in; } + } // Styled lines with width > 1 are not rendered directly, but through an intermediate step. // We prepare the necessary precursor bitmaps now std::map precursors; - create_precursor_bitmaps (view_ops_in, pbitmaps_in, ls, width, height, precursors, mutex); + create_precursor_bitmaps (view_ops_in, vo_map, pbitmaps_in, bm_map, ls, width, height, precursors, mutex); std::vector view_ops; std::vector pbitmaps; @@ -722,18 +736,19 @@ bitmaps_to_image_mono (const std::vector &view_ops_in, unsigned int w = vop.width (); const lay::Bitmap *pb = 0; + unsigned int bm_index = bm_map[i]; if (bm_map [i] < pbitmaps_in.size ()) { if (w > 1 && ls.style (vop.line_style_index ()).width () > 0) { - tl_assert (precursors.find (i) != precursors.end ()); - pb = &precursors [i]; + tl_assert (precursors.find (bm_index) != precursors.end ()); + pb = &precursors [bm_index]; } else { - pb = pbitmaps_in [bm_map[i]]; + pb = pbitmaps_in [bm_index]; } } - if (pb != 0 + if (pb != 0 && w > 0 - && ((pb->first_scanline () < y + slice && pb->last_scanline () > y) || w > 1) + && (pb->first_scanline () + w - 1 < y + slice && pb->last_scanline () > y + w - 1) && (vop.ormask () | ~vop.andmask ()) != 0) { uint32_t non_empty_sl = 0; From dc9267d61e9d5734efb5b14f1b73cdc65113abc4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Sep 2018 00:16:19 +0200 Subject: [PATCH 4/9] WIP: updated solution * Reverted first solution partially because it lead to drawing errors. * Redraw thread will fire workers only for layers that really need to be drawn --- src/laybasic/laybasic/layBitmapsToImage.cc | 4 ++-- src/laybasic/laybasic/layRedrawLayerInfo.h | 15 ++++++++++++--- src/laybasic/laybasic/layRedrawThread.cc | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/laybasic/laybasic/layBitmapsToImage.cc b/src/laybasic/laybasic/layBitmapsToImage.cc index 2c0ca533b..dbd8bbc64 100644 --- a/src/laybasic/laybasic/layBitmapsToImage.cc +++ b/src/laybasic/laybasic/layBitmapsToImage.cc @@ -510,7 +510,7 @@ bitmaps_to_image_rgb (const std::vector &view_ops_in, if (pb != 0 && w > 0 - && (pb->first_scanline () + w - 1 < y + slice && pb->last_scanline () > y + w - 1) + && ((pb->first_scanline () < y + slice && pb->last_scanline () > y) || w > 1) && (vop.ormask () | ~vop.andmask ()) != 0) { uint32_t non_empty_sl = 0; @@ -748,7 +748,7 @@ bitmaps_to_image_mono (const std::vector &view_ops_in, if (pb != 0 && w > 0 - && (pb->first_scanline () + w - 1 < y + slice && pb->last_scanline () > y + w - 1) + && ((pb->first_scanline () < y + slice && pb->last_scanline () > y) || w > 1) && (vop.ormask () | ~vop.andmask ()) != 0) { uint32_t non_empty_sl = 0; diff --git a/src/laybasic/laybasic/layRedrawLayerInfo.h b/src/laybasic/laybasic/layRedrawLayerInfo.h index 3befe4c33..60865b241 100644 --- a/src/laybasic/laybasic/layRedrawLayerInfo.h +++ b/src/laybasic/laybasic/layRedrawLayerInfo.h @@ -74,9 +74,10 @@ struct RedrawLayerInfo /** * @brief The layer index * - * The logical layer to draw. If this member is <0 and the cellview_index is <0, it is an invalid layer, - * which is ignored. If the cellview_index is >=0, it denotes a "cell frame" pseudo - * layer. It is set by the constructor. + * The logical layer to draw. The layer index can be <0 which indicates a + * layer with not layout source (cell_frame may be true to indicate a + * pseudo layer then). + * This attribute is set by the constructor. */ int layer_index; @@ -115,6 +116,14 @@ struct RedrawLayerInfo * This member is set by the constructor. */ bool inverse_prop_sel; + + /** + * @brief Returns true, if the layer needs to be drawn + */ + bool needs_drawing () const + { + return visible && enabled && (cell_frame || layer_index >= 0) && cellview_index >= 0; + } }; } diff --git a/src/laybasic/laybasic/layRedrawThread.cc b/src/laybasic/laybasic/layRedrawThread.cc index f71f15e00..757692ada 100644 --- a/src/laybasic/laybasic/layRedrawThread.cc +++ b/src/laybasic/laybasic/layRedrawThread.cc @@ -307,7 +307,7 @@ RedrawThread::do_start (bool clear, const db::Vector *shift_vector, const std::v } for (int i = 0; i < m_nlayers; ++i) { - if (m_layers [i].visible && m_layers [i].enabled) { + if (m_layers [i].needs_drawing ()) { schedule (new RedrawThreadTask (i)); } } From d67a50bd1d8c280c5473dce7488164224ff2c873 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Sep 2018 10:27:44 +0200 Subject: [PATCH 5/9] Fixed #176 by introducing a separate bitmap for text drawing optimization. --- .../laybasic/layRedrawThreadWorker.cc | 26 +++++++++++++------ src/laybasic/laybasic/layRedrawThreadWorker.h | 2 +- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.cc b/src/laybasic/laybasic/layRedrawThreadWorker.cc index a7d84268a..daabbdac3 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.cc +++ b/src/laybasic/laybasic/layRedrawThreadWorker.cc @@ -1232,20 +1232,26 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c vertex = m_planes[3 + plane_group * (planes_per_layer / 3)]; // do not draw, if there is nothing to draw - if (mp_layout->cells () <= ci || vp.empty ()) { + if (mp_layout->cells () <= ci || vp.empty () || mp_layout->cell (ci).bbox (m_layer).empty ()) { return; } if (cell_var_cached (ci, trans)) { return; } + std::auto_ptr opt_bitmap; + lay::Bitmap *vertex_bitmap = dynamic_cast (vertex); + if (m_text_lazy_rendering && vertex_bitmap) { + opt_bitmap.reset (new lay::Bitmap (vertex_bitmap->width (), vertex_bitmap->height (), vertex_bitmap->resolution ())); + } + for (std::vector::const_iterator b = vp.begin (); b != vp.end (); ++b) { - draw_text_layer (drawing_context, ci, trans, *b, level, fill, frame, vertex, text); + draw_text_layer (drawing_context, ci, trans, *b, level, fill, frame, vertex, text, opt_bitmap.get ()); } } void -RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, CanvasPlane *fill, CanvasPlane *frame, CanvasPlane *vertex, CanvasPlane *text) +RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &vp, int level, CanvasPlane *fill, CanvasPlane *frame, CanvasPlane *vertex, CanvasPlane *text, lay::Bitmap *opt_bitmap) { test_snapshot (0); @@ -1283,6 +1289,9 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c // paint the simplified box if (anything) { r.draw (trans * bbox, 0, frame, vertex, 0); + if (opt_bitmap) { + r.draw (trans * bbox, 0, 0, opt_bitmap, 0); + } } // do not dive further into hierarchy @@ -1318,6 +1327,9 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c test_snapshot (0); r.draw (*shape, trans, fill, frame, vertex, text); + if (opt_bitmap) { + r.draw (*shape, trans, 0, 0, opt_bitmap, 0); + } ++shape; --ntexts; @@ -1355,8 +1367,6 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c // dive down into the hierarchy .. if (need_to_dive) { - const lay::Bitmap *vertex_bitmap = dynamic_cast (vertex); - // create a set of boxes to look into std::vector vv = search_regions (cell_bbox, vp, level); @@ -1377,7 +1387,7 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c bool skip = false; if (m_text_lazy_rendering && qid != current_quad_id) { current_quad_id = qid; - skip = vertex_bitmap && skip_quad (inst.quad_box () & bbox, vertex_bitmap, trans); + skip = opt_bitmap && skip_quad (inst.quad_box () & bbox, opt_bitmap, trans); } if (skip) { @@ -1448,7 +1458,7 @@ RedrawThreadWorker::draw_text_layer (bool drawing_context, db::cell_index_type c db::ICplxTrans t (cell_inst.complex_trans (*p)); db::Box new_vp = db::Box (t.inverted () * *v); - draw_text_layer (drawing_context, new_ci, trans * t, new_vp, level + 1, fill, frame, vertex, text); + draw_text_layer (drawing_context, new_ci, trans * t, new_vp, level + 1, fill, frame, vertex, text, opt_bitmap); } @@ -1648,7 +1658,7 @@ RedrawThreadWorker::draw_layer_wo_cache (int from_level, int to_level, db::cell_ // skip this quad if we have drawn something here already size_t qid = inst.quad_id (); bool skip = false; - if (m_text_lazy_rendering && qid != current_quad_id) { + if (qid != current_quad_id) { current_quad_id = qid; skip = skip_quad (inst.quad_box () & bbox, vertex_bitmap, trans); } diff --git a/src/laybasic/laybasic/layRedrawThreadWorker.h b/src/laybasic/laybasic/layRedrawThreadWorker.h index 0b49a44d3..ea2a90a11 100644 --- a/src/laybasic/laybasic/layRedrawThreadWorker.h +++ b/src/laybasic/laybasic/layRedrawThreadWorker.h @@ -180,7 +180,7 @@ private: void draw_layer (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_box, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot); void draw_layer_wo_cache (int from_level, int to_level, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &vv, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, const UpdateSnapshotCallback *update_snapshot); void draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); - void draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text); + void draw_text_layer (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level, lay::CanvasPlane *fill, lay::CanvasPlane *frame, lay::CanvasPlane *vertex, lay::CanvasPlane *text, Bitmap *opt_bitmap); void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); void draw_boxes (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const db::Box &redraw_region, int level); void draw_box_properties (bool drawing_context, db::cell_index_type ci, const db::CplxTrans &trans, const std::vector &redraw_regions, int level); From d4636803e610827a7c9b71bdc65ee49277f36528 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 20 Oct 2018 09:56:45 +0200 Subject: [PATCH 6/9] Fixed typo in Changelog --- Changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog b/Changelog index 1f0dbe239..138367007 100644 --- a/Changelog +++ b/Changelog @@ -15,7 +15,7 @@ DEF reader did not pull vias from LEF * Bugfix: https://github.com/klayoutmatthias/klayout/issues/174 Performance issue with many layers with width >1 -* Bugfix: https://github.com/klayoutmatthias/klayout/issues/175 +* Bugfix: https://github.com/klayoutmatthias/klayout/issues/176 Painting issue with texts * Bugfix: https://github.com/klayoutmatthias/klayout/issues/185 Hash values available as __hash__ standard method now From 79871f6bab032b052fdf90c60ae9156a5d18b4b2 Mon Sep 17 00:00:00 2001 From: klayoutmatthias Date: Sat, 20 Oct 2018 20:44:56 +0000 Subject: [PATCH 7/9] Fixed setup.py for Linux (and maybe for MacOS too) - Added "lib" prefix for libraries - Forced setuptools to produce shared objects for the libraries - otherwise it will produce static libs --- setup.py | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 53beff5eb..f0fedbec3 100644 --- a/setup.py +++ b/setup.py @@ -62,6 +62,7 @@ import platform import distutils.sysconfig as sysconfig from distutils.errors import CompileError import distutils.command.build_ext +import setuptools.command.build_ext import multiprocessing N_cores = multiprocessing.cpu_count() @@ -122,6 +123,26 @@ def patched_get_ext_filename(self, ext_name): distutils.command.build_ext.build_ext.get_ext_filename = patched_get_ext_filename + +# despite it's name, setuptools.command.build_ext.link_shared_object won't +# link a shared object on Linux, but a static library and patches distutils +# for this ... We're patching this back now. + +def always_link_shared_object( + self, objects, output_libname, output_dir=None, libraries=None, + library_dirs=None, runtime_library_dirs=None, export_symbols=None, + debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, + target_lang=None): + self.link( + self.SHARED_LIBRARY, objects, output_libname, + output_dir, libraries, library_dirs, runtime_library_dirs, + export_symbols, debug, extra_preargs, extra_postargs, + build_temp, target_lang + ) + +setuptools.command.build_ext.libtype = "shared" +setuptools.command.build_ext.link_shared_object = always_link_shared_object + # ---------------------------------------------------------------------------------------- @@ -159,9 +180,17 @@ class Config(object): The library name is usually decorated (i.e. "tl" -> "tl.cpython-35m-x86_64-linux-gnu.so"). """ ext_suffix = self.ext_suffix - if platform.system() == "Darwin" and '_dbpi' in mod: - ext_suffix = ext_suffix.replace('.so', '.dylib') - return mod + ext_suffix + if platform.system() == "Windows": + return mod + ext_suffix + else: + if platform.system() == "Darwin" and '_dbpi' in mod: + ext_suffix = ext_suffix.replace('.so', '.dylib') + if mod[0] == '_': + # is a library, not an extension module and setuptools + # will add the "lib" suffix + return "lib" + mod + ext_suffix + else: + return mod + ext_suffix def path_of(self, mod, mod_src_path): """ @@ -203,7 +232,7 @@ class Config(object): return [ "libcurl", "expat", "pthreadVCE2", "zlib", "wsock32" ] else: if mod == "_tl": - ['curl', 'expat'], + return ['curl', 'expat'] return [] def link_args(self, mod): @@ -252,7 +281,7 @@ class Config(object): """ Gets the version string """ - return "0.26.0.dev5" + return "0.26.0.dev6" config = Config() From a0c3b095a2d923ac14bb2555deb33587866c79a5 Mon Sep 17 00:00:00 2001 From: Thomas Ferreira de Lima Date: Sat, 20 Oct 2018 17:21:00 -0400 Subject: [PATCH 8/9] fixing dbpi library names --- setup.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index f0fedbec3..45942e0cc 100644 --- a/setup.py +++ b/setup.py @@ -106,6 +106,8 @@ if sys.version_info[0] * 10 + sys.version_info[1] > 26: distutils.ccompiler.CCompiler.compile = parallelCCompile +# TODO: delete (Obsolete) +# patch get_ext_filename from distutils.command.build_ext import build_ext _old_get_ext_filename = build_ext.get_ext_filename @@ -124,8 +126,32 @@ def patched_get_ext_filename(self, ext_name): distutils.command.build_ext.build_ext.get_ext_filename = patched_get_ext_filename -# despite it's name, setuptools.command.build_ext.link_shared_object won't -# link a shared object on Linux, but a static library and patches distutils +# end patch get_ext_filename + + +# patch CCompiler's library_filename for _dbpi libraries (SOs) +# Its default is to have .so for shared objects, instead of dylib, +# hence the patch + +from distutils.ccompiler import CCompiler +old_library_filename = CCompiler.library_filename + + +def patched_library_filename(self, libname, lib_type='static', # or 'shared' + strip_dir=0, output_dir=''): + if platform.system() == "Darwin" and '_dbpi' in libname: + lib_type = 'dylib' + return old_library_filename(self, libname, lib_type=lib_type, + strip_dir=strip_dir, output_dir=output_dir) + + +distutils.ccompiler.CCompiler.library_filename = patched_library_filename + +# end patch CCompiler's library_filename for _dbpi libraries (SOs) + + +# despite its name, setuptools.command.build_ext.link_shared_object won't +# link a shared object on Linux, but a static library and patches distutils # for this ... We're patching this back now. def always_link_shared_object( From ecc90ab4db6dfef88947b5f463ef03cdd2dd60d2 Mon Sep 17 00:00:00 2001 From: Thomas Ferreira de Lima Date: Sat, 20 Oct 2018 23:46:12 -0400 Subject: [PATCH 9/9] attempt to solve the libname_of computation for all platforms --- setup.py | 54 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/setup.py b/setup.py index 45942e0cc..eebfebf74 100644 --- a/setup.py +++ b/setup.py @@ -193,30 +193,43 @@ class Config(object): else: self.build_temp = os.path.join(self.build_temp, "Release") - self.ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") + build_ext_cmd = Distribution().get_command_obj('build_ext') - if self.ext_suffix is None: - self.ext_suffix = ".so" + build_ext_cmd.initialize_options() + build_ext_cmd.setup_shlib_compiler() + self.build_ext_cmd = build_ext_cmd self.root = "klayout" - def libname_of(self, mod): + def add_extension(self, ext): + self.build_ext_cmd.ext_map[ext.name] = ext + + def libname_of(self, mod, is_lib=None): """ Returns the library name for a given module The library name is usually decorated (i.e. "tl" -> "tl.cpython-35m-x86_64-linux-gnu.so"). + This code was extracted from the source in setuptools.command.build_ext.build_ext """ - ext_suffix = self.ext_suffix - if platform.system() == "Windows": - return mod + ext_suffix + libtype = setuptools.command.build_ext.libtype + full_mod = self.root + '.' + mod + if is_lib is True: + # Here we are guessing it is a library and that the filename + # is to be computed by shlib_compiler. + # See source code of setuptools.command.build_ext.build_ext.get_ext_filename + filename = self.build_ext_cmd.get_ext_filename(full_mod) + fn, ext = os.path.splitext(filename) + ext_path = self.build_ext_cmd.shlib_compiler.library_filename(fn, libtype) else: - if platform.system() == "Darwin" and '_dbpi' in mod: - ext_suffix = ext_suffix.replace('.so', '.dylib') - if mod[0] == '_': - # is a library, not an extension module and setuptools - # will add the "lib" suffix - return "lib" + mod + ext_suffix - else: - return mod + ext_suffix + # This assumes that the Extension/Library object was added to the + # ext_map dictionary via config.add_extension. + assert full_mod in self.build_ext_cmd.ext_map + ext_path = self.build_ext_cmd.get_ext_filename(full_mod) + ext_filename = os.path.basename(ext_path) + + # Exception for database plugins, which will always be dylib. + if platform.system() == "Darwin" and '_dbpi' in mod: + ext_filename = ext_filename.replace('.so', '.dylib') + return ext_filename def path_of(self, mod, mod_src_path): """ @@ -279,7 +292,7 @@ class Config(object): # We can only link against such, but the bundles produced otherwise. args = [] if mod[0] == "_": - args += ["-Wl,-dylib", '-Wl,-install_name,@rpath/%s' % self.libname_of(mod)] + args += ["-Wl,-dylib", '-Wl,-install_name,@rpath/%s' % self.libname_of(mod, is_lib=True)] args += ['-Wl,-rpath,@loader_path/'] return args else: @@ -289,7 +302,7 @@ class Config(object): # will look for the path-qualified library. But that's the # build path and the loader will fail. args = [] - args += ['-Wl,-soname,' + self.libname_of(mod)] + args += ['-Wl,-soname,' + self.libname_of(mod, is_lib=True)] if '_dbpi' not in mod: loader_path = '$ORIGIN' else: @@ -334,6 +347,8 @@ _tl = Library(config.root + '._tl', extra_compile_args=config.compile_args('_tl'), sources=list(_tl_sources)) +config.add_extension(_tl) + # ------------------------------------------------------------------ # _gsi dependency library @@ -348,6 +363,7 @@ _gsi = Library(config.root + '._gsi', extra_link_args=config.link_args('_gsi'), extra_compile_args=config.compile_args('_gsi'), sources=list(_gsi_sources)) +config.add_extension(_gsi) # ------------------------------------------------------------------ # _pya dependency library @@ -363,6 +379,7 @@ _pya = Library(config.root + '._pya', extra_link_args=config.link_args('_pya'), extra_compile_args=config.compile_args('_pya'), sources=list(_pya_sources)) +config.add_extension(_pya) # ------------------------------------------------------------------ # _db dependency library @@ -383,6 +400,7 @@ _db = Library(config.root + '._db', extra_link_args=config.link_args('_db'), extra_compile_args=config.compile_args('_db'), sources=list(_db_sources)) +config.add_extension(_db) # ------------------------------------------------------------------ # _rdb dependency library @@ -398,6 +416,7 @@ _rdb = Library(config.root + '._rdb', extra_link_args=config.link_args('_rdb'), extra_compile_args=config.compile_args('_rdb'), sources=list(_rdb_sources)) +config.add_extension(_rdb) # ------------------------------------------------------------------ # dependency libraries from db_plugins @@ -424,6 +443,7 @@ for pi in dbpi_dirs: sources=pi_sources) db_plugins.append(pi_ext) + config.add_extension(pi_ext) # ------------------------------------------------------------------ # tl extension library