1 /*
2 * Copyright 2019 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
8 #include "tests/Test.h"
9
10 #include <chrono>
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkSurface.h"
14 #include "include/gpu/GrDirectContext.h"
15 #include "src/gpu/GrDirectContextPriv.h"
16 #include "src/gpu/GrGpu.h"
17 #include "tools/gpu/ManagedBackendTexture.h"
18
19 using namespace sk_gpu_test;
20
testing_finished_proc(void * ctx)21 static void testing_finished_proc(void* ctx) {
22 int* count = (int*)ctx;
23 *count += 1;
24 }
25
busy_wait_for_callback(int * count,int expectedValue,GrDirectContext * dContext,skiatest::Reporter * reporter)26 static void busy_wait_for_callback(int* count, int expectedValue, GrDirectContext* dContext,
27 skiatest::Reporter* reporter) {
28 // Busy waiting should detect that the work is done.
29 auto begin = std::chrono::steady_clock::now();
30 auto end = begin;
31 do {
32 dContext->checkAsyncWorkCompletion();
33 end = std::chrono::steady_clock::now();
34 } while (*count != expectedValue && (end - begin) < std::chrono::seconds(1));
35 if (*count != expectedValue) {
36 ERRORF(reporter, "Expected count failed to reach %d within 1 second of busy waiting.",
37 expectedValue);
38 }
39 }
40
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(FlushFinishedProcTest,reporter,ctxInfo)41 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(FlushFinishedProcTest, reporter, ctxInfo) {
42 auto dContext = ctxInfo.directContext();
43
44 SkImageInfo info =
45 SkImageInfo::Make(8, 8, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
46 sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(dContext, SkBudgeted::kNo, info);
47 SkCanvas* canvas = surface->getCanvas();
48
49 canvas->clear(SK_ColorGREEN);
50 auto image = surface->makeImageSnapshot();
51
52 dContext->flush();
53 dContext->submit(true);
54
55 int count = 0;
56
57 GrFlushInfo flushInfoFinishedProc;
58 flushInfoFinishedProc.fFinishedProc = testing_finished_proc;
59 flushInfoFinishedProc.fFinishedContext = (void*)&count;
60 // There is no work on the surface so flushing may immediately call the finished proc.
61 surface->flush(flushInfoFinishedProc);
62 dContext->submit();
63 REPORTER_ASSERT(reporter, count == 0 || count == 1);
64 // Busy waiting should detect that the work is done.
65 busy_wait_for_callback(&count, 1, dContext, reporter);
66
67 canvas->clear(SK_ColorRED);
68
69 surface->flush(flushInfoFinishedProc);
70 dContext->submit();
71
72 bool fenceSupport = dContext->priv().caps()->fenceSyncSupport();
73 bool expectAsyncCallback =
74 dContext->backend() == GrBackendApi::kVulkan ||
75 ((dContext->backend() == GrBackendApi::kOpenGL) && fenceSupport) ||
76 ((dContext->backend() == GrBackendApi::kMetal) && fenceSupport) ||
77 dContext->backend() == GrBackendApi::kDawn ||
78 dContext->backend() == GrBackendApi::kDirect3D;
79 if (expectAsyncCallback) {
80 // On Vulkan the command buffer we just submitted may or may not have finished immediately
81 // so the finish proc may not have been called.
82 REPORTER_ASSERT(reporter, count == 1 || count == 2);
83 } else {
84 REPORTER_ASSERT(reporter, count == 2);
85 }
86 dContext->flush();
87 dContext->submit(true);
88 REPORTER_ASSERT(reporter, count == 2);
89
90 // Test flushing via the SkImage
91 canvas->drawImage(image, 0, 0);
92 image->flush(dContext, flushInfoFinishedProc);
93 dContext->submit();
94 if (expectAsyncCallback) {
95 // On Vulkan the command buffer we just submitted may or may not have finished immediately
96 // so the finish proc may not have been called.
97 REPORTER_ASSERT(reporter, count == 2 || count == 3);
98 } else {
99 REPORTER_ASSERT(reporter, count == 3);
100 }
101 dContext->flush();
102 dContext->submit(true);
103 REPORTER_ASSERT(reporter, count == 3);
104
105 // Test flushing via the GrDirectContext
106 canvas->clear(SK_ColorBLUE);
107 dContext->flush(flushInfoFinishedProc);
108 dContext->submit();
109 if (expectAsyncCallback) {
110 // On Vulkan the command buffer we just submitted may or may not have finished immediately
111 // so the finish proc may not have been called.
112 REPORTER_ASSERT(reporter, count == 3 || count == 4);
113 } else {
114 REPORTER_ASSERT(reporter, count == 4);
115 }
116 dContext->flush();
117 dContext->submit(true);
118 REPORTER_ASSERT(reporter, count == 4);
119
120 // There is no work on the surface so flushing may immediately call the finished proc.
121 dContext->flush(flushInfoFinishedProc);
122 dContext->submit();
123 REPORTER_ASSERT(reporter, count == 4 || count == 5);
124 busy_wait_for_callback(&count, 5, dContext, reporter);
125
126 count = 0;
127 int count2 = 0;
128 canvas->clear(SK_ColorGREEN);
129 surface->flush(flushInfoFinishedProc);
130 dContext->submit();
131 // There is no work to be flushed here so this will return immediately, but make sure the
132 // finished call from this proc isn't called till the previous surface flush also is finished.
133 flushInfoFinishedProc.fFinishedContext = (void*)&count2;
134 dContext->flush(flushInfoFinishedProc);
135 dContext->submit();
136 REPORTER_ASSERT(reporter, count <= 1 && count2 <= count);
137
138 dContext->flush();
139 dContext->submit(true);
140
141 REPORTER_ASSERT(reporter, count == 1);
142 REPORTER_ASSERT(reporter, count == count2);
143 }
144
145
abandon_context(void * context)146 static void abandon_context(void* context) {
147 ((GrDirectContext*)context)->abandonContext();
148 }
149
async_callback(void * c,std::unique_ptr<const SkImage::AsyncReadResult> result)150 static void async_callback(void* c, std::unique_ptr<const SkImage::AsyncReadResult> result) {
151 // We don't actually care about the results so just drop them without doing anything.
152 };
153
154 // This test checks that calls to the async read pixels callback can safely be made even if the
155 // context has been abandoned previously. Specifically there was a bug where the client buffer
156 // manager stored on the GrDirectContext was accessed in the async callback after it was deleted.
157 // This bug is detected on ASAN bots running non GL backends (GL isn't affected purely based on
158 // how we call finish callbacks during abandon).
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(FinishedAsyncProcWhenAbandonedTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)159 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(FinishedAsyncProcWhenAbandonedTest,
160 reporter,
161 ctxInfo,
162 CtsEnforcement::kApiLevel_T) {
163 auto dContext = ctxInfo.directContext();
164
165 SkImageInfo info =
166 SkImageInfo::Make(8, 8, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
167
168 auto mbet = sk_gpu_test::ManagedBackendTexture::MakeFromInfo(dContext,
169 info,
170 GrMipmapped::kNo,
171 GrRenderable::kYes);
172 if (!mbet) {
173 return;
174 }
175
176 auto surface = SkSurface::MakeFromBackendTexture(
177 dContext,
178 mbet->texture(),
179 kTopLeft_GrSurfaceOrigin,
180 /*sample count*/ 1,
181 kRGBA_8888_SkColorType,
182 /*color space*/ nullptr,
183 /*surface props*/ nullptr,
184 sk_gpu_test::ManagedBackendTexture::ReleaseProc,
185 mbet->releaseContext(nullptr, nullptr));
186
187 if (!surface) {
188 return;
189 }
190 SkCanvas* canvas = surface->getCanvas();
191 canvas->clear(SK_ColorGREEN);
192
193 // To trigger bug we must have a finish callback that abanonds the context before an asyc
194 // read callbck on the same command buffer. So we add the abandon callback first and flush
195 // then add the asyc to enforce this order.
196 GrFlushInfo flushInfo;
197 flushInfo.fFinishedProc = abandon_context;
198 flushInfo.fFinishedContext = dContext;
199
200 dContext->flush(flushInfo);
201
202 surface->asyncRescaleAndReadPixels(info,
203 SkIRect::MakeWH(8, 8),
204 SkImage::RescaleGamma::kSrc,
205 SkImage::RescaleMode::kNearest,
206 async_callback,
207 nullptr);
208
209 surface.reset();
210
211 dContext->flushAndSubmit(/*syncCpu=*/true);
212 }
213