blob: 7063bc71e2991c25e0e236982520fbea7e9b4e47 [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 Phillipsf35dfab2020-03-06 19:13:54 +000055 SkASSERT(!fDisplayList);
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 Phillipsf35dfab2020-03-06 19:13:54 +000061 SkCanvas* subCanvas = 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 Phillipsf35dfab2020-03-06 19:13:54 +000067 GrContext* newContext = subCanvas->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 Phillipsf35dfab2020-03-06 19:13:54 +000075 subCanvas->clipRect(SkRect::MakeWH(fClip.width(), fClip.height()));
76 subCanvas->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 Phillipsf35dfab2020-03-06 19:13:54 +000080 if (fReconstitutedPicture) {
81 subCanvas->drawPicture(fReconstitutedPicture);
82 }
Robert Phillips96601082018-05-29 16:13:26 -040083
Robert Phillips7a3197b2018-09-26 21:18:23 +000084 fDisplayList = recorder.detach();
Robert Phillips96601082018-05-29 16:13:26 -040085}
86
Robert Phillips6eb5cb92020-03-05 12:52:45 -050087void DDLTileHelper::TileData::precompile(GrContext* context) {
88 SkASSERT(fDisplayList);
89
90 SkDeferredDisplayList::ProgramIterator iter(context, fDisplayList.get());
91 for (; !iter.done(); iter.next()) {
92 iter.compile();
93 }
94}
95
Robert Phillipsa865a3a2020-02-14 10:49:39 -050096void DDLTileHelper::TileData::draw(GrContext* context) {
97 SkASSERT(fDisplayList && !fImage);
Robert Phillips96601082018-05-29 16:13:26 -040098
Robert Phillipsa865a3a2020-02-14 10:49:39 -050099 sk_sp<SkSurface> tileSurface = SkSurface::MakeRenderTarget(context, fCharacterization,
100 SkBudgeted::kYes);
101 if (tileSurface) {
102 tileSurface->draw(fDisplayList.get());
103
104 fImage = tileSurface->makeImageSnapshot();
105 }
Robert Phillips96601082018-05-29 16:13:26 -0400106}
107
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500108// TODO: We should create a single DDL for the composition step and just add replaying it
109// as the last GPU task
110void DDLTileHelper::TileData::compose() {
111 SkASSERT(fDstSurface && fImage);
112
113 SkCanvas* canvas = fDstSurface->getCanvas();
114 canvas->save();
115 canvas->clipRect(SkRect::Make(fClip));
Mike Reed3eaed8d2020-02-15 11:07:37 -0500116 canvas->drawImage(fImage, fClip.fLeft, fClip.fTop);
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500117 canvas->restore();
Robert Phillips96601082018-05-29 16:13:26 -0400118}
119
120void DDLTileHelper::TileData::reset() {
121 // TODO: when DDLs are re-renderable we don't need to do this
122 fDisplayList = nullptr;
123}
124
125///////////////////////////////////////////////////////////////////////////////////////////////////
126
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500127DDLTileHelper::DDLTileHelper(sk_sp<SkSurface> dstSurface,
Robert Phillips19f466d2020-02-26 10:27:07 -0500128 const SkSurfaceCharacterization& dstChar,
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500129 const SkIRect& viewport,
130 int numDivisions)
Robert Phillips96601082018-05-29 16:13:26 -0400131 : fNumDivisions(numDivisions) {
132 SkASSERT(fNumDivisions > 0);
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500133 fTiles = new TileData[this->numTiles()];
Robert Phillips96601082018-05-29 16:13:26 -0400134
135 int xTileSize = viewport.width()/fNumDivisions;
136 int yTileSize = viewport.height()/fNumDivisions;
137
138 // Create the destination tiles
139 for (int y = 0, yOff = 0; y < fNumDivisions; ++y, yOff += yTileSize) {
140 int ySize = (y < fNumDivisions-1) ? yTileSize : viewport.height()-yOff;
141
142 for (int x = 0, xOff = 0; x < fNumDivisions; ++x, xOff += xTileSize) {
143 int xSize = (x < fNumDivisions-1) ? xTileSize : viewport.width()-xOff;
144
145 SkIRect clip = SkIRect::MakeXYWH(xOff, yOff, xSize, ySize);
146
147 SkASSERT(viewport.contains(clip));
148
Robert Phillips19f466d2020-02-26 10:27:07 -0500149 fTiles[y*fNumDivisions+x].init(y*fNumDivisions+x, dstSurface, dstChar, clip);
Robert Phillips96601082018-05-29 16:13:26 -0400150 }
151 }
152}
153
154void DDLTileHelper::createSKPPerTile(SkData* compressedPictureData,
155 const DDLPromiseImageHelper& helper) {
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500156 for (int i = 0; i < this->numTiles(); ++i) {
Robert Phillips96601082018-05-29 16:13:26 -0400157 fTiles[i].createTileSpecificSKP(compressedPictureData, helper);
158 }
159}
160
161void DDLTileHelper::createDDLsInParallel() {
162#if 1
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500163 SkTaskGroup().batch(this->numTiles(), [&](int i) { fTiles[i].createDDL(); });
Robert Phillipsf18c7562018-06-13 09:01:36 -0400164 SkTaskGroup().wait();
Robert Phillips96601082018-05-29 16:13:26 -0400165#else
166 // Use this code path to debug w/o threads
Robert Phillipsf35dfab2020-03-06 19:13:54 +0000167 for (int i = 0; i < fTiles.count(); ++i) {
Robert Phillips96601082018-05-29 16:13:26 -0400168 fTiles[i].createDDL();
169 }
170#endif
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500171}
Robert Phillips96601082018-05-29 16:13:26 -0400172
Robert Phillips7b0ed552020-02-20 12:45:19 -0500173// On the gpu thread:
174// precompile any programs
175// replay the DDL into a surface to make the tile image
176// compose the tile image into the main canvas
177static void do_gpu_stuff(GrContext* context, DDLTileHelper::TileData* tile) {
Robert Phillips7b0ed552020-02-20 12:45:19 -0500178
179 // TODO: schedule program compilation as their own tasks
Robert Phillips6eb5cb92020-03-05 12:52:45 -0500180 tile->precompile(context);
Robert Phillips7b0ed552020-02-20 12:45:19 -0500181
182 tile->draw(context);
183
184 // TODO: we should actually have a separate DDL that does
185 // the final composition draw
186 tile->compose();
187}
188
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500189// We expect to have more than one recording thread but just one gpu thread
190void DDLTileHelper::kickOffThreadedWork(SkTaskGroup* recordingTaskGroup,
191 SkTaskGroup* gpuTaskGroup,
192 GrContext* gpuThreadContext) {
193 SkASSERT(recordingTaskGroup && gpuTaskGroup && gpuThreadContext);
194
195 for (int i = 0; i < this->numTiles(); ++i) {
196 TileData* tile = &fTiles[i];
197
198 // On a recording thread:
199 // generate the tile's DDL
200 // schedule gpu-thread processing of the DDL
201 // Note: a finer grained approach would be add a scheduling task which would evaluate
202 // which DDLs were ready to be rendered based on their prerequisites
203 recordingTaskGroup->add([tile, gpuTaskGroup, gpuThreadContext]() {
204 tile->createDDL();
205
206 gpuTaskGroup->add([gpuThreadContext, tile]() {
Robert Phillips7b0ed552020-02-20 12:45:19 -0500207 do_gpu_stuff(gpuThreadContext, tile);
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500208 });
209 });
210 }
Robert Phillips96601082018-05-29 16:13:26 -0400211}
212
Robert Phillips6eb5cb92020-03-05 12:52:45 -0500213void DDLTileHelper::precompileAndDrawAllTiles(GrContext* context) {
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500214 for (int i = 0; i < this->numTiles(); ++i) {
Robert Phillips6eb5cb92020-03-05 12:52:45 -0500215 fTiles[i].precompile(context);
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500216 fTiles[i].draw(context);
Robert Phillips96601082018-05-29 16:13:26 -0400217 }
Robert Phillips96601082018-05-29 16:13:26 -0400218}
219
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500220void DDLTileHelper::composeAllTiles() {
221 for (int i = 0; i < this->numTiles(); ++i) {
222 fTiles[i].compose();
Robert Phillips96601082018-05-29 16:13:26 -0400223 }
224}
225
226void DDLTileHelper::resetAllTiles() {
Robert Phillipsa865a3a2020-02-14 10:49:39 -0500227 for (int i = 0; i < this->numTiles(); ++i) {
Robert Phillips96601082018-05-29 16:13:26 -0400228 fTiles[i].reset();
229 }
230}