blob: d5d00453236bf47d58f9a3c48675b5acae2b2277 [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"
Robert Phillips7b0ed552020-02-20 12:45:19 -050017#include "src/gpu/GrContextPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050018#include "src/image/SkImage_Gpu.h"
19#include "tools/DDLPromiseImageHelper.h"
Robert Phillips96601082018-05-29 16:13:26 -040020
Robert Phillips19f466d2020-02-26 10:27:07 -050021void DDLTileHelper::TileData::init(int id,
22 sk_sp<SkSurface> dstSurface,
23 const SkSurfaceCharacterization& dstSurfaceCharacterization,
24 const SkIRect& clip) {
Robert Phillipsa865a3a2020-02-14 10:49:39 -050025 fID = id;
26 fDstSurface = dstSurface;
27 fClip = clip;
28
Robert Phillips19f466d2020-02-26 10:27:07 -050029 fCharacterization = dstSurfaceCharacterization.createResized(clip.width(), clip.height());
Robert Phillipsa865a3a2020-02-14 10:49:39 -050030 SkASSERT(fCharacterization.isValid());
Robert Phillips96601082018-05-29 16:13:26 -040031}
32
Robert Phillipsa865a3a2020-02-14 10:49:39 -050033DDLTileHelper::TileData::~TileData() {}
34
Robert Phillips96601082018-05-29 16:13:26 -040035void DDLTileHelper::TileData::createTileSpecificSKP(SkData* compressedPictureData,
36 const DDLPromiseImageHelper& helper) {
Robert Phillips7a3197b2018-09-26 21:18:23 +000037 SkASSERT(!fReconstitutedPicture);
Robert Phillips96601082018-05-29 16:13:26 -040038
Robert Phillips7a3197b2018-09-26 21:18:23 +000039 // This is bending the DDLRecorder contract! The promise images in the SKP should be
40 // created by the same recorder used to create the matching DDL.
41 SkDeferredDisplayListRecorder recorder(fCharacterization);
Robert Phillips96601082018-05-29 16:13:26 -040042
Robert Phillips7a3197b2018-09-26 21:18:23 +000043 fReconstitutedPicture = helper.reinflateSKP(&recorder, compressedPictureData, &fPromiseImages);
Robert Phillipse8e2bb12018-09-27 14:26:47 -040044
45 std::unique_ptr<SkDeferredDisplayList> ddl = recorder.detach();
Chris Dalton6b498102019-08-01 14:14:52 -060046 if (ddl->priv().numRenderTasks()) {
Robert Phillipse8e2bb12018-09-27 14:26:47 -040047 // TODO: remove this once skbug.com/8424 is fixed. If the DDL resulting from the
Greg Danielf41b2bd2019-08-22 16:19:24 -040048 // reinflation of the SKPs contains opsTasks that means some image subset operation
Robert Phillipse8e2bb12018-09-27 14:26:47 -040049 // created a draw.
50 fReconstitutedPicture.reset();
51 }
Robert Phillips96601082018-05-29 16:13:26 -040052}
53
54void DDLTileHelper::TileData::createDDL() {
Robert Phillips24a8e9e2020-03-06 20:26:28 +000055 SkASSERT(!fDisplayList && fReconstitutedPicture);
Robert Phillips96601082018-05-29 16:13:26 -040056
Robert Phillips7a3197b2018-09-26 21:18:23 +000057 SkDeferredDisplayListRecorder recorder(fCharacterization);
58
59 // DDL TODO: the DDLRecorder's GrContext isn't initialized until getCanvas is called.
60 // Maybe set it up in the ctor?
Robert Phillips24a8e9e2020-03-06 20:26:28 +000061 SkCanvas* recordingCanvas = recorder.getCanvas();
Robert Phillips7a3197b2018-09-26 21:18:23 +000062
63 // Because we cheated in createTileSpecificSKP and used the wrong DDLRecorder, the GrContext's
64 // stored in fReconstitutedPicture's promise images are incorrect. Patch them with the correct
65 // one now.
66 for (int i = 0; i < fPromiseImages.count(); ++i) {
Robert Phillips24a8e9e2020-03-06 20:26:28 +000067 GrContext* newContext = recordingCanvas->getGrContext();
Robert Phillips7a3197b2018-09-26 21:18:23 +000068
69 if (fPromiseImages[i]->isTextureBacked()) {
Jim Van Verth21bd60d2018-10-12 15:00:20 -040070 SkImage_GpuBase* gpuImage = (SkImage_GpuBase*) fPromiseImages[i].get();
Robert Phillips7a3197b2018-09-26 21:18:23 +000071 gpuImage->resetContext(sk_ref_sp(newContext));
72 }
73 }
Robert Phillips96601082018-05-29 16:13:26 -040074
Robert Phillips24a8e9e2020-03-06 20:26:28 +000075 recordingCanvas->clipRect(SkRect::MakeWH(fClip.width(), fClip.height()));
76 recordingCanvas->translate(-fClip.fLeft, -fClip.fTop);
Robert Phillips96601082018-05-29 16:13:26 -040077
78 // Note: in this use case we only render a picture to the deferred canvas
79 // but, more generally, clients will use arbitrary draw calls.
Robert Phillips24a8e9e2020-03-06 20:26:28 +000080 recordingCanvas->drawPicture(fReconstitutedPicture);
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 Phillips6eb5cb92020-03-05 12:52:45 -050085void DDLTileHelper::TileData::precompile(GrContext* context) {
86 SkASSERT(fDisplayList);
87
88 SkDeferredDisplayList::ProgramIterator iter(context, fDisplayList.get());
89 for (; !iter.done(); iter.next()) {
90 iter.compile();
91 }
92}
93
Robert Phillips24a8e9e2020-03-06 20:26:28 +000094void DDLTileHelper::TileData::drawSKPDirectly(GrContext* context) {
95 SkASSERT(!fDisplayList && !fImage && fReconstitutedPicture);
96
97 sk_sp<SkSurface> tileSurface = SkSurface::MakeRenderTarget(context, fCharacterization,
98 SkBudgeted::kYes);
99 if (tileSurface) {
100 SkCanvas* tileCanvas = tileSurface->getCanvas();
101
102 tileCanvas->clipRect(SkRect::MakeWH(fClip.width(), fClip.height()));
103 tileCanvas->translate(-fClip.fLeft, -fClip.fTop);
104
105 tileCanvas->drawPicture(fReconstitutedPicture);
106
107 fImage = tileSurface->makeImageSnapshot();
108 }
109}
110
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500111void DDLTileHelper::TileData::draw(GrContext* context) {
112 SkASSERT(fDisplayList && !fImage);
Robert Phillips96601082018-05-29 16:13:26 -0400113
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500114 sk_sp<SkSurface> tileSurface = SkSurface::MakeRenderTarget(context, fCharacterization,
115 SkBudgeted::kYes);
116 if (tileSurface) {
117 tileSurface->draw(fDisplayList.get());
118
119 fImage = tileSurface->makeImageSnapshot();
120 }
Robert Phillips96601082018-05-29 16:13:26 -0400121}
122
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500123// TODO: We should create a single DDL for the composition step and just add replaying it
124// as the last GPU task
125void DDLTileHelper::TileData::compose() {
126 SkASSERT(fDstSurface && fImage);
127
128 SkCanvas* canvas = fDstSurface->getCanvas();
129 canvas->save();
130 canvas->clipRect(SkRect::Make(fClip));
Mike Reed3eaed8d2020-02-15 11:07:37 -0500131 canvas->drawImage(fImage, fClip.fLeft, fClip.fTop);
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500132 canvas->restore();
Robert Phillips96601082018-05-29 16:13:26 -0400133}
134
135void DDLTileHelper::TileData::reset() {
136 // TODO: when DDLs are re-renderable we don't need to do this
137 fDisplayList = nullptr;
Robert Phillips24a8e9e2020-03-06 20:26:28 +0000138 fImage = nullptr;
Robert Phillips96601082018-05-29 16:13:26 -0400139}
140
141///////////////////////////////////////////////////////////////////////////////////////////////////
142
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500143DDLTileHelper::DDLTileHelper(sk_sp<SkSurface> dstSurface,
Robert Phillips19f466d2020-02-26 10:27:07 -0500144 const SkSurfaceCharacterization& dstChar,
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500145 const SkIRect& viewport,
146 int numDivisions)
Robert Phillips96601082018-05-29 16:13:26 -0400147 : fNumDivisions(numDivisions) {
148 SkASSERT(fNumDivisions > 0);
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500149 fTiles = new TileData[this->numTiles()];
Robert Phillips96601082018-05-29 16:13:26 -0400150
151 int xTileSize = viewport.width()/fNumDivisions;
152 int yTileSize = viewport.height()/fNumDivisions;
153
154 // Create the destination tiles
155 for (int y = 0, yOff = 0; y < fNumDivisions; ++y, yOff += yTileSize) {
156 int ySize = (y < fNumDivisions-1) ? yTileSize : viewport.height()-yOff;
157
158 for (int x = 0, xOff = 0; x < fNumDivisions; ++x, xOff += xTileSize) {
159 int xSize = (x < fNumDivisions-1) ? xTileSize : viewport.width()-xOff;
160
161 SkIRect clip = SkIRect::MakeXYWH(xOff, yOff, xSize, ySize);
162
163 SkASSERT(viewport.contains(clip));
164
Robert Phillips19f466d2020-02-26 10:27:07 -0500165 fTiles[y*fNumDivisions+x].init(y*fNumDivisions+x, dstSurface, dstChar, clip);
Robert Phillips96601082018-05-29 16:13:26 -0400166 }
167 }
168}
169
170void DDLTileHelper::createSKPPerTile(SkData* compressedPictureData,
171 const DDLPromiseImageHelper& helper) {
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500172 for (int i = 0; i < this->numTiles(); ++i) {
Robert Phillips96601082018-05-29 16:13:26 -0400173 fTiles[i].createTileSpecificSKP(compressedPictureData, helper);
174 }
175}
176
177void DDLTileHelper::createDDLsInParallel() {
178#if 1
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500179 SkTaskGroup().batch(this->numTiles(), [&](int i) { fTiles[i].createDDL(); });
Robert Phillipsf18c7562018-06-13 09:01:36 -0400180 SkTaskGroup().wait();
Robert Phillips96601082018-05-29 16:13:26 -0400181#else
182 // Use this code path to debug w/o threads
Robert Phillips24a8e9e2020-03-06 20:26:28 +0000183 for (int i = 0; i < this->numTiles(); ++i) {
Robert Phillips96601082018-05-29 16:13:26 -0400184 fTiles[i].createDDL();
185 }
186#endif
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500187}
Robert Phillips96601082018-05-29 16:13:26 -0400188
Robert Phillips7b0ed552020-02-20 12:45:19 -0500189// On the gpu thread:
190// precompile any programs
191// replay the DDL into a surface to make the tile image
192// compose the tile image into the main canvas
193static void do_gpu_stuff(GrContext* context, DDLTileHelper::TileData* tile) {
Robert Phillips7b0ed552020-02-20 12:45:19 -0500194
195 // TODO: schedule program compilation as their own tasks
Robert Phillips6eb5cb92020-03-05 12:52:45 -0500196 tile->precompile(context);
Robert Phillips7b0ed552020-02-20 12:45:19 -0500197
198 tile->draw(context);
199
200 // TODO: we should actually have a separate DDL that does
201 // the final composition draw
202 tile->compose();
203}
204
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500205// We expect to have more than one recording thread but just one gpu thread
206void DDLTileHelper::kickOffThreadedWork(SkTaskGroup* recordingTaskGroup,
207 SkTaskGroup* gpuTaskGroup,
208 GrContext* gpuThreadContext) {
209 SkASSERT(recordingTaskGroup && gpuTaskGroup && gpuThreadContext);
210
211 for (int i = 0; i < this->numTiles(); ++i) {
212 TileData* tile = &fTiles[i];
213
214 // On a recording thread:
215 // generate the tile's DDL
216 // schedule gpu-thread processing of the DDL
217 // Note: a finer grained approach would be add a scheduling task which would evaluate
218 // which DDLs were ready to be rendered based on their prerequisites
219 recordingTaskGroup->add([tile, gpuTaskGroup, gpuThreadContext]() {
220 tile->createDDL();
221
222 gpuTaskGroup->add([gpuThreadContext, tile]() {
Robert Phillips7b0ed552020-02-20 12:45:19 -0500223 do_gpu_stuff(gpuThreadContext, tile);
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500224 });
225 });
226 }
Robert Phillips96601082018-05-29 16:13:26 -0400227}
228
Robert Phillips6eb5cb92020-03-05 12:52:45 -0500229void DDLTileHelper::precompileAndDrawAllTiles(GrContext* context) {
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500230 for (int i = 0; i < this->numTiles(); ++i) {
Robert Phillips6eb5cb92020-03-05 12:52:45 -0500231 fTiles[i].precompile(context);
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500232 fTiles[i].draw(context);
Robert Phillips96601082018-05-29 16:13:26 -0400233 }
Robert Phillips96601082018-05-29 16:13:26 -0400234}
235
Robert Phillips24a8e9e2020-03-06 20:26:28 +0000236void DDLTileHelper::interleaveDDLCreationAndDraw(GrContext* context) {
237 for (int i = 0; i < this->numTiles(); ++i) {
238 fTiles[i].createDDL();
239 fTiles[i].draw(context);
240 }
241}
242
243void DDLTileHelper::drawAllTilesDirectly(GrContext* context) {
244 for (int i = 0; i < this->numTiles(); ++i) {
245 fTiles[i].drawSKPDirectly(context);
246 }
247}
248
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500249void DDLTileHelper::composeAllTiles() {
250 for (int i = 0; i < this->numTiles(); ++i) {
251 fTiles[i].compose();
Robert Phillips96601082018-05-29 16:13:26 -0400252 }
253}
254
255void DDLTileHelper::resetAllTiles() {
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500256 for (int i = 0; i < this->numTiles(); ++i) {
Robert Phillips96601082018-05-29 16:13:26 -0400257 fTiles[i].reset();
258 }
259}