blob: ad9fe04982b125243919152a7b302f990d9fb15e [file] [log] [blame]
Robert Phillips96601082018-05-29 16:13:26 -04001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "tools/DDLTileHelper.h"
Robert Phillips96601082018-05-29 16:13:26 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkCanvas.h"
11#include "include/core/SkDeferredDisplayListRecorder.h"
12#include "include/core/SkPicture.h"
13#include "include/core/SkSurface.h"
14#include "include/core/SkSurfaceCharacterization.h"
15#include "src/core/SkDeferredDisplayListPriv.h"
16#include "src/core/SkTaskGroup.h"
17#include "src/image/SkImage_Gpu.h"
18#include "tools/DDLPromiseImageHelper.h"
Robert Phillips96601082018-05-29 16:13:26 -040019
Robert Phillipsa865a3a2020-02-14 10:49:39 -050020void DDLTileHelper::TileData::init(int id, sk_sp<SkSurface> dstSurface, const SkIRect& clip) {
21 fID = id;
22 fDstSurface = dstSurface;
23 fClip = clip;
24
25 SkSurfaceCharacterization tmp;
26 SkAssertResult(fDstSurface->characterize(&tmp));
27 fCharacterization = tmp.createResized(clip.width(), clip.height());
28 SkASSERT(fCharacterization.isValid());
Robert Phillips96601082018-05-29 16:13:26 -040029}
30
Robert Phillipsa865a3a2020-02-14 10:49:39 -050031DDLTileHelper::TileData::~TileData() {}
32
Robert Phillips96601082018-05-29 16:13:26 -040033void DDLTileHelper::TileData::createTileSpecificSKP(SkData* compressedPictureData,
34 const DDLPromiseImageHelper& helper) {
Robert Phillips7a3197b2018-09-26 21:18:23 +000035 SkASSERT(!fReconstitutedPicture);
Robert Phillips96601082018-05-29 16:13:26 -040036
Robert Phillips7a3197b2018-09-26 21:18:23 +000037 // This is bending the DDLRecorder contract! The promise images in the SKP should be
38 // created by the same recorder used to create the matching DDL.
39 SkDeferredDisplayListRecorder recorder(fCharacterization);
Robert Phillips96601082018-05-29 16:13:26 -040040
Robert Phillips7a3197b2018-09-26 21:18:23 +000041 fReconstitutedPicture = helper.reinflateSKP(&recorder, compressedPictureData, &fPromiseImages);
Robert Phillipse8e2bb12018-09-27 14:26:47 -040042
43 std::unique_ptr<SkDeferredDisplayList> ddl = recorder.detach();
Chris Dalton6b498102019-08-01 14:14:52 -060044 if (ddl->priv().numRenderTasks()) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -040045 // TODO: remove this once skbug.com/8424 is fixed. If the DDL resulting from the
Greg Danielf41b2bd2019-08-22 16:19:24 -040046 // reinflation of the SKPs contains opsTasks that means some image subset operation
Robert Phillipse8e2bb12018-09-27 14:26:47 -040047 // created a draw.
48 fReconstitutedPicture.reset();
49 }
Robert Phillips96601082018-05-29 16:13:26 -040050}
51
52void DDLTileHelper::TileData::createDDL() {
Robert Phillips96601082018-05-29 16:13:26 -040053 SkASSERT(!fDisplayList);
54
Robert Phillips7a3197b2018-09-26 21:18:23 +000055 SkDeferredDisplayListRecorder recorder(fCharacterization);
56
57 // DDL TODO: the DDLRecorder's GrContext isn't initialized until getCanvas is called.
58 // Maybe set it up in the ctor?
59 SkCanvas* subCanvas = recorder.getCanvas();
60
61 // Because we cheated in createTileSpecificSKP and used the wrong DDLRecorder, the GrContext's
62 // stored in fReconstitutedPicture's promise images are incorrect. Patch them with the correct
63 // one now.
64 for (int i = 0; i < fPromiseImages.count(); ++i) {
65 GrContext* newContext = subCanvas->getGrContext();
66
67 if (fPromiseImages[i]->isTextureBacked()) {
Jim Van Verth21bd60d2018-10-12 15:00:20 -040068 SkImage_GpuBase* gpuImage = (SkImage_GpuBase*) fPromiseImages[i].get();
Robert Phillips7a3197b2018-09-26 21:18:23 +000069 gpuImage->resetContext(sk_ref_sp(newContext));
70 }
71 }
Robert Phillips96601082018-05-29 16:13:26 -040072
73 subCanvas->clipRect(SkRect::MakeWH(fClip.width(), fClip.height()));
74 subCanvas->translate(-fClip.fLeft, -fClip.fTop);
75
76 // Note: in this use case we only render a picture to the deferred canvas
77 // but, more generally, clients will use arbitrary draw calls.
Robert Phillipse8e2bb12018-09-27 14:26:47 -040078 if (fReconstitutedPicture) {
79 subCanvas->drawPicture(fReconstitutedPicture);
80 }
Robert Phillips96601082018-05-29 16:13:26 -040081
Robert Phillips7a3197b2018-09-26 21:18:23 +000082 fDisplayList = recorder.detach();
Robert Phillips96601082018-05-29 16:13:26 -040083}
84
Robert Phillipsa865a3a2020-02-14 10:49:39 -050085void DDLTileHelper::TileData::draw(GrContext* context) {
86 SkASSERT(fDisplayList && !fImage);
Robert Phillips96601082018-05-29 16:13:26 -040087
Robert Phillipsa865a3a2020-02-14 10:49:39 -050088 sk_sp<SkSurface> tileSurface = SkSurface::MakeRenderTarget(context, fCharacterization,
89 SkBudgeted::kYes);
90 if (tileSurface) {
91 tileSurface->draw(fDisplayList.get());
92
93 fImage = tileSurface->makeImageSnapshot();
94 }
Robert Phillips96601082018-05-29 16:13:26 -040095}
96
Robert Phillipsa865a3a2020-02-14 10:49:39 -050097// TODO: We should create a single DDL for the composition step and just add replaying it
98// as the last GPU task
99void DDLTileHelper::TileData::compose() {
100 SkASSERT(fDstSurface && fImage);
101
102 SkCanvas* canvas = fDstSurface->getCanvas();
103 canvas->save();
104 canvas->clipRect(SkRect::Make(fClip));
105 canvas->drawImage(std::move(fImage), fClip.fLeft, fClip.fTop);
106 canvas->restore();
Robert Phillips96601082018-05-29 16:13:26 -0400107}
108
109void DDLTileHelper::TileData::reset() {
110 // TODO: when DDLs are re-renderable we don't need to do this
111 fDisplayList = nullptr;
112}
113
114///////////////////////////////////////////////////////////////////////////////////////////////////
115
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500116DDLTileHelper::DDLTileHelper(sk_sp<SkSurface> dstSurface,
117 const SkIRect& viewport,
118 int numDivisions)
Robert Phillips96601082018-05-29 16:13:26 -0400119 : fNumDivisions(numDivisions) {
120 SkASSERT(fNumDivisions > 0);
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500121 fTiles = new TileData[this->numTiles()];
Robert Phillips96601082018-05-29 16:13:26 -0400122
123 int xTileSize = viewport.width()/fNumDivisions;
124 int yTileSize = viewport.height()/fNumDivisions;
125
126 // Create the destination tiles
127 for (int y = 0, yOff = 0; y < fNumDivisions; ++y, yOff += yTileSize) {
128 int ySize = (y < fNumDivisions-1) ? yTileSize : viewport.height()-yOff;
129
130 for (int x = 0, xOff = 0; x < fNumDivisions; ++x, xOff += xTileSize) {
131 int xSize = (x < fNumDivisions-1) ? xTileSize : viewport.width()-xOff;
132
133 SkIRect clip = SkIRect::MakeXYWH(xOff, yOff, xSize, ySize);
134
135 SkASSERT(viewport.contains(clip));
136
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500137 fTiles[y*fNumDivisions+x].init(y*fNumDivisions+x, dstSurface, clip);
Robert Phillips96601082018-05-29 16:13:26 -0400138 }
139 }
140}
141
142void DDLTileHelper::createSKPPerTile(SkData* compressedPictureData,
143 const DDLPromiseImageHelper& helper) {
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500144 for (int i = 0; i < this->numTiles(); ++i) {
Robert Phillips96601082018-05-29 16:13:26 -0400145 fTiles[i].createTileSpecificSKP(compressedPictureData, helper);
146 }
147}
148
149void DDLTileHelper::createDDLsInParallel() {
150#if 1
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500151 SkTaskGroup().batch(this->numTiles(), [&](int i) { fTiles[i].createDDL(); });
Robert Phillipsf18c7562018-06-13 09:01:36 -0400152 SkTaskGroup().wait();
Robert Phillips96601082018-05-29 16:13:26 -0400153#else
154 // Use this code path to debug w/o threads
155 for (int i = 0; i < fTiles.count(); ++i) {
156 fTiles[i].createDDL();
157 }
158#endif
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500159}
Robert Phillips96601082018-05-29 16:13:26 -0400160
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500161// We expect to have more than one recording thread but just one gpu thread
162void DDLTileHelper::kickOffThreadedWork(SkTaskGroup* recordingTaskGroup,
163 SkTaskGroup* gpuTaskGroup,
164 GrContext* gpuThreadContext) {
165 SkASSERT(recordingTaskGroup && gpuTaskGroup && gpuThreadContext);
166
167 for (int i = 0; i < this->numTiles(); ++i) {
168 TileData* tile = &fTiles[i];
169
170 // On a recording thread:
171 // generate the tile's DDL
172 // schedule gpu-thread processing of the DDL
173 // Note: a finer grained approach would be add a scheduling task which would evaluate
174 // which DDLs were ready to be rendered based on their prerequisites
175 recordingTaskGroup->add([tile, gpuTaskGroup, gpuThreadContext]() {
176 tile->createDDL();
177
178 gpuTaskGroup->add([gpuThreadContext, tile]() {
179 // On the gpu thread:
180 // replay the DDL into a surface to make the tile image
181 // compose the tile image into the main canvas
182 tile->draw(gpuThreadContext);
183
184 // TODO: we should actually have a separate DDL that does
185 // the final composition draw
186 tile->compose();
187 });
188 });
189 }
Robert Phillips96601082018-05-29 16:13:26 -0400190}
191
192void DDLTileHelper::drawAllTilesAndFlush(GrContext* context, bool flush) {
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500193 for (int i = 0; i < this->numTiles(); ++i) {
194 fTiles[i].draw(context);
Robert Phillips96601082018-05-29 16:13:26 -0400195 }
196 if (flush) {
197 context->flush();
198 }
199}
200
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500201void DDLTileHelper::composeAllTiles() {
202 for (int i = 0; i < this->numTiles(); ++i) {
203 fTiles[i].compose();
Robert Phillips96601082018-05-29 16:13:26 -0400204 }
205}
206
207void DDLTileHelper::resetAllTiles() {
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500208 for (int i = 0; i < this->numTiles(); ++i) {
Robert Phillips96601082018-05-29 16:13:26 -0400209 fTiles[i].reset();
210 }
211}