remove SkThreadedBMPDevice and friends

It is unused, is becoming a maintainence burden and source of bugs,
and takes up a lot of time on the *SAN bots.

Change-Id: If383eb6e4838ca23140f9e16d518b1bfc655fa12
Reviewed-on: https://skia-review.googlesource.com/143307
Auto-Submit: Mike Klein <mtklein@google.com>
Commit-Queue: Herb Derby <herb@google.com>
Reviewed-by: Herb Derby <herb@google.com>
This commit is contained in:
Mike Klein 2018-07-25 13:28:44 -04:00 committed by Skia Commit-Bot
parent 1c94a8fabe
commit 48b649060c
17 changed files with 10 additions and 1346 deletions

View File

@ -906,7 +906,6 @@ static Sink* create_sink(const GrContextOptions& grCtxOptions, const SkCommandLi
SINK("rgbx", RasterSink, kRGB_888x_SkColorType);
SINK("1010102", RasterSink, kRGBA_1010102_SkColorType);
SINK("101010x", RasterSink, kRGB_101010x_SkColorType);
SINK("t8888", ThreadedSink, kN32_SkColorType);
SINK("pdf", PDFSink, false, SK_ScalarDefaultRasterDPI);
SINK("skp", SKPSink);
SINK("pipe", PipeSink);

View File

@ -56,11 +56,11 @@
#include "SkRecordDraw.h"
#include "SkRecorder.h"
#include "SkStream.h"
#include "SkSurface.h"
#include "SkSurfaceCharacterization.h"
#include "SkSwizzler.h"
#include "SkTLogic.h"
#include "SkTaskGroup.h"
#include "SkThreadedBMPDevice.h"
#if defined(SK_BUILD_FOR_WIN)
#include "SkAutoCoInitialize.h"
#include "SkHRESULT.h"
@ -1804,7 +1804,7 @@ RasterSink::RasterSink(SkColorType colorType, sk_sp<SkColorSpace> colorSpace)
: fColorType(colorType)
, fColorSpace(std::move(colorSpace)) {}
void RasterSink::allocPixels(const Src& src, SkBitmap* dst) const {
Error RasterSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString*) const {
const SkISize size = src.size();
// If there's an appropriate alpha type for this color type, use it, otherwise use premul.
SkAlphaType alphaType = kPremul_SkAlphaType;
@ -1813,42 +1813,13 @@ void RasterSink::allocPixels(const Src& src, SkBitmap* dst) const {
dst->allocPixelsFlags(SkImageInfo::Make(size.width(), size.height(),
fColorType, alphaType, fColorSpace),
SkBitmap::kZeroPixels_AllocFlag);
}
Error RasterSink::draw(const Src& src, SkBitmap* dst, SkWStream*, SkString*) const {
this->allocPixels(src, dst);
SkCanvas canvas(*dst);
return src.draw(&canvas);
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
ThreadedSink::ThreadedSink(SkColorType colorType, sk_sp<SkColorSpace> colorSpace)
: RasterSink(colorType, colorSpace) {}
Error ThreadedSink::draw(const Src& src, SkBitmap* dst, SkWStream* stream, SkString* str) const {
this->allocPixels(src, dst);
auto canvas = skstd::make_unique<SkCanvas>(
sk_make_sp<SkThreadedBMPDevice>(
*dst, FLAGS_backendTiles, FLAGS_backendThreads));
Error result = src.draw(canvas.get());
canvas->flush();
return result;
// ??? yuqian: why does the following give me segmentation fault while the above one works?
// The seg fault occurs right in the beginning of ThreadedSink::draw with invalid
// memory address (it would crash without even calling this->allocPixels).
// SkThreadedBMPDevice device(*dst, tileCnt, FLAGS_cpuThreads, fExecutor.get());
// SkCanvas canvas(&device);
// Error result = src.draw(&canvas);
// canvas.flush();
// return result;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
// Handy for front-patching a Src. Do whatever up-front work you need, then call draw_to_canvas(),
// passing the Sink draw() arguments, a size, and a function draws into an SkCanvas.
// Several examples below.

View File

@ -441,9 +441,8 @@ public:
Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
const char* fileExtension() const override { return "png"; }
SinkFlags flags() const override { return SinkFlags{ SinkFlags::kRaster, SinkFlags::kDirect }; }
protected:
void allocPixels(const Src& src, SkBitmap*) const;
private:
SkColorType fColorType;
sk_sp<SkColorSpace> fColorSpace;
};

View File

@ -311,8 +311,6 @@ skia_core_sources = [
"$_src/core/SkSRGB.cpp",
"$_src/core/SkTaskGroup.cpp",
"$_src/core/SkTaskGroup.h",
"$_src/core/SkTaskGroup2D.cpp",
"$_src/core/SkTaskGroup2D.h",
"$_src/core/SkTDPQueue.h",
"$_src/core/SkTDynamicHash.h",
"$_src/core/SkTextBlob.cpp",
@ -322,9 +320,6 @@ skia_core_sources = [
"$_src/core/SkTextToPathIter.h",
"$_src/core/SkTime.cpp",
# The Clang's false positive thread warnings have been resolved in SkTaskGroup2D.cpp
"$_src/core/SkThreadedBMPDevice.cpp",
"$_src/core/SkThreadedBMPDevice.h",
"$_src/core/SkThreadID.cpp",
"$_src/core/SkTLList.h",
"$_src/core/SkTLS.cpp",

View File

@ -404,7 +404,6 @@
"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-NativeFonts",
"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-SK_USE_DISCARDABLE_SCALEDIMAGECACHE",
"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-SafeStack",
"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-T8888",
"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-Shard_12-Coverage",
"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All",
"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-ASAN",

View File

@ -1,278 +0,0 @@
[
{
"cmd": [
"python",
"-u",
"RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
"--json-output",
"/path/to/tmp/json",
"ensure-directory",
"--mode",
"0777",
"[START_DIR]/tmp"
],
"infra_step": true,
"name": "makedirs tmp_dir"
},
{
"cmd": [
"python",
"-u",
"RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
"--json-output",
"/path/to/tmp/json",
"copy",
"[START_DIR]/skia/infra/bots/assets/skp/VERSION",
"/path/to/tmp/"
],
"infra_step": true,
"name": "Get skp VERSION"
},
{
"cmd": [
"python",
"-u",
"RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
"--json-output",
"/path/to/tmp/json",
"copy",
"42",
"[START_DIR]/tmp/SKP_VERSION"
],
"infra_step": true,
"name": "write SKP_VERSION"
},
{
"cmd": [
"python",
"-u",
"RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
"--json-output",
"/path/to/tmp/json",
"copy",
"[START_DIR]/skia/infra/bots/assets/skimage/VERSION",
"/path/to/tmp/"
],
"infra_step": true,
"name": "Get skimage VERSION"
},
{
"cmd": [
"python",
"-u",
"RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
"--json-output",
"/path/to/tmp/json",
"copy",
"42",
"[START_DIR]/tmp/SK_IMAGE_VERSION"
],
"infra_step": true,
"name": "write SK_IMAGE_VERSION"
},
{
"cmd": [
"python",
"-u",
"RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
"--json-output",
"/path/to/tmp/json",
"copy",
"[START_DIR]/skia/infra/bots/assets/svg/VERSION",
"/path/to/tmp/"
],
"infra_step": true,
"name": "Get svg VERSION"
},
{
"cmd": [
"python",
"-u",
"RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
"--json-output",
"/path/to/tmp/json",
"copy",
"42",
"[START_DIR]/tmp/SVG_VERSION"
],
"infra_step": true,
"name": "write SVG_VERSION"
},
{
"cmd": [
"python",
"-u",
"RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
"--json-output",
"/path/to/tmp/json",
"rmtree",
"[START_DIR]/test"
],
"infra_step": true,
"name": "rmtree test"
},
{
"cmd": [
"python",
"-u",
"RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
"--json-output",
"/path/to/tmp/json",
"ensure-directory",
"--mode",
"0777",
"[START_DIR]/test"
],
"infra_step": true,
"name": "makedirs test"
},
{
"cmd": [
"python",
"-u",
"\nimport contextlib\nimport math\nimport socket\nimport sys\nimport time\nimport urllib2\n\nHASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'\nRETRIES = 5\nTIMEOUT = 60\nWAIT_BASE = 15\n\nsocket.setdefaulttimeout(TIMEOUT)\nfor retry in range(RETRIES):\n try:\n with contextlib.closing(\n urllib2.urlopen(HASHES_URL, timeout=TIMEOUT)) as w:\n hashes = w.read()\n with open(sys.argv[1], 'w') as f:\n f.write(hashes)\n break\n except Exception as e:\n print 'Failed to get uninteresting hashes from %s:' % HASHES_URL\n print e\n if retry == RETRIES:\n raise\n waittime = WAIT_BASE * math.pow(2, retry)\n print 'Retry in %d seconds.' % waittime\n time.sleep(waittime)\n",
"[START_DIR]/tmp/uninteresting_hashes.txt"
],
"env": {
"CHROME_HEADLESS": "1",
"PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]"
},
"infra_step": true,
"name": "get uninteresting hashes",
"~followup_annotations": [
"@@@STEP_LOG_LINE@python.inline@@@@",
"@@@STEP_LOG_LINE@python.inline@import contextlib@@@",
"@@@STEP_LOG_LINE@python.inline@import math@@@",
"@@@STEP_LOG_LINE@python.inline@import socket@@@",
"@@@STEP_LOG_LINE@python.inline@import sys@@@",
"@@@STEP_LOG_LINE@python.inline@import time@@@",
"@@@STEP_LOG_LINE@python.inline@import urllib2@@@",
"@@@STEP_LOG_LINE@python.inline@@@@",
"@@@STEP_LOG_LINE@python.inline@HASHES_URL = 'https://storage.googleapis.com/skia-infra-gm/hash_files/gold-prod-hashes.txt'@@@",
"@@@STEP_LOG_LINE@python.inline@RETRIES = 5@@@",
"@@@STEP_LOG_LINE@python.inline@TIMEOUT = 60@@@",
"@@@STEP_LOG_LINE@python.inline@WAIT_BASE = 15@@@",
"@@@STEP_LOG_LINE@python.inline@@@@",
"@@@STEP_LOG_LINE@python.inline@socket.setdefaulttimeout(TIMEOUT)@@@",
"@@@STEP_LOG_LINE@python.inline@for retry in range(RETRIES):@@@",
"@@@STEP_LOG_LINE@python.inline@ try:@@@",
"@@@STEP_LOG_LINE@python.inline@ with contextlib.closing(@@@",
"@@@STEP_LOG_LINE@python.inline@ urllib2.urlopen(HASHES_URL, timeout=TIMEOUT)) as w:@@@",
"@@@STEP_LOG_LINE@python.inline@ hashes = w.read()@@@",
"@@@STEP_LOG_LINE@python.inline@ with open(sys.argv[1], 'w') as f:@@@",
"@@@STEP_LOG_LINE@python.inline@ f.write(hashes)@@@",
"@@@STEP_LOG_LINE@python.inline@ break@@@",
"@@@STEP_LOG_LINE@python.inline@ except Exception as e:@@@",
"@@@STEP_LOG_LINE@python.inline@ print 'Failed to get uninteresting hashes from %s:' % HASHES_URL@@@",
"@@@STEP_LOG_LINE@python.inline@ print e@@@",
"@@@STEP_LOG_LINE@python.inline@ if retry == RETRIES:@@@",
"@@@STEP_LOG_LINE@python.inline@ raise@@@",
"@@@STEP_LOG_LINE@python.inline@ waittime = WAIT_BASE * math.pow(2, retry)@@@",
"@@@STEP_LOG_LINE@python.inline@ print 'Retry in %d seconds.' % waittime@@@",
"@@@STEP_LOG_LINE@python.inline@ time.sleep(waittime)@@@",
"@@@STEP_LOG_END@python.inline@@@"
]
},
{
"cmd": [
"python",
"-u",
"import os\nprint os.environ.get('SWARMING_BOT_ID', '')\n"
],
"name": "get swarming bot id",
"stdout": "/path/to/tmp/",
"~followup_annotations": [
"@@@STEP_LOG_LINE@python.inline@import os@@@",
"@@@STEP_LOG_LINE@python.inline@print os.environ.get('SWARMING_BOT_ID', '')@@@",
"@@@STEP_LOG_END@python.inline@@@"
]
},
{
"cmd": [
"python",
"-u",
"import os\nprint os.environ.get('SWARMING_TASK_ID', '')\n"
],
"name": "get swarming task id",
"stdout": "/path/to/tmp/",
"~followup_annotations": [
"@@@STEP_LOG_LINE@python.inline@import os@@@",
"@@@STEP_LOG_LINE@python.inline@print os.environ.get('SWARMING_TASK_ID', '')@@@",
"@@@STEP_LOG_END@python.inline@@@"
]
},
{
"cmd": [
"python",
"-u",
"RECIPE_MODULE[skia::flavor]/resources/symbolize_stack_trace.py",
"[START_DIR]",
"catchsegv",
"[START_DIR]/build/dm",
"--resourcePath",
"[START_DIR]/skia/resources",
"--skps",
"[START_DIR]/skp",
"--images",
"[START_DIR]/skimage/dm",
"--colorImages",
"[START_DIR]/skimage/colorspace",
"--nameByHash",
"--properties",
"gitHash",
"abc123",
"builder",
"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-T8888",
"buildbucket_build_id",
"123454321",
"swarming_bot_id",
"skia-bot-123",
"swarming_task_id",
"123456",
"--svgs",
"[START_DIR]/svg",
"--key",
"arch",
"x86_64",
"compiler",
"Clang",
"configuration",
"Debug",
"cpu_or_gpu",
"CPU",
"cpu_or_gpu_value",
"AVX2",
"extra_config",
"T8888",
"model",
"GCE",
"os",
"Debian9",
"--uninterestingHashesFile",
"[START_DIR]/tmp/uninteresting_hashes.txt",
"--writePath",
"[START_DIR]/[SWARM_OUT_DIR]",
"--dont_write",
"pdf",
"--randomProcessorTest",
"--nogpu",
"--config",
"t8888",
"--src",
"gm",
"--nonativeFonts",
"--verbose"
],
"cwd": "[START_DIR]/skia",
"env": {
"CHROME_HEADLESS": "1",
"PATH": "<PATH>:RECIPE_PACKAGE_REPO[depot_tools]"
},
"name": "symbolized dm"
},
{
"name": "$result",
"recipe_result": null,
"status_code": 0
}
]

View File

@ -126,15 +126,8 @@ def dm_flags(api, bot):
configs.extend(['narrow'])
configs.extend(['enarrow'])
# Temporarily disabled while debugging.
#if 'SAN' in bot:
# configs.extend(['t8888'])
configs.extend(mode + '-8888' for mode in ['serialize', 'tiles_rt', 'pic'])
if 'T8888' in bot:
configs = ['t8888']
# This bot only differs from vanilla CPU bots in 8888 config.
if 'SK_FORCE_RASTER_PIPELINE_BLITTER' in bot:
configs = ['8888']
@ -329,11 +322,6 @@ def dm_flags(api, bot):
args.remove('image')
args.remove('colorImage')
if 'T8888' in bot:
args.remove('tests')
args.remove('image')
args.remove('colorImage')
def remove_from_args(arg):
if arg in args:
args.remove(arg)
@ -1045,7 +1033,6 @@ TEST_BUILDERS = [
'Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN',
('Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All'
'-SK_USE_DISCARDABLE_SCALEDIMAGECACHE'),
'Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-T8888',
'Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie',
('Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All'
'-SK_FORCE_RASTER_PIPELINE_BLITTER'),

View File

@ -2038,11 +2038,6 @@
"Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-SafeStack"
]
},
"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-T8888": {
"tasks": [
"Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-T8888"
]
},
"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-Shard_12-Coverage": {
"tasks": [
"Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-Shard_12-Coverage"
@ -48294,118 +48289,6 @@
"test"
]
},
"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-T8888": {
"caches": [
{
"name": "vpython",
"path": "cache/vpython"
}
],
"cipd_packages": [
{
"name": "infra/tools/luci/kitchen/${platform}",
"path": ".",
"version": "git_revision:546aae39f1fb9dce9add528e2011afa574535ecd"
},
{
"name": "infra/tools/luci-auth/${platform}",
"path": "cipd_bin_packages",
"version": "git_revision:e1abc57be62d198b5c2f487bfb2fa2d2eb0e867c"
},
{
"name": "infra/tools/luci/vpython/${platform}",
"path": "cipd_bin_packages",
"version": "git_revision:b9c4670197dcefd8762d6e509302acd3efc6e303"
},
{
"name": "skia/bots/skimage",
"path": "skimage",
"version": "version:34"
},
{
"name": "skia/bots/skp",
"path": "skp",
"version": "version:135"
},
{
"name": "skia/bots/svg",
"path": "svg",
"version": "version:9"
}
],
"command": [
"./kitchen${EXECUTABLE_SUFFIX}",
"cook",
"-checkout-dir",
"recipe_bundle",
"-mode",
"swarming",
"-luci-system-account",
"system",
"-cache-dir",
"cache",
"-temp-dir",
"tmp",
"-known-gerrit-host",
"android.googlesource.com",
"-known-gerrit-host",
"boringssl.googlesource.com",
"-known-gerrit-host",
"chromium.googlesource.com",
"-known-gerrit-host",
"dart.googlesource.com",
"-known-gerrit-host",
"fuchsia.googlesource.com",
"-known-gerrit-host",
"go.googlesource.com",
"-known-gerrit-host",
"llvm.googlesource.com",
"-known-gerrit-host",
"skia.googlesource.com",
"-known-gerrit-host",
"webrtc.googlesource.com",
"-output-result-json",
"${ISOLATED_OUTDIR}/build_result_filename",
"-workdir",
".",
"-recipe",
"test",
"-properties",
"{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-T8888\",\"patch_issue\":\"<(ISSUE)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"test\"}",
"-logdog-annotation-url",
"logdog://logs.chromium.org/skia/<(TASK_ID)/+/annotations"
],
"dependencies": [
"Housekeeper-PerCommit-BundleRecipes",
"Build-Debian9-Clang-x86_64-Debug"
],
"dimensions": [
"cpu:x86-64-Haswell_GCE",
"machine_type:n1-standard-16",
"os:Debian-9.4",
"pool:Skia"
],
"env_prefixes": {
"PATH": [
"cipd_bin_packages",
"cipd_bin_packages/bin"
],
"VPYTHON_VIRTUALENV_ROOT": [
"${cache_dir}/vpython"
]
},
"execution_timeout_ns": 14400000000000,
"expiration_ns": 72000000000000,
"extra_tags": {
"log_location": "logdog://logs.chromium.org/skia/<(TASK_ID)/+/annotations"
},
"io_timeout_ns": 14400000000000,
"isolate": "test_skia_bundled.isolate",
"max_attempts": 1,
"outputs": [
"test"
]
},
"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-shard_00_12-Coverage": {
"caches": [
{
@ -87690,105 +87573,6 @@
"isolate": "swarm_recipe.isolate",
"service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
},
"Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-T8888": {
"caches": [
{
"name": "vpython",
"path": "cache/vpython"
}
],
"cipd_packages": [
{
"name": "infra/tools/luci/kitchen/${platform}",
"path": ".",
"version": "git_revision:546aae39f1fb9dce9add528e2011afa574535ecd"
},
{
"name": "infra/tools/luci-auth/${platform}",
"path": "cipd_bin_packages",
"version": "git_revision:e1abc57be62d198b5c2f487bfb2fa2d2eb0e867c"
},
{
"name": "infra/tools/luci/vpython/${platform}",
"path": "cipd_bin_packages",
"version": "git_revision:b9c4670197dcefd8762d6e509302acd3efc6e303"
},
{
"name": "infra/gsutil",
"path": "cipd_bin_packages",
"version": "version:4.28"
}
],
"command": [
"./kitchen${EXECUTABLE_SUFFIX}",
"cook",
"-checkout-dir",
"recipe_bundle",
"-mode",
"swarming",
"-luci-system-account",
"system",
"-cache-dir",
"cache",
"-temp-dir",
"tmp",
"-known-gerrit-host",
"android.googlesource.com",
"-known-gerrit-host",
"boringssl.googlesource.com",
"-known-gerrit-host",
"chromium.googlesource.com",
"-known-gerrit-host",
"dart.googlesource.com",
"-known-gerrit-host",
"fuchsia.googlesource.com",
"-known-gerrit-host",
"go.googlesource.com",
"-known-gerrit-host",
"llvm.googlesource.com",
"-known-gerrit-host",
"skia.googlesource.com",
"-known-gerrit-host",
"webrtc.googlesource.com",
"-output-result-json",
"${ISOLATED_OUTDIR}/build_result_filename",
"-workdir",
".",
"-recipe",
"upload_dm_results",
"-properties",
"{\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"buildbucket_build_id\":\"<(BUILDBUCKET_BUILD_ID)\",\"buildername\":\"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-T8888\",\"gs_bucket\":\"skia-infra-gm\",\"patch_issue\":\"<(ISSUE)\",\"patch_ref\":\"<(PATCH_REF)\",\"patch_repo\":\"<(PATCH_REPO)\",\"patch_set\":\"<(PATCHSET)\",\"patch_storage\":\"<(PATCH_STORAGE)\",\"repository\":\"<(REPO)\",\"revision\":\"<(REVISION)\",\"swarm_out_dir\":\"output_ignored\"}",
"-logdog-annotation-url",
"logdog://logs.chromium.org/skia/<(TASK_ID)/+/annotations"
],
"dependencies": [
"Housekeeper-PerCommit-BundleRecipes",
"Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-T8888"
],
"dimensions": [
"cpu:x86-64-Haswell_GCE",
"gpu:none",
"machine_type:n1-highmem-2",
"os:Debian-9.4",
"pool:Skia"
],
"env_prefixes": {
"PATH": [
"cipd_bin_packages",
"cipd_bin_packages/bin"
],
"VPYTHON_VIRTUALENV_ROOT": [
"${cache_dir}/vpython"
]
},
"execution_timeout_ns": 3600000000000,
"extra_tags": {
"log_location": "logdog://logs.chromium.org/skia/<(TASK_ID)/+/annotations"
},
"io_timeout_ns": 3600000000000,
"isolate": "swarm_recipe.isolate",
"service_account": "skia-external-gm-uploader@skia-swarming-bots.iam.gserviceaccount.com"
},
"Upload-Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-Shard_12-Coverage": {
"caches": [
{

View File

@ -155,7 +155,6 @@ private:
friend class SkDrawTiler;
friend class SkDeviceFilteredPaint;
friend class SkSurface_Raster;
friend class SkThreadedBMPDevice; // to copy fRCStack
class BDDraw;

View File

@ -35,7 +35,6 @@
#include "SkTLazy.h"
#include "SkTemplates.h"
#include "SkTextMapStateProc.h"
#include "SkThreadedBMPDevice.h"
#include "SkTo.h"
#include "SkUtils.h"
@ -957,11 +956,8 @@ SkScalar SkDraw::ComputeResScaleForStroking(const SkMatrix& matrix) {
}
void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage,
SkBlitter* customBlitter, bool doFill, SkInitOnceData* iData) const {
SkBlitter* customBlitter, bool doFill) const {
if (SkPathPriv::TooBigForMath(devPath)) {
if (iData) {
iData->setEmptyDrawFn();
}
return;
}
SkBlitter* blitter = nullptr;
@ -976,9 +972,6 @@ void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawC
SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle
: SkStrokeRec::kHairline_InitStyle;
if (as_MFB(paint.getMaskFilter())->filterPath(devPath, *fMatrix, *fRC, blitter, style)) {
if (iData) {
iData->setEmptyDrawFn();
}
return; // filterPath() called the blitter, so we're done
}
}
@ -1024,48 +1017,16 @@ void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawC
}
}
if (iData == nullptr) {
proc(devPath, *fRC, blitter); // proceed directly if we're not in threaded init-once
} else if (!doFill || !paint.isAntiAlias()) {
// We're in threaded init-once but we can't use DAA. Hence we'll stop here and hand all the
// remaining work to draw phase. This is a simple example of how to add init-once to
// existing drawXXX commands: simply send in SkInitOnceData, do as much init work as
// possible, and finally wrap the remaining work into iData->fElement->fDrawFn.
SkASSERT(customBlitter == nullptr);
devPath.updateBoundsCache(); // make it thread safe
iData->fElement->setDrawFn([proc, devPath, paint, drawCoverage](SkArenaAlloc* alloc,
const SkThreadedBMPDevice::DrawState& ds, const SkIRect& tileBounds) {
SkThreadedBMPDevice::TileDraw tileDraw(ds, tileBounds);
SkAutoBlitterChoose blitterStorage(tileDraw, nullptr, paint, drawCoverage);
proc(devPath, *tileDraw.fRC, blitterStorage.get());
});
} else {
// We can use DAA to do scan conversion in the init-once phase.
SkDAARecord* record = iData->fAlloc->make<SkDAARecord>(iData->fAlloc);
SkNullBlitter nullBlitter; // We don't want to blit anything during the init phase
SkScan::AntiFillPath(devPath, *fRC, &nullBlitter, record);
SkASSERT(customBlitter == nullptr);
iData->fElement->setDrawFn([record, devPath, paint, drawCoverage](SkArenaAlloc* alloc,
const SkThreadedBMPDevice::DrawState& ds, const SkIRect& tileBounds) {
SkASSERT(record->fType != SkDAARecord::Type::kToBeComputed);
SkThreadedBMPDevice::TileDraw tileDraw(ds, tileBounds);
SkAutoBlitterChoose blitterStorage(tileDraw, nullptr, paint, drawCoverage);
SkScan::AntiFillPath(devPath, *tileDraw.fRC, blitterStorage.get(), record);
});
}
proc(devPath, *fRC, blitter);
}
void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
const SkMatrix* prePathMatrix, bool pathIsMutable,
bool drawCoverage, SkBlitter* customBlitter,
SkInitOnceData* iData) const {
bool drawCoverage, SkBlitter* customBlitter) const {
SkDEBUGCODE(this->validate();)
// nothing to draw
if (fRC->isEmpty()) {
if (iData) {
iData->setEmptyDrawFn();
}
return;
}
@ -1075,9 +1036,6 @@ void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
SkPath* tmpPath = &tmpPathStorage;
SkMatrix tmpMatrix;
const SkMatrix* matrix = fMatrix;
if (iData) {
tmpPath = iData->fAlloc->make<SkPath>();
}
tmpPath->setIsVolatile(true);
SkPathPriv::SetIsBadForDAA(*tmpPath, SkPathPriv::IsBadForDAA(origSrcPath));
@ -1142,7 +1100,7 @@ void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint,
// transform the path into device space
pathPtr->transform(*matrix, devPathPtr);
this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill, iData);
this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill);
}
void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkPaint& paint) const {

View File

@ -31,7 +31,6 @@ class SkRasterClip;
struct SkDrawProcs;
struct SkRect;
class SkRRect;
struct SkInitOnceData;
class SkDraw {
public:
@ -137,11 +136,11 @@ private:
void drawPath(const SkPath&, const SkPaint&, const SkMatrix* preMatrix,
bool pathIsMutable, bool drawCoverage,
SkBlitter* customBlitter = nullptr, SkInitOnceData* iData = nullptr) const;
SkBlitter* customBlitter = nullptr) const;
void drawLine(const SkPoint[2], const SkPaint&) const;
void drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage,
SkBlitter* customBlitter, bool doFill, SkInitOnceData* iData = nullptr) const;
SkBlitter* customBlitter, bool doFill) const;
/**
* Return the current clip bounds, in local coordinates, with slop to account
* for antialiasing or hairlines (i.e. device-bounds outset by 1, and then
@ -169,8 +168,6 @@ public:
#else
void validate() const {}
#endif
friend class SkThreadedBMPDevice; // to access private method drawPath
};
#endif

View File

@ -1,86 +0,0 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkTaskGroup2D.h"
void SkTaskGroup2D::start() {
fThreadsGroup->batch(fThreadCnt, [this](int threadId){
this->work(threadId);
});
}
void SkTaskGroup2D::addColumn() {
SkASSERT(!fIsFinishing); // we're not supposed to add more work after the calling of finish
fWidth++;
}
void SkTaskGroup2D::finish() {
fIsFinishing = true;
fThreadsGroup->wait();
}
void SkSpinningTaskGroup2D::work(int threadId) {
int workCol = 0;
int initCol = 0;
while (true) {
SkASSERT(workCol <= fWidth);
if (this->isFinishing() && workCol >= fWidth) {
return;
}
// Note that row = threadId
if (workCol < fWidth && fKernel->work2D(threadId, workCol, threadId)) {
workCol++;
} else {
// Initialize something if we can't work
this->initAnUninitializedColumn(initCol, threadId);
}
}
}
void SkFlexibleTaskGroup2D::work(int threadId) {
int row = threadId;
int initCol = 0;
int numRowsCompleted = 0;
std::vector<bool> completedRows(fHeight, false);
// Only keep fHeight - numRowsCompleted number of threads looping. When rows are about to
// complete, this strategy keeps the contention low.
while (threadId < fHeight - numRowsCompleted) {
RowData& rowData = fRowData[row];
// The Android roller somehow gets a false-positive compile warning/error about the try-lock
// and unlock process. Hence we disable -Wthread-safety-analysis to bypass it.
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wthread-safety-analysis"
#endif
if (rowData.fMutex.try_lock()) {
while (rowData.fNextColumn < fWidth &&
fKernel->work2D(row, rowData.fNextColumn, threadId)) {
rowData.fNextColumn++;
}
// isFinishing can never go from true to false. Once it's true, we count how many rows
// are completed (out of work). If that count reaches fHeight, then we're out of work
// for the whole group and we can stop.
if (rowData.fNextColumn == fWidth && this->isFinishing()) {
numRowsCompleted += (completedRows[row] == false);
completedRows[row] = true; // so we won't count this row twice
}
rowData.fMutex.unlock();
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif
// By reaching here, we're either unable to acquire the row, or out of work, or blocked by
// initialization
row = (row + 1) % fHeight; // Move to the next row
this->initAnUninitializedColumn(initCol, threadId); // Initialize something
}
}

View File

@ -1,123 +0,0 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkTaskGroup2D_DEFINED
#define SkTaskGroup2D_DEFINED
#include "SkTaskGroup.h"
#include <mutex>
#include <vector>
// The interface for doing work on a 2D grid with possible initialization on columns.
class SkWorkKernel2D {
public:
// Return false iff the column needs initialization and such initialization is not finished yet.
virtual bool work2D(int row, int column, int thread) = 0;
// Return false if no initialization is done for this colum (e.g., it's already initialized; or
// maybe some other thread is initializing the column).
virtual bool initColumn(int column, int thread) = 0;
virtual ~SkWorkKernel2D() {}
};
// A 2D grid (height rows x width columns) of tasks to be executed on a given executor with
// threadCnt number of threads.
//
// The height (number of rows) is fixed. The width (number of columns) may be dynamically expanded.
//
// The task on row i and column j is abstracted as work2D(i, j, t). Parameter t is the thread id and
// it shouldn't affect the work to be done. It's only used to allow some variables that are not
// thread safe and should be used exclusively by one thread (e.g., thread allocators). We guarantee
// that the task on the same row will be executed in order (i.e., work2D(1, 1, t) is guaranteed to
// finish before calling work2D(1, 2, t)). Tasks in different rows can happen in any order.
//
// There are also width number of init calls, one per column. work2D(i, j, t) may return false if
// column j requires initialization but it's not initialized yet. In that case, a thread t needs to
// call initColumn(j, t) once to unblock all rows that depend on the initialization of column j.
// (Again, t shouldn't affect the init work to be done; it's just for some non-thread-safe
// variables). The init calls have no order requirement so we can call them in any order.
//
// Multiple therads may try to init the same column j at the same time. InitFn is expected to handle
// this gracefully (e.g., let only one thread do the init and return immediately for other threads).
class SkTaskGroup2D {
public:
SkTaskGroup2D(SkWorkKernel2D* kernel, int height, SkExecutor* executor, int threadCnt)
: fKernel(kernel), fHeight(height), fThreadCnt(threadCnt), fIsFinishing(false)
, fWidth(0), fThreadsGroup(new SkTaskGroup(*executor)) {}
virtual ~SkTaskGroup2D() {}
virtual void addColumn(); // Add a new column of tasks.
void start(); // start threads to execute tasks
void finish(); // wait and finish all tasks (no more tasks can be added after calling this)
SK_ALWAYS_INLINE bool isFinishing() const {
return fIsFinishing;
}
protected:
static constexpr int MAX_CACHE_LINE = 64;
// Finish all tasks on the threadId and then return.
virtual void work(int threadId) = 0;
// Initialize a column that needs to be initialized. The parameter initCol is not thread safe
// and should only be exclusively accessed by the working thread which will modify it to the
// column that may need to be initialized next.
void initAnUninitializedColumn(int& initCol, int threadId) {
bool didSomeInit = false;
while (initCol < fWidth && !didSomeInit) {
didSomeInit = fKernel->initColumn(initCol++, threadId);
}
}
SkWorkKernel2D* fKernel;
const int fHeight;
const int fThreadCnt;
std::atomic<bool> fIsFinishing;
std::atomic<int> fWidth;
std::unique_ptr<SkTaskGroup> fThreadsGroup;
};
// A simple spinning task group that assumes height equals threadCnt.
class SkSpinningTaskGroup2D final : public SkTaskGroup2D {
public:
SkSpinningTaskGroup2D(SkWorkKernel2D* kernel, int h, SkExecutor* x, int t)
: SkTaskGroup2D(kernel, h, x, t) {
SkASSERT(h == t); // height must be equal to threadCnt
}
protected:
void work(int threadId) override;
};
class SkFlexibleTaskGroup2D final : public SkTaskGroup2D {
public:
SkFlexibleTaskGroup2D(SkWorkKernel2D* kernel, int h, SkExecutor* x, int t)
: SkTaskGroup2D(kernel, h, x, t), fRowData(h) {}
protected:
void work(int threadId) override;
private:
// alignas(MAX_CACHE_LINE) to avoid false sharing by cache lines
struct alignas(MAX_CACHE_LINE) RowData {
RowData() : fNextColumn(0) {}
int fNextColumn; // next column index to work
std::mutex fMutex; // the mutex for the thread to acquire
};
std::vector<RowData> fRowData;
};
#endif//SkTaskGroup2D_DEFINED

View File

@ -1,250 +0,0 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkThreadedBMPDevice.h"
#include "SkPath.h"
#include "SkSpecialImage.h"
#include "SkTaskGroup.h"
#include "SkVertices.h"
// Calling init(j, k) would initialize the j-th element on k-th thread. It returns false if it's
// already initiailized.
bool SkThreadedBMPDevice::DrawQueue::initColumn(int column, int thread) {
return fElements[column].tryInitOnce(&fThreadAllocs[thread]);
}
// Calling work(i, j, k) would draw j-th element the i-th tile on k-th thead. If the element still
// needs to be initialized, drawFn will return false without drawing.
bool SkThreadedBMPDevice::DrawQueue::work2D(int row, int column, int thread) {
return fElements[column].tryDraw(fDevice->fTileBounds[row], &fThreadAllocs[thread]);
}
void SkThreadedBMPDevice::DrawQueue::reset() {
if (fTasks) {
fTasks->finish();
}
fThreadAllocs.reset(fDevice->fThreadCnt);
fSize = 0;
// using TaskGroup2D = SkSpinningTaskGroup2D;
using TaskGroup2D = SkFlexibleTaskGroup2D;
fTasks.reset(new TaskGroup2D(this, fDevice->fTileCnt, fDevice->fExecutor,
fDevice->fThreadCnt));
fTasks->start();
}
SkThreadedBMPDevice::SkThreadedBMPDevice(const SkBitmap& bitmap,
int tiles,
int threads,
SkExecutor* executor)
: INHERITED(bitmap)
, fTileCnt(tiles)
, fThreadCnt(threads <= 0 ? tiles : threads)
, fQueue(this)
{
if (executor == nullptr) {
fInternalExecutor = SkExecutor::MakeFIFOThreadPool(fThreadCnt);
executor = fInternalExecutor.get();
}
fExecutor = executor;
// Tiling using stripes for now; we'll explore better tiling in the future.
int h = (bitmap.height() + fTileCnt - 1) / SkTMax(fTileCnt, 1);
int w = bitmap.width();
int top = 0;
for(int tid = 0; tid < fTileCnt; ++tid, top += h) {
fTileBounds.push_back(SkIRect::MakeLTRB(0, top, w, top + h));
}
fQueue.reset();
}
void SkThreadedBMPDevice::flush() {
fQueue.reset();
fAlloc.reset();
}
SkThreadedBMPDevice::DrawState::DrawState(SkThreadedBMPDevice* dev) {
// we need fDst to be set, and if we're actually drawing, to dirty the genID
if (!dev->accessPixels(&fDst)) {
// NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
fDst.reset(dev->imageInfo(), nullptr, 0);
}
fMatrix = dev->ctm();
fMatrix.getType(); // make it thread safe
fRC = dev->fRCStack.rc();
}
SkDraw SkThreadedBMPDevice::DrawState::getDraw() const {
SkDraw draw;
draw.fDst = fDst;
draw.fMatrix = &fMatrix;
draw.fRC = &fRC;
return draw;
}
SkThreadedBMPDevice::TileDraw::TileDraw(const DrawState& ds, const SkIRect& tileBounds)
: fTileRC(ds.fRC) {
fDst = ds.fDst;
fMatrix = &ds.fMatrix;
fTileRC.op(tileBounds, SkRegion::kIntersect_Op);
fRC = &fTileRC;
}
static inline SkRect get_fast_bounds(const SkRect& r, const SkPaint& p) {
SkRect result;
if (p.canComputeFastBounds()) {
result = p.computeFastBounds(r, &result);
} else {
result = SkRectPriv::MakeLargest();
}
return result;
}
void SkThreadedBMPDevice::drawPaint(const SkPaint& paint) {
SkRect drawBounds = SkRectPriv::MakeLargest();
fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
TileDraw(ds, tileBounds).drawPaint(paint);
});
}
void SkThreadedBMPDevice::drawPoints(SkCanvas::PointMode mode, size_t count,
const SkPoint pts[], const SkPaint& paint) {
SkPoint* clonedPts = this->cloneArray(pts, count);
SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
TileDraw(ds, tileBounds).drawPoints(mode, count, clonedPts, paint, nullptr);
});
}
void SkThreadedBMPDevice::drawRect(const SkRect& r, const SkPaint& paint) {
SkRect drawBounds = get_fast_bounds(r, paint);
fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
TileDraw(ds, tileBounds).drawRect(r, paint);
});
}
void SkThreadedBMPDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
#ifdef SK_IGNORE_BLURRED_RRECT_OPT
SkPath path;
path.addRRect(rrect);
// call the VIRTUAL version, so any subclasses who do handle drawPath aren't
// required to override drawRRect.
this->drawPath(path, paint, nullptr, false);
#else
SkRect drawBounds = get_fast_bounds(rrect.getBounds(), paint);
fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
TileDraw(ds, tileBounds).drawRRect(rrect, paint);
});
#endif
}
void SkThreadedBMPDevice::drawPath(const SkPath& path, const SkPaint& paint,
const SkMatrix* prePathMatrix, bool pathIsMutable) {
SkRect drawBounds = path.isInverseFillType() ? SkRectPriv::MakeLargest()
: get_fast_bounds(path.getBounds(), paint);
// when path is small, init-once has too much overhead; init-once also can't handle mask filter
if (path.countVerbs() < 4 || paint.getMaskFilter()) {
fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds) {
TileDraw(ds, tileBounds).drawPath(path, paint, prePathMatrix, false);
});
} else {
fQueue.push(drawBounds, [=](SkArenaAlloc* alloc, DrawElement* elem) {
SkInitOnceData data = {alloc, elem};
elem->getDraw().drawPath(path, paint, prePathMatrix, false, false, nullptr, &data);
});
}
}
SkBitmap SkThreadedBMPDevice::snapBitmap(const SkBitmap& bitmap) {
// We can't use bitmap.isImmutable() because it could be temporarily immutable
// TODO(liyuqian): use genID to reduce the copy frequency
SkBitmap snap;
snap.allocPixels(bitmap.info());
bitmap.readPixels(snap.pixmap());
return snap;
}
void SkThreadedBMPDevice::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
const SkRect* dstOrNull, const SkPaint& paint) {
SkRect drawBounds;
SkRect* clonedDstOrNull = nullptr;
if (dstOrNull == nullptr) {
drawBounds = SkRect::MakeWH(bitmap.width(), bitmap.height());
matrix.mapRect(&drawBounds);
} else {
drawBounds = *dstOrNull;
clonedDstOrNull = fAlloc.make<SkRect>(*dstOrNull);
}
SkBitmap snap = this->snapBitmap(bitmap);
fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
SkBitmap local = snap; // bitmap is not thread safe; copy a local one.
TileDraw(ds, tileBounds).drawBitmap(local, matrix, clonedDstOrNull, paint);
});
}
void SkThreadedBMPDevice::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
const SkRect& dst, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) {
// SkBitmapDevice::drawBitmapRect may use shader and drawRect. In that case, we need to snap
// the bitmap here because we won't go into SkThreadedBMPDevice::drawBitmap.
SkBitmap snap = this->snapBitmap(bitmap);
this->SkBitmapDevice::drawBitmapRect(snap, src, dst, paint, constraint);
}
void SkThreadedBMPDevice::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) {
SkRect drawBounds = SkRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
SkBitmap snap = this->snapBitmap(bitmap);
fQueue.push<false>(drawBounds, [=](SkArenaAlloc*, const DrawState& ds,
const SkIRect& tileBounds){
SkBitmap local = snap; // bitmap is not thread safe; copy a local one.
TileDraw(ds, tileBounds).drawSprite(local, x, y, paint);
});
}
void SkThreadedBMPDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[],
int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) {
char* clonedText = this->cloneArray((const char*)text, len);
SkScalar* clonedXpos = this->cloneArray(xpos, paint.countText(text, len) * scalarsPerPos);
SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
SkSurfaceProps prop(SkBitmapDeviceFilteredSurfaceProps(fBitmap, paint, this->surfaceProps())());
fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
TileDraw(ds, tileBounds).drawPosText(clonedText, len, clonedXpos, scalarsPerPos, offset,
paint, &prop);
});
}
void SkThreadedBMPDevice::drawVertices(const SkVertices* vertices, const SkMatrix* bones,
int boneCount, SkBlendMode bmode, const SkPaint& paint) {
const sk_sp<SkVertices> verts = sk_ref_sp(vertices); // retain vertices until flush
SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
// Make a copy of the bone matrices.
SkMatrix* clonedBones = bones ? this->cloneArray(bones, boneCount) : nullptr;
// Make the bone matrices thread-safe.
for (int i = 0; i < boneCount; i ++) {
clonedBones[i].getType();
}
fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
TileDraw(ds, tileBounds).drawVertices(verts->mode(), verts->vertexCount(),
verts->positions(), verts->texCoords(),
verts->colors(), verts->boneIndices(),
verts->boneWeights(), bmode, verts->indices(),
verts->indexCount(), paint, clonedBones, boneCount);
});
}
sk_sp<SkSpecialImage> SkThreadedBMPDevice::snapSpecial() {
this->flush();
return this->makeSpecial(fBitmap);
}

View File

@ -1,225 +0,0 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkThreadedBMPDevice_DEFINED
#define SkThreadedBMPDevice_DEFINED
#include "SkBitmapDevice.h"
#include "SkDraw.h"
#include "SkRectPriv.h"
#include "SkTaskGroup2D.h"
#include <new>
class SkThreadedBMPDevice : public SkBitmapDevice {
public:
// When threads = 0, we make fThreadCnt = tiles. Otherwise fThreadCnt = threads.
// When executor = nullptr, we manages the thread pool. Otherwise, the caller manages it.
SkThreadedBMPDevice(const SkBitmap& bitmap, int tiles, int threads = 0,
SkExecutor* executor = nullptr);
~SkThreadedBMPDevice() override { fQueue.finish(); }
protected:
void drawPaint(const SkPaint& paint) override;
void drawPoints(SkCanvas::PointMode mode, size_t count,
const SkPoint[], const SkPaint& paint) override;
void drawRect(const SkRect& r, const SkPaint& paint) override;
void drawRRect(const SkRRect& rr, const SkPaint& paint) override;
void drawPath(const SkPath&, const SkPaint&, const SkMatrix* prePathMatrix,
bool pathIsMutable) override;
void drawSprite(const SkBitmap&, int x, int y, const SkPaint&) override;
void drawPosText(const void* text, size_t len, const SkScalar pos[],
int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) override;
void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode,
const SkPaint&) override;
void drawBitmap(const SkBitmap&, const SkMatrix&, const SkRect* dstOrNull,
const SkPaint&) override;
void drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) override;
sk_sp<SkSpecialImage> snapSpecial() override;
void flush() override;
private:
// We store DrawState inside DrawElement because inifFn and drawFn both want to use it
struct DrawState {
SkPixmap fDst;
SkMatrix fMatrix;
SkRasterClip fRC;
DrawState() {}
explicit DrawState(SkThreadedBMPDevice* dev);
SkDraw getDraw() const;
};
class TileDraw : public SkDraw {
public: TileDraw(const DrawState& ds, const SkIRect& tileBounds);
private: SkRasterClip fTileRC;
};
class DrawElement {
public:
using InitFn = std::function<void(SkArenaAlloc* threadAlloc, DrawElement* element)>;
using DrawFn = std::function<void(SkArenaAlloc* threadAlloc, const DrawState& ds,
const SkIRect& tileBounds)>;
DrawElement() {}
DrawElement(SkThreadedBMPDevice* device, DrawFn&& drawFn, const SkIRect& drawBounds)
: fInitialized(true)
, fDrawFn(std::move(drawFn))
, fDS(device)
, fDrawBounds(drawBounds) {}
DrawElement(SkThreadedBMPDevice* device, InitFn&& initFn, const SkIRect& drawBounds)
: fInitialized(false)
, fNeedInit(true)
, fInitFn(std::move(initFn))
, fDS(device)
, fDrawBounds(drawBounds) {}
SK_ALWAYS_INLINE bool tryInitOnce(SkArenaAlloc* alloc) {
bool t = true;
// If there are multiple threads reaching this point simutaneously,
// compare_exchange_strong ensures that only one thread can enter the if condition and
// do the initialization.
if (!fInitialized && fNeedInit && fNeedInit.compare_exchange_strong(t, false)) {
#ifdef SK_DEBUG
fDrawFn = 0; // Invalidate fDrawFn
#endif
fInitFn(alloc, this);
fInitialized = true;
SkASSERT(fDrawFn != 0); // Ensure that fInitFn does populate fDrawFn
return true;
}
return false;
}
SK_ALWAYS_INLINE bool tryDraw(const SkIRect& tileBounds, SkArenaAlloc* alloc) {
if (!SkIRect::Intersects(tileBounds, fDrawBounds)) {
return true;
}
if (fInitialized) {
fDrawFn(alloc, fDS, tileBounds);
return true;
}
return false;
}
SkDraw getDraw() const { return fDS.getDraw(); }
void setDrawFn(DrawFn&& fn) { fDrawFn = std::move(fn); }
private:
std::atomic<bool> fInitialized;
std::atomic<bool> fNeedInit;
InitFn fInitFn;
DrawFn fDrawFn;
DrawState fDS;
SkIRect fDrawBounds;
};
class DrawQueue : public SkWorkKernel2D {
public:
static constexpr int MAX_QUEUE_SIZE = 100000;
DrawQueue(SkThreadedBMPDevice* device) : fDevice(device) {}
void reset();
// For ~SkThreadedBMPDevice() to shutdown tasks, we use this instead of reset because reset
// will start new tasks.
void finish() { fTasks->finish(); }
// Push a draw command into the queue. If Fn is DrawFn, we're pushing an element without
// the need of initialization. If Fn is InitFn, we're pushing an element with init-once
// and the InitFn will generate the DrawFn during initialization.
template<bool useCTM = true, typename Fn>
SK_ALWAYS_INLINE void push(const SkRect& rawDrawBounds, Fn&& fn) {
if (fSize == MAX_QUEUE_SIZE) {
this->reset();
}
SkASSERT(fSize < MAX_QUEUE_SIZE);
SkIRect drawBounds = fDevice->transformDrawBounds<useCTM>(rawDrawBounds);
fElements[fSize].~DrawElement(); // release previous resources to prevent memory leak
new (&fElements[fSize++]) DrawElement(fDevice, std::move(fn), drawBounds);
fTasks->addColumn();
}
// SkWorkKernel2D
bool initColumn(int column, int thread) override;
bool work2D(int row, int column, int thread) override;
private:
SkThreadedBMPDevice* fDevice;
std::unique_ptr<SkTaskGroup2D> fTasks;
SkTArray<SkSTArenaAlloc<8 << 10>> fThreadAllocs; // 8k stack size
DrawElement fElements[MAX_QUEUE_SIZE];
int fSize;
};
template<bool useCTM = true>
SkIRect transformDrawBounds(const SkRect& drawBounds) const {
if (drawBounds == SkRectPriv::MakeLargest()) {
return SkRectPriv::MakeILarge();
}
SkRect transformedBounds;
if (useCTM) {
this->ctm().mapRect(&transformedBounds, drawBounds);
} else {
transformedBounds = drawBounds;
}
return transformedBounds.roundOut();
}
template<typename T>
T* cloneArray(const T* array, int count) {
T* clone = fAlloc.makeArrayDefault<T>(count);
memcpy(clone, array, sizeof(T) * count);
return clone;
}
SkBitmap snapBitmap(const SkBitmap& bitmap);
const int fTileCnt;
const int fThreadCnt;
SkTArray<SkIRect> fTileBounds;
/**
* This can either be
* 1. fInternalExecutor.get() which means that we're managing the thread pool's life cycle.
* 2. provided by our caller which means that our caller is managing the threads' life cycle.
* In the 2nd case, fInternalExecutor == nullptr.
*/
SkExecutor* fExecutor = nullptr;
std::unique_ptr<SkExecutor> fInternalExecutor;
SkSTArenaAlloc<8 << 10> fAlloc; // so we can allocate memory that lives until flush
DrawQueue fQueue;
friend struct SkInitOnceData; // to access DrawElement and DrawState
friend class SkDraw; // to access DrawState
typedef SkBitmapDevice INHERITED;
};
// Passed to SkDraw::drawXXX to enable threaded draw with init-once. The goal is to reuse as much
// code as possible from SkDraw. (See SkDraw::drawPath and SkDraw::drawDevPath for an example.)
struct SkInitOnceData {
SkArenaAlloc* fAlloc;
SkThreadedBMPDevice::DrawElement* fElement;
void setEmptyDrawFn() {
fElement->setDrawFn([](SkArenaAlloc* threadAlloc, const SkThreadedBMPDevice::DrawState& ds,
const SkIRect& tileBounds){});
}
};
#endif // SkThreadedBMPDevice_DEFINED

