• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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