1 /*
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 
8 // This is a GPU-backend specific test. It relies on static initializers to work
9 
10 #include "include/core/SkTypes.h"
11 
12 #if defined(SK_GANESH) && defined(SK_VULKAN)
13 #include "include/core/SkAlphaType.h"
14 #include "include/core/SkBitmap.h"
15 #include "include/core/SkCanvas.h"
16 #include "include/core/SkColor.h"
17 #include "include/core/SkColorType.h"
18 #include "include/core/SkDrawable.h"
19 #include "include/core/SkImageInfo.h"
20 #include "include/core/SkMatrix.h"
21 #include "include/core/SkPaint.h"
22 #include "include/core/SkRect.h"
23 #include "include/core/SkRefCnt.h"
24 #include "include/core/SkSamplingOptions.h"
25 #include "include/core/SkString.h"
26 #include "include/core/SkSurface.h"
27 #include "include/gpu/GpuTypes.h"
28 #include "include/gpu/GrBackendDrawableInfo.h"
29 #include "include/gpu/GrDirectContext.h"
30 #include "include/gpu/GrTypes.h"
31 #include "include/gpu/vk/GrVkTypes.h"
32 #include "include/private/chromium/GrVkSecondaryCBDrawContext.h"
33 #include "src/gpu/ganesh/GrDirectContextPriv.h"
34 #include "src/gpu/ganesh/vk/GrVkGpu.h"
35 #include "src/gpu/ganesh/vk/GrVkUtil.h"
36 #include "tests/CtsEnforcement.h"
37 #include "tests/Test.h"
38 
39 #include <vulkan/vulkan_core.h>
40 #include <cstdint>
41 #include <memory>
42 
43 struct GrContextOptions;
44 
45 namespace skgpu { struct VulkanInterface; }
46 
47 using sk_gpu_test::GrContextFactory;
48 
49 static const int DEV_W = 16, DEV_H = 16;
50 
51 class TestDrawable : public SkDrawable {
52 public:
TestDrawable(const skgpu::VulkanInterface * interface,GrDirectContext * dContext,int32_t width,int32_t height)53     TestDrawable(const skgpu::VulkanInterface* interface, GrDirectContext* dContext,
54                  int32_t width, int32_t height)
55             : INHERITED()
56             , fInterface(interface)
57             , fDContext(dContext)
58             , fWidth(width)
59             , fHeight(height) {}
60 
~TestDrawable()61     ~TestDrawable() override {}
62 
63     class DrawHandlerBasic : public GpuDrawHandler {
64     public:
DrawHandlerBasic(const skgpu::VulkanInterface * interface,int32_t width,int32_t height)65         DrawHandlerBasic(const skgpu::VulkanInterface* interface, int32_t width, int32_t height)
66             : INHERITED()
67             , fInterface(interface)
68             , fWidth(width)
69             , fHeight(height) {}
~DrawHandlerBasic()70         ~DrawHandlerBasic() override {}
71 
draw(const GrBackendDrawableInfo & info)72         void draw(const GrBackendDrawableInfo& info) override {
73             GrVkDrawableInfo vkInfo;
74             SkAssertResult(info.getVkDrawableInfo(&vkInfo));
75 
76             // Clear to Red
77             VkClearColorValue vkColor;
78             vkColor.float32[0] = 1.0f; // r
79             vkColor.float32[1] = 0.0f; // g
80             vkColor.float32[2] = 0.0f; // b
81             vkColor.float32[3] = 1.0f; // a
82 
83             // Clear right half of render target
84             VkClearRect clearRect;
85             clearRect.rect.offset = { fWidth / 2, 0 };
86             clearRect.rect.extent = { (uint32_t)fWidth / 2, (uint32_t)fHeight };
87             clearRect.baseArrayLayer = 0;
88             clearRect.layerCount = 1;
89 
90             VkClearAttachment attachment;
91             attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
92             attachment.colorAttachment = vkInfo.fColorAttachmentIndex;
93             attachment.clearValue.color = vkColor;
94 
95             GR_VK_CALL(fInterface, CmdClearAttachments(vkInfo.fSecondaryCommandBuffer,
96                                                        1,
97                                                        &attachment,
98                                                        1,
99                                                        &clearRect));
100             vkInfo.fDrawBounds->offset = { fWidth / 2, 0 };
101             vkInfo.fDrawBounds->extent = { (uint32_t)fWidth / 2, (uint32_t)fHeight };
102         }
103     private:
104         const skgpu::VulkanInterface* fInterface;
105         int32_t                       fWidth;
106         int32_t                       fHeight;
107 
108         using INHERITED = GpuDrawHandler;
109     };
110 
111     typedef void (*DrawProc)(TestDrawable*, const SkMatrix&, const SkIRect&,
112                              const SkImageInfo&, const GrVkDrawableInfo&);
113     typedef void (*SubmitProc)(TestDrawable*);
114 
115     // Exercises the exporting of a secondary command buffer from one context and then importing
116     // it into a second context. We then draw to the secondary command buffer from the second
117     // context.
118     class DrawHandlerImport : public GpuDrawHandler {
119     public:
DrawHandlerImport(TestDrawable * td,DrawProc drawProc,SubmitProc submitProc,const SkMatrix & matrix,const SkIRect & clipBounds,const SkImageInfo & bufferInfo)120         DrawHandlerImport(TestDrawable* td, DrawProc drawProc, SubmitProc submitProc,
121                           const SkMatrix& matrix,
122                           const SkIRect& clipBounds,
123                           const SkImageInfo& bufferInfo)
124             : INHERITED()
125             , fTestDrawable(td)
126             , fDrawProc(drawProc)
127             , fSubmitProc(submitProc)
128             , fMatrix(matrix)
129             , fClipBounds(clipBounds)
130             , fBufferInfo(bufferInfo) {}
~DrawHandlerImport()131         ~DrawHandlerImport() override {
132             fSubmitProc(fTestDrawable);
133         }
134 
draw(const GrBackendDrawableInfo & info)135         void draw(const GrBackendDrawableInfo& info) override {
136             GrVkDrawableInfo vkInfo;
137             SkAssertResult(info.getVkDrawableInfo(&vkInfo));
138 
139             fDrawProc(fTestDrawable, fMatrix, fClipBounds, fBufferInfo, vkInfo);
140         }
141     private:
142         TestDrawable*     fTestDrawable;
143         DrawProc          fDrawProc;
144         SubmitProc        fSubmitProc;
145         const SkMatrix    fMatrix;
146         const SkIRect     fClipBounds;
147         const SkImageInfo fBufferInfo;
148 
149         using INHERITED = GpuDrawHandler;
150     };
151 
152     // Helper function to test drawing to a secondary command buffer that we imported into the
153     // context using a GrVkSecondaryCBDrawContext.
ImportDraw(TestDrawable * td,const SkMatrix & matrix,const SkIRect & clipBounds,const SkImageInfo & bufferInfo,const GrVkDrawableInfo & info)154     static void ImportDraw(TestDrawable* td, const SkMatrix& matrix, const SkIRect& clipBounds,
155                            const SkImageInfo& bufferInfo, const GrVkDrawableInfo& info) {
156         td->fDrawContext = GrVkSecondaryCBDrawContext::Make(td->fDContext, bufferInfo,
157                                                             info, nullptr);
158         if (!td->fDrawContext) {
159             return;
160         }
161 
162         SkCanvas* canvas = td->fDrawContext->getCanvas();
163         canvas->clipRect(SkRect::Make(clipBounds));
164         canvas->setMatrix(matrix);
165 
166         SkIRect rect = SkIRect::MakeXYWH(td->fWidth/2, 0, td->fWidth/4, td->fHeight);
167         SkPaint paint;
168         paint.setColor(SK_ColorRED);
169         canvas->drawIRect(rect, paint);
170 
171         // Draw to an offscreen target so that we end up with a mix of "real" secondary command
172         // buffers and the imported secondary command buffer.
173         sk_sp<SkSurface> surf =
174                 SkSurface::MakeRenderTarget(td->fDContext, skgpu::Budgeted::kYes, bufferInfo);
175         surf->getCanvas()->clear(SK_ColorRED);
176 
177         SkRect dstRect = SkRect::MakeXYWH(3*td->fWidth/4, 0, td->fWidth/4, td->fHeight);
178         SkRect srcRect = SkRect::MakeIWH(td->fWidth/4, td->fHeight);
179         canvas->drawImageRect(surf->makeImageSnapshot(), srcRect, dstRect, SkSamplingOptions(),
180                               &paint, SkCanvas::kStrict_SrcRectConstraint);
181 
182         td->fDrawContext->flush();
183     }
184 
185     // Helper function to test waiting for the imported secondary command buffer to be submitted on
186     // its original context and then cleaning up the GrVkSecondaryCBDrawContext from this context.
ImportSubmitted(TestDrawable * td)187     static void ImportSubmitted(TestDrawable* td) {
188         // Typical use case here would be to create a fence that we submit to the gpu and then wait
189         // on before releasing the GrVkSecondaryCBDrawContext resources. To simulate that for this
190         // test (and since we are running single threaded anyways), we will just force a sync of
191         // the gpu and cpu here.
192         td->fDContext->submit(true);
193 
194         td->fDrawContext->releaseResources();
195         // We release the context here manually to test that we waited long enough before
196         // releasing the GrVkSecondaryCBDrawContext. This simulates when a client is able to delete
197         // the context it used to imported the secondary command buffer. If we had released the
198         // context's resources earlier (before waiting on the gpu above), we would get vulkan
199         // validation layer errors saying we freed some vulkan objects while they were still in use
200         // on the GPU.
201         td->fDContext->releaseResourcesAndAbandonContext();
202     }
203 
204 
onSnapGpuDrawHandler(GrBackendApi backendApi,const SkMatrix & matrix,const SkIRect & clipBounds,const SkImageInfo & bufferInfo)205     std::unique_ptr<GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi,
206                                                          const SkMatrix& matrix,
207                                                          const SkIRect& clipBounds,
208                                                          const SkImageInfo& bufferInfo) override {
209         if (backendApi != GrBackendApi::kVulkan) {
210             return nullptr;
211         }
212         std::unique_ptr<GpuDrawHandler> draw;
213         if (fDContext) {
214             draw = std::make_unique<DrawHandlerImport>(this, ImportDraw, ImportSubmitted, matrix,
215                                                        clipBounds, bufferInfo);
216         } else {
217             draw = std::make_unique<DrawHandlerBasic>(fInterface, fWidth, fHeight);
218         }
219         return draw;
220     }
221 
onGetBounds()222     SkRect onGetBounds() override {
223         return SkRect::MakeLTRB(fWidth / 2, 0, fWidth, fHeight);
224     }
225 
onDraw(SkCanvas *)226     void onDraw(SkCanvas*) override {
227         SkASSERT(false);
228     }
229 
230 private:
231     const skgpu::VulkanInterface*     fInterface;
232     GrDirectContext*                  fDContext;
233     sk_sp<GrVkSecondaryCBDrawContext> fDrawContext;
234     int32_t                           fWidth;
235     int32_t                           fHeight;
236 
237     using INHERITED = SkDrawable;
238 };
239 
draw_drawable_test(skiatest::Reporter * reporter,GrDirectContext * dContext,GrDirectContext * childDContext)240 void draw_drawable_test(skiatest::Reporter* reporter,
241                         GrDirectContext* dContext,
242                         GrDirectContext* childDContext) {
243     GrVkGpu* gpu = static_cast<GrVkGpu*>(dContext->priv().getGpu());
244 
245     const SkImageInfo ii = SkImageInfo::Make(DEV_W, DEV_H, kRGBA_8888_SkColorType,
246                                              kPremul_SkAlphaType);
247     sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(
248             dContext, skgpu::Budgeted::kNo, ii, 0, kTopLeft_GrSurfaceOrigin, nullptr));
249     SkCanvas* canvas = surface->getCanvas();
250     canvas->clear(SK_ColorBLUE);
251 
252     sk_sp<TestDrawable> drawable(new TestDrawable(gpu->vkInterface(), childDContext, DEV_W, DEV_H));
253     canvas->drawDrawable(drawable.get());
254 
255     SkPaint paint;
256     paint.setColor(SK_ColorGREEN);
257     SkIRect rect = SkIRect::MakeLTRB(0, DEV_H/2, DEV_W, DEV_H);
258     canvas->drawIRect(rect, paint);
259 
260     // read pixels
261     SkBitmap bitmap;
262     bitmap.allocPixels(ii);
263     canvas->readPixels(bitmap, 0, 0);
264 
265     const uint32_t* canvasPixels = static_cast<const uint32_t*>(bitmap.getPixels());
266     bool failureFound = false;
267     SkPMColor expectedPixel;
268     for (int cy = 0; cy < DEV_H && !failureFound; ++cy) {
269         for (int cx = 0; cx < DEV_W && !failureFound; ++cx) {
270             SkPMColor canvasPixel = canvasPixels[cy * DEV_W + cx];
271             if (cy < DEV_H / 2) {
272                 if (cx < DEV_W / 2) {
273                     expectedPixel = 0xFFFF0000; // Blue
274                 } else {
275                     expectedPixel = 0xFF0000FF; // Red
276                 }
277             } else {
278                 expectedPixel = 0xFF00FF00; // Green
279             }
280             if (expectedPixel != canvasPixel) {
281                 failureFound = true;
282                 ERRORF(reporter, "Wrong color at %d, %d. Got 0x%08x when we expected 0x%08x",
283                        cx, cy, canvasPixel, expectedPixel);
284             }
285         }
286     }
287 }
288 
DEF_GANESH_TEST_FOR_VULKAN_CONTEXT(VkDrawableTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)289 DEF_GANESH_TEST_FOR_VULKAN_CONTEXT(VkDrawableTest, reporter, ctxInfo, CtsEnforcement::kApiLevel_T) {
290     draw_drawable_test(reporter, ctxInfo.directContext(), nullptr);
291 }
292 
DEF_GANESH_TEST(VkDrawableImportTest,reporter,options,CtsEnforcement::kApiLevel_T)293 DEF_GANESH_TEST(VkDrawableImportTest, reporter, options, CtsEnforcement::kApiLevel_T) {
294     for (int typeInt = 0; typeInt < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++typeInt) {
295         sk_gpu_test::GrContextFactory::ContextType contextType =
296                 (sk_gpu_test::GrContextFactory::ContextType) typeInt;
297         if (contextType != sk_gpu_test::GrContextFactory::kVulkan_ContextType) {
298             continue;
299         }
300         sk_gpu_test::GrContextFactory factory(options);
301         sk_gpu_test::ContextInfo ctxInfo = factory.getContextInfo(contextType);
302         skiatest::ReporterContext ctx(
303                    reporter, SkString(sk_gpu_test::GrContextFactory::ContextTypeName(contextType)));
304         if (ctxInfo.directContext()) {
305             sk_gpu_test::ContextInfo child =
306                     factory.getSharedContextInfo(ctxInfo.directContext(), 0);
307             if (!child.directContext()) {
308                 continue;
309             }
310 
311             draw_drawable_test(reporter, ctxInfo.directContext(), child.directContext());
312         }
313     }
314 }
315 
316 #endif
317