View File

@ -34,7 +34,6 @@
#include "SkSurface.h"
#include "SkTaskGroup.h"
#include "SkTestFontMgr.h"
#include "SkThreadedBMPDevice.h"
#include "SkTo.h"
#include "SvgSlide.h"
#include "Viewer.h"
@ -194,8 +193,6 @@ Viewer::Viewer(int argc, char** argv, void* platformData)
, fOffset{0.0f, 0.0f}
, fGestureDevice(GestureDevice::kNone)
, fPerspectiveMode(kPerspective_Off)
, fTileCnt(0)
, fThreadCnt(0)
{
SkGraphics::Init();
@ -326,40 +323,6 @@ Viewer::Viewer(int argc, char** argv, void* platformData)
#endif
this->setBackend(newBackend);
});
fCommands.addCommand('+', "Threaded Backend", "Increase tile count", [this]() {
fTileCnt++;
if (fThreadCnt == 0) {
this->resetExecutor();
}
this->updateTitle();
fWindow->inval();
});
fCommands.addCommand('-', "Threaded Backend", "Decrease tile count", [this]() {
fTileCnt = SkTMax(0, fTileCnt - 1);
if (fThreadCnt == 0) {
this->resetExecutor();
}
this->updateTitle();
fWindow->inval();
});
fCommands.addCommand('>', "Threaded Backend", "Increase thread count", [this]() {
if (fTileCnt == 0) {
return;
}
fThreadCnt = (fThreadCnt + 1) % fTileCnt;
this->resetExecutor();
this->updateTitle();
fWindow->inval();
});
fCommands.addCommand('<', "Threaded Backend", "Decrease thread count", [this]() {
if (fTileCnt == 0) {
return;
}
fThreadCnt = (fThreadCnt + fTileCnt - 1) % fTileCnt;
this->resetExecutor();
this->updateTitle();
fWindow->inval();
});
fCommands.addCommand('K', "IO", "Save slide to SKP", [this]() {
fSaveToSKP = true;
fWindow->inval();
@ -768,13 +731,6 @@ void Viewer::updateTitle() {
}
paintTitle.done();
if (fTileCnt > 0) {
title.appendf(" T%d", fTileCnt);
if (fThreadCnt > 0) {
title.appendf("/%d", fThreadCnt);
}
}
switch (fColorMode) {
case ColorMode::kLegacy:
title.append(" Legacy 8888");
@ -1140,17 +1096,7 @@ void Viewer::drawSlide(SkCanvas* canvas) {
? SkSurface::MakeRaster(info, &props)
: canvas->makeSurface(info);
SkPixmap offscreenPixmap;
if (fTileCnt > 0 && offscreenSurface->peekPixels(&offscreenPixmap)) {
SkBitmap offscreenBitmap;
offscreenBitmap.installPixels(offscreenPixmap);
threadedCanvas =
skstd::make_unique<SkCanvas>(
sk_make_sp<SkThreadedBMPDevice>(
offscreenBitmap, fTileCnt, fThreadCnt, fExecutor.get()));
slideCanvas = threadedCanvas.get();
} else {
slideCanvas = offscreenSurface->getCanvas();
}
slideCanvas = offscreenSurface->getCanvas();
}
std::unique_ptr<SkCanvas> xformCanvas = nullptr;

View File

@ -106,10 +106,6 @@ private:
SkMatrix computeMatrix();
SkPoint mapEvent(float x, float y);
void resetExecutor() {
fExecutor = SkExecutor::MakeFIFOThreadPool(fThreadCnt == 0 ? fTileCnt : fThreadCnt);
}
sk_app::Window* fWindow;
StatsLayer fStatsLayer;
@ -172,10 +168,6 @@ private:
SkTArray<std::function<void(void)>> fDeferredActions;
int fTileCnt;
int fThreadCnt;
std::unique_ptr<SkExecutor> fExecutor;
SkPaint fPaint;
SkPaintFields fPaintOverrides;
bool fPixelGeometryOverrides = false;