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