Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(127)

Side by Side Diff: gm/rebaseline_server/compare_rendered_pictures.py

Issue 443013002: rebaseline_server: add "prefetch" directive that just warms the cache without awaiting results (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: download all images, unless downloadOnlyDifferingImages=true Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | gm/rebaseline_server/imagediffdb.py » ('j') | gm/rebaseline_server/imagediffdb.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 """ 3 """
4 Copyright 2014 Google Inc. 4 Copyright 2014 Google Inc.
5 5
6 Use of this source code is governed by a BSD-style license that can be 6 Use of this source code is governed by a BSD-style license that can be
7 found in the LICENSE file. 7 found in the LICENSE file.
8 8
9 Compare results of two render_pictures runs. 9 Compare results of two render_pictures runs.
10 10
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 57
58 58
59 class RenderedPicturesComparisons(results.BaseComparisons): 59 class RenderedPicturesComparisons(results.BaseComparisons):
60 """Loads results from multiple render_pictures runs into an ImagePairSet. 60 """Loads results from multiple render_pictures runs into an ImagePairSet.
61 """ 61 """
62 62
63 def __init__(self, setA_dirs, setB_dirs, image_diff_db, 63 def __init__(self, setA_dirs, setB_dirs, image_diff_db,
64 image_base_gs_url=DEFAULT_IMAGE_BASE_GS_URL, 64 image_base_gs_url=DEFAULT_IMAGE_BASE_GS_URL,
65 diff_base_url=None, setA_label='setA', 65 diff_base_url=None, setA_label='setA',
66 setB_label='setB', gs=None, 66 setB_label='setB', gs=None,
67 truncate_results=False): 67 truncate_results=False, prefetch_only=False,
68 """ 68 download_all_images=False):
69 """Constructor: downloads images and generates diffs.
70
71 Once the object has been created (which may take a while), you can call its
72 get_packaged_results_of_type() method to quickly retrieve the results...
73 unless you have set prefetch_only to True, in which case we will
74 asynchronously warm up the ImageDiffDB cache but not fill in self._results.
75
69 Args: 76 Args:
70 setA_dirs: list of root directories to copy all JSON summaries from, 77 setA_dirs: list of root directories to copy all JSON summaries from,
71 and to use as setA within the comparisons 78 and to use as setA within the comparisons
72 setB_dirs: list of root directories to copy all JSON summaries from, 79 setB_dirs: list of root directories to copy all JSON summaries from,
73 and to use as setB within the comparisons 80 and to use as setB within the comparisons
74 image_diff_db: ImageDiffDB instance 81 image_diff_db: ImageDiffDB instance
75 image_base_gs_url: "gs://" URL pointing at the Google Storage bucket/dir 82 image_base_gs_url: "gs://" URL pointing at the Google Storage bucket/dir
76 under which all render_pictures result images can 83 under which all render_pictures result images can
77 be found; this will be used to read images for comparison within 84 be found; this will be used to read images for comparison within
78 this code, and included in the ImagePairSet (as an HTTP URL) so its 85 this code, and included in the ImagePairSet (as an HTTP URL) so its
79 consumers know where to download the images from 86 consumers know where to download the images from
80 diff_base_url: base URL within which the client should look for diff 87 diff_base_url: base URL within which the client should look for diff
81 images; if not specified, defaults to a "file:///" URL representation 88 images; if not specified, defaults to a "file:///" URL representation
82 of image_diff_db's storage_root 89 of image_diff_db's storage_root
83 setA_label: description to use for results in setA 90 setA_label: description to use for results in setA
84 setB_label: description to use for results in setB 91 setB_label: description to use for results in setB
85 gs: instance of GSUtils object we can use to download summary files 92 gs: instance of GSUtils object we can use to download summary files
86 truncate_results: FOR MANUAL TESTING: if True, truncate the set of images 93 truncate_results: FOR MANUAL TESTING: if True, truncate the set of images
87 we process, to speed up testing. 94 we process, to speed up testing.
95 prefetch_only: if True, return the new object as quickly as possible
96 with empty self._results (just queue up all the files to process,
97 don't wait around for them to be processed and recorded); otherwise,
98 block until the results have been assembled and recorded in
99 self._results.
100 download_all_images: if True, download all images, even if we don't
101 need them to generate diffs. This will take much longer to complete,
102 but is useful for warming up the bitmap cache on local disk.
88 """ 103 """
89 super(RenderedPicturesComparisons, self).__init__() 104 super(RenderedPicturesComparisons, self).__init__()
90 self._image_diff_db = image_diff_db 105 self._image_diff_db = image_diff_db
91 self._image_base_gs_url = image_base_gs_url 106 self._image_base_gs_url = image_base_gs_url
92 self._diff_base_url = ( 107 self._diff_base_url = (
93 diff_base_url or 108 diff_base_url or
94 url_utils.create_filepath_url(image_diff_db.storage_root)) 109 url_utils.create_filepath_url(image_diff_db.storage_root))
95 self._setA_label = setA_label 110 self._setA_label = setA_label
96 self._setB_label = setB_label 111 self._setB_label = setB_label
97 self._gs = gs 112 self._gs = gs
98 self.truncate_results = truncate_results 113 self.truncate_results = truncate_results
114 self._prefetch_only = prefetch_only
115 self._download_all_images = download_all_images
99 116
100 tempdir = tempfile.mkdtemp() 117 tempdir = tempfile.mkdtemp()
101 try: 118 try:
102 setA_root = os.path.join(tempdir, 'setA') 119 setA_root = os.path.join(tempdir, 'setA')
103 setB_root = os.path.join(tempdir, 'setB') 120 setB_root = os.path.join(tempdir, 'setB')
104 for source_dir in setA_dirs: 121 for source_dir in setA_dirs:
105 self._copy_dir_contents(source_dir=source_dir, dest_dir=setA_root) 122 self._copy_dir_contents(source_dir=source_dir, dest_dir=setA_root)
106 for source_dir in setB_dirs: 123 for source_dir in setB_dirs:
107 self._copy_dir_contents(source_dir=source_dir, dest_dir=setB_root) 124 self._copy_dir_contents(source_dir=source_dir, dest_dir=setB_root)
108 125
109 time_start = int(time.time()) 126 time_start = int(time.time())
110 # TODO(epoger): For now, this assumes that we are always comparing two 127 # TODO(epoger): For now, this assumes that we are always comparing two
111 # sets of actual results, not actuals vs expectations. Allow the user 128 # sets of actual results, not actuals vs expectations. Allow the user
112 # to control this. 129 # to control this.
113 self._results = self._load_result_pairs( 130 self._results = self._load_result_pairs(
114 setA_root=setA_root, setA_section=gm_json.JSONKEY_ACTUALRESULTS, 131 setA_root=setA_root, setA_section=gm_json.JSONKEY_ACTUALRESULTS,
115 setB_root=setB_root, setB_section=gm_json.JSONKEY_ACTUALRESULTS) 132 setB_root=setB_root, setB_section=gm_json.JSONKEY_ACTUALRESULTS)
116 self._timestamp = int(time.time()) 133 if self._results:
117 logging.info('Number of download file collisions: %s' % 134 self._timestamp = int(time.time())
118 imagediffdb.global_file_collisions) 135 logging.info('Number of download file collisions: %s' %
119 logging.info('Results complete; took %d seconds.' % 136 imagediffdb.global_file_collisions)
120 (self._timestamp - time_start)) 137 logging.info('Results complete; took %d seconds.' %
138 (self._timestamp - time_start))
121 finally: 139 finally:
122 shutil.rmtree(tempdir) 140 shutil.rmtree(tempdir)
123 141
124 def _load_result_pairs(self, setA_root, setA_section, setB_root, 142 def _load_result_pairs(self, setA_root, setA_section, setB_root,
125 setB_section): 143 setB_section):
126 """Loads all JSON image summaries from 2 directory trees and compares them. 144 """Loads all JSON image summaries from 2 directory trees and compares them.
127 145
128 Args: 146 Args:
129 setA_root: root directory containing JSON summaries of rendering results 147 setA_root: root directory containing JSON summaries of rendering results
130 setA_section: which section (gm_json.JSONKEY_ACTUALRESULTS or 148 setA_section: which section (gm_json.JSONKEY_ACTUALRESULTS or
131 gm_json.JSONKEY_EXPECTEDRESULTS) to load from the summaries in setA 149 gm_json.JSONKEY_EXPECTEDRESULTS) to load from the summaries in setA
132 setB_root: root directory containing JSON summaries of rendering results 150 setB_root: root directory containing JSON summaries of rendering results
133 setB_section: which section (gm_json.JSONKEY_ACTUALRESULTS or 151 setB_section: which section (gm_json.JSONKEY_ACTUALRESULTS or
134 gm_json.JSONKEY_EXPECTEDRESULTS) to load from the summaries in setB 152 gm_json.JSONKEY_EXPECTEDRESULTS) to load from the summaries in setB
135 153
136 Returns the summary of all image diff results. 154 Returns the summary of all image diff results (or None, depending on
155 self._prefetch_only).
137 """ 156 """
138 logging.info('Reading JSON image summaries from dirs %s and %s...' % ( 157 logging.info('Reading JSON image summaries from dirs %s and %s...' % (
139 setA_root, setB_root)) 158 setA_root, setB_root))
140 setA_dicts = self._read_dicts_from_root(setA_root) 159 setA_dicts = self._read_dicts_from_root(setA_root)
141 setB_dicts = self._read_dicts_from_root(setB_root) 160 setB_dicts = self._read_dicts_from_root(setB_root)
142 logging.info('Comparing summary dicts...') 161 logging.info('Comparing summary dicts...')
143 162
144 all_image_pairs = imagepairset.ImagePairSet( 163 all_image_pairs = imagepairset.ImagePairSet(
145 descriptions=(self._setA_label, self._setB_label), 164 descriptions=(self._setA_label, self._setB_label),
146 diff_base_url=self._diff_base_url) 165 diff_base_url=self._diff_base_url)
(...skipping 20 matching lines...) Expand all
167 column_id=COLUMN__RESULT_TYPE, values=[ 186 column_id=COLUMN__RESULT_TYPE, values=[
168 results.KEY__RESULT_TYPE__FAILED, 187 results.KEY__RESULT_TYPE__FAILED,
169 results.KEY__RESULT_TYPE__NOCOMPARISON, 188 results.KEY__RESULT_TYPE__NOCOMPARISON,
170 ]) 189 ])
171 190
172 union_dict_paths = sorted(set(setA_dicts.keys() + setB_dicts.keys())) 191 union_dict_paths = sorted(set(setA_dicts.keys() + setB_dicts.keys()))
173 num_union_dict_paths = len(union_dict_paths) 192 num_union_dict_paths = len(union_dict_paths)
174 dict_num = 0 193 dict_num = 0
175 for dict_path in union_dict_paths: 194 for dict_path in union_dict_paths:
176 dict_num += 1 195 dict_num += 1
177 logging.info('Generating pixel diffs for dict #%d of %d, "%s"...' % 196 logging.info(
178 (dict_num, num_union_dict_paths, dict_path)) 197 'Asynchronously requesting pixel diffs for dict #%d of %d, "%s"...' %
198 (dict_num, num_union_dict_paths, dict_path))
179 199
180 dictA = self.get_default(setA_dicts, None, dict_path) 200 dictA = self.get_default(setA_dicts, None, dict_path)
181 self._validate_dict_version(dictA) 201 self._validate_dict_version(dictA)
182 dictA_results = self.get_default(dictA, {}, setA_section) 202 dictA_results = self.get_default(dictA, {}, setA_section)
183 203
184 dictB = self.get_default(setB_dicts, None, dict_path) 204 dictB = self.get_default(setB_dicts, None, dict_path)
185 self._validate_dict_version(dictB) 205 self._validate_dict_version(dictB)
186 dictB_results = self.get_default(dictB, {}, setB_section) 206 dictB_results = self.get_default(dictB, {}, setB_section)
187 207
188 skp_names = sorted(set(dictA_results.keys() + dictB_results.keys())) 208 skp_names = sorted(set(dictA_results.keys() + dictB_results.keys()))
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
220 source_skp_name=skp_name, tilenum=tile_num)) 240 source_skp_name=skp_name, tilenum=tile_num))
221 241
222 for one_imagepair in imagepairs_for_this_skp: 242 for one_imagepair in imagepairs_for_this_skp:
223 if one_imagepair: 243 if one_imagepair:
224 all_image_pairs.add_image_pair(one_imagepair) 244 all_image_pairs.add_image_pair(one_imagepair)
225 result_type = one_imagepair.extra_columns_dict\ 245 result_type = one_imagepair.extra_columns_dict\
226 [COLUMN__RESULT_TYPE] 246 [COLUMN__RESULT_TYPE]
227 if result_type != results.KEY__RESULT_TYPE__SUCCEEDED: 247 if result_type != results.KEY__RESULT_TYPE__SUCCEEDED:
228 failing_image_pairs.add_image_pair(one_imagepair) 248 failing_image_pairs.add_image_pair(one_imagepair)
229 249
230 return { 250 if self._prefetch_only:
231 results.KEY__HEADER__RESULTS_ALL: all_image_pairs.as_dict( 251 return None
232 column_ids_in_order=ORDERED_COLUMN_IDS), 252 else:
233 results.KEY__HEADER__RESULTS_FAILURES: failing_image_pairs.as_dict( 253 return {
234 column_ids_in_order=ORDERED_COLUMN_IDS), 254 results.KEY__HEADER__RESULTS_ALL: all_image_pairs.as_dict(
235 } 255 column_ids_in_order=ORDERED_COLUMN_IDS),
256 results.KEY__HEADER__RESULTS_FAILURES: failing_image_pairs.as_dict(
257 column_ids_in_order=ORDERED_COLUMN_IDS),
258 }
236 259
237 def _validate_dict_version(self, result_dict): 260 def _validate_dict_version(self, result_dict):
238 """Raises Exception if the dict is not the type/version we know how to read. 261 """Raises Exception if the dict is not the type/version we know how to read.
239 262
240 Args: 263 Args:
241 result_dict: dictionary holding output of render_pictures; if None, 264 result_dict: dictionary holding output of render_pictures; if None,
242 this method will return without raising an Exception 265 this method will return without raising an Exception
243 """ 266 """
244 expected_header_type = 'ChecksummedImages' 267 expected_header_type = 'ChecksummedImages'
245 expected_header_revision = 1 268 expected_header_revision = 1
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
305 else: 328 else:
306 extra_columns_dict[COLUMN__TILED_OR_WHOLE] = 'tiled' 329 extra_columns_dict[COLUMN__TILED_OR_WHOLE] = 'tiled'
307 extra_columns_dict[COLUMN__TILENUM] = str(tilenum) 330 extra_columns_dict[COLUMN__TILENUM] = str(tilenum)
308 331
309 try: 332 try:
310 return imagepair.ImagePair( 333 return imagepair.ImagePair(
311 image_diff_db=self._image_diff_db, 334 image_diff_db=self._image_diff_db,
312 base_url=self._image_base_gs_url, 335 base_url=self._image_base_gs_url,
313 imageA_relative_url=imageA_relative_url, 336 imageA_relative_url=imageA_relative_url,
314 imageB_relative_url=imageB_relative_url, 337 imageB_relative_url=imageB_relative_url,
315 extra_columns=extra_columns_dict) 338 extra_columns=extra_columns_dict,
339 download_all_images=self._download_all_images)
316 except (KeyError, TypeError): 340 except (KeyError, TypeError):
317 logging.exception( 341 logging.exception(
318 'got exception while creating ImagePair for' 342 'got exception while creating ImagePair for'
319 ' urlPair=("%s","%s"), source_skp_name="%s", tilenum="%s"' % ( 343 ' urlPair=("%s","%s"), source_skp_name="%s", tilenum="%s"' % (
320 imageA_relative_url, imageB_relative_url, source_skp_name, 344 imageA_relative_url, imageB_relative_url, source_skp_name,
321 tilenum)) 345 tilenum))
322 return None 346 return None
323 347
324 def _copy_dir_contents(self, source_dir, dest_dir): 348 def _copy_dir_contents(self, source_dir, dest_dir):
325 """Copy all contents of source_dir into dest_dir, recursing into subdirs. 349 """Copy all contents of source_dir into dest_dir, recursing into subdirs.
326 350
327 Args: 351 Args:
328 source_dir: path to source dir (GS URL or local filepath) 352 source_dir: path to source dir (GS URL or local filepath)
329 dest_dir: path to destination dir (local filepath) 353 dest_dir: path to destination dir (local filepath)
330 354
331 The copy operates as a "merge with overwrite": any files in source_dir will 355 The copy operates as a "merge with overwrite": any files in source_dir will
332 be "overlaid" on top of the existing content in dest_dir. Existing files 356 be "overlaid" on top of the existing content in dest_dir. Existing files
333 with the same names will be overwritten. 357 with the same names will be overwritten.
334 """ 358 """
335 if gs_utils.GSUtils.is_gs_url(source_dir): 359 if gs_utils.GSUtils.is_gs_url(source_dir):
336 (bucket, path) = gs_utils.GSUtils.split_gs_url(source_dir) 360 (bucket, path) = gs_utils.GSUtils.split_gs_url(source_dir)
337 self._gs.download_dir_contents(source_bucket=bucket, source_dir=path, 361 self._gs.download_dir_contents(source_bucket=bucket, source_dir=path,
338 dest_dir=dest_dir) 362 dest_dir=dest_dir)
339 else: 363 else:
340 shutil.copytree(source_dir, dest_dir) 364 shutil.copytree(source_dir, dest_dir)
OLDNEW
« no previous file with comments | « no previous file | gm/rebaseline_server/imagediffdb.py » ('j') | gm/rebaseline_server/imagediffdb.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698