blob: 3ff260737aa97068e1b2147a2087e59cd62a930f [file] [log] [blame]
Greg Daniel64cc9aa2018-10-19 13:54:56 -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
8// This is a GPU-backend specific test. It relies on static intializers to work
9
10#include "SkTypes.h"
11
12#if SK_SUPPORT_GPU && defined(SK_VULKAN)
13
Greg Daniel54bfb182018-11-20 17:12:36 -050014#include "vk/GrVkVulkan.h"
15
Greg Daniel64cc9aa2018-10-19 13:54:56 -040016#include "GrBackendDrawableInfo.h"
17#include "GrContextFactory.h"
18#include "GrContextPriv.h"
19#include "SkDrawable.h"
20#include "SkSurface.h"
21#include "Test.h"
22#include "vk/GrVkGpu.h"
23#include "vk/GrVkInterface.h"
24#include "vk/GrVkMemory.h"
Greg Daniel070cbaf2019-01-03 17:35:54 -050025#include "vk/GrVkSecondaryCBDrawContext.h"
Greg Daniel64cc9aa2018-10-19 13:54:56 -040026#include "vk/GrVkUtil.h"
27
28using sk_gpu_test::GrContextFactory;
29
30static const int DEV_W = 16, DEV_H = 16;
31
32class TestDrawable : public SkDrawable {
33public:
Greg Daniel070cbaf2019-01-03 17:35:54 -050034 TestDrawable(const GrVkInterface* interface, GrContext* context, int32_t width, int32_t height)
Greg Daniel64cc9aa2018-10-19 13:54:56 -040035 : INHERITED()
36 , fInterface(interface)
Greg Daniel070cbaf2019-01-03 17:35:54 -050037 , fContext(context)
Greg Daniel64cc9aa2018-10-19 13:54:56 -040038 , fWidth(width)
39 , fHeight(height) {}
40
41 ~TestDrawable() override {}
42
Greg Daniel070cbaf2019-01-03 17:35:54 -050043 class DrawHandlerBasic : public GpuDrawHandler {
Greg Daniel64cc9aa2018-10-19 13:54:56 -040044 public:
Greg Daniel070cbaf2019-01-03 17:35:54 -050045 DrawHandlerBasic(const GrVkInterface* interface, int32_t width, int32_t height)
Greg Daniel64cc9aa2018-10-19 13:54:56 -040046 : INHERITED()
47 , fInterface(interface)
48 , fWidth(width)
49 , fHeight(height) {}
Greg Daniel070cbaf2019-01-03 17:35:54 -050050 ~DrawHandlerBasic() override {}
Greg Daniel64cc9aa2018-10-19 13:54:56 -040051
52 void draw(const GrBackendDrawableInfo& info) override {
53 GrVkDrawableInfo vkInfo;
54 SkAssertResult(info.getVkDrawableInfo(&vkInfo));
55
56 // Clear to Red
57 VkClearColorValue vkColor;
58 vkColor.float32[0] = 1.0f; // r
59 vkColor.float32[1] = 0.0f; // g
60 vkColor.float32[2] = 0.0f; // b
61 vkColor.float32[3] = 1.0f; // a
62
63 // Clear right half of render target
64 VkClearRect clearRect;
65 clearRect.rect.offset = { fWidth / 2, 0 };
66 clearRect.rect.extent = { (uint32_t)fWidth / 2, (uint32_t)fHeight };
67 clearRect.baseArrayLayer = 0;
68 clearRect.layerCount = 1;
69
70 VkClearAttachment attachment;
71 attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
Greg Danielb353eeb2018-12-05 11:01:58 -050072 attachment.colorAttachment = vkInfo.fColorAttachmentIndex;
Greg Daniel64cc9aa2018-10-19 13:54:56 -040073 attachment.clearValue.color = vkColor;
74
75 GR_VK_CALL(fInterface, CmdClearAttachments(vkInfo.fSecondaryCommandBuffer,
76 1,
77 &attachment,
78 1,
79 &clearRect));
80 vkInfo.fDrawBounds->offset = { fWidth / 2, 0 };
81 vkInfo.fDrawBounds->extent = { (uint32_t)fWidth / 2, (uint32_t)fHeight };
82 }
83 private:
84 const GrVkInterface* fInterface;
85 int32_t fWidth;
86 int32_t fHeight;
87
88 typedef GpuDrawHandler INHERITED;
89 };
90
Greg Daniel070cbaf2019-01-03 17:35:54 -050091 typedef void (*DrawProc)(TestDrawable*, const GrVkDrawableInfo&);
92 typedef void (*SubmitProc)(TestDrawable*);
93
94 // Exercises the exporting of a secondary command buffer from one GrContext and then importing
95 // it into a second GrContext. We then draw to the secondary command buffer from the second
96 // GrContext.
97 class DrawHandlerImport : public GpuDrawHandler {
98 public:
99 DrawHandlerImport(TestDrawable* td, DrawProc drawProc, SubmitProc submitProc)
100 : INHERITED()
101 , fTestDrawable(td)
102 , fDrawProc(drawProc)
103 , fSubmitProc(submitProc) {}
104 ~DrawHandlerImport() override {
105 fSubmitProc(fTestDrawable);
106 }
107
108 void draw(const GrBackendDrawableInfo& info) override {
109 GrVkDrawableInfo vkInfo;
110 SkAssertResult(info.getVkDrawableInfo(&vkInfo));
111
112 fDrawProc(fTestDrawable, vkInfo);
113 }
114 private:
115 TestDrawable* fTestDrawable;
116 DrawProc fDrawProc;
117 SubmitProc fSubmitProc;
118
119 typedef GpuDrawHandler INHERITED;
120 };
121
122 // Helper function to test drawing to a secondary command buffer that we imported into the
123 // GrContext using a GrVkSecondaryCBDrawContext.
124 static void ImportDraw(TestDrawable* td, const GrVkDrawableInfo& info) {
125 SkImageInfo imageInfo = SkImageInfo::Make(td->fWidth, td->fHeight, kRGBA_8888_SkColorType,
126 kPremul_SkAlphaType);
127
128 td->fDrawContext = GrVkSecondaryCBDrawContext::Make(td->fContext, imageInfo, info, nullptr);
129 if (!td->fDrawContext) {
130 return;
131 }
132
133 SkCanvas* canvas = td->fDrawContext->getCanvas();
134 SkIRect rect = SkIRect::MakeXYWH(td->fWidth/2, 0, td->fWidth/4, td->fHeight);
135 SkPaint paint;
136 paint.setColor(SK_ColorRED);
137 canvas->drawIRect(rect, paint);
138
139 // Draw to an offscreen target so that we end up with a mix of "real" secondary command
140 // buffers and the imported secondary command buffer.
141 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(td->fContext, SkBudgeted::kYes,
142 imageInfo);
143 surf->getCanvas()->clear(SK_ColorRED);
144
145 SkRect dstRect = SkRect::MakeXYWH(3*td->fWidth/4, 0, td->fWidth/4, td->fHeight);
146 SkIRect srcRect = SkIRect::MakeWH(td->fWidth/4, td->fHeight);
147 canvas->drawImageRect(surf->makeImageSnapshot(), srcRect, dstRect, &paint);
148
149 td->fDrawContext->flush();
150 }
151
152 // Helper function to test waiting for the imported secondary command buffer to be submitted on
153 // its original context and then cleaning up the GrVkSecondaryCBDrawContext from this GrContext.
154 static void ImportSubmitted(TestDrawable* td) {
155 // Typical use case here would be to create a fence that we submit to the gpu and then wait
156 // on before releasing the GrVkSecondaryCBDrawContext resources. To simulate that for this
157 // test (and since we are running single threaded anyways), we will just force a sync of
158 // the gpu and cpu here.
159 td->fContext->contextPriv().getGpu()->testingOnly_flushGpuAndSync();
160
161 td->fDrawContext->releaseResources();
162 // We release the GrContext here manually to test that we waited long enough before
163 // releasing the GrVkSecondaryCBDrawContext. This simulates when a client is able to delete
164 // the GrContext it used to imported the secondary command buffer. If we had released the
165 // GrContext's resources earlier (before waiting on the gpu above), we would get vulkan
166 // validation layer errors saying we freed some vulkan objects while they were still in use
167 // on the GPU.
168 td->fContext->releaseResourcesAndAbandonContext();
169 }
170
171
Greg Daniel64cc9aa2018-10-19 13:54:56 -0400172 std::unique_ptr<GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi,
Greg Danielb2a259c2018-12-17 10:28:47 -0500173 const SkMatrix& matrix,
174 const SkIRect& clipBounds) override {
Greg Daniel64cc9aa2018-10-19 13:54:56 -0400175 if (backendApi != GrBackendApi::kVulkan) {
176 return nullptr;
177 }
Greg Daniel070cbaf2019-01-03 17:35:54 -0500178 std::unique_ptr<GpuDrawHandler> draw;
179 if (fContext) {
180 draw.reset(new DrawHandlerImport(this, ImportDraw, ImportSubmitted));
181 } else {
182 draw.reset(new DrawHandlerBasic(fInterface, fWidth, fHeight));
183 }
184 return draw;
Greg Daniel64cc9aa2018-10-19 13:54:56 -0400185 }
186
187 SkRect onGetBounds() override {
188 return SkRect::MakeLTRB(fWidth / 2, 0, fWidth, fHeight);
189 }
190
191 void onDraw(SkCanvas*) override {
192 SkASSERT(false);
193 }
194
195private:
196 const GrVkInterface* fInterface;
Greg Daniel070cbaf2019-01-03 17:35:54 -0500197 GrContext* fContext;
198 sk_sp<GrVkSecondaryCBDrawContext> fDrawContext;
199 int32_t fWidth;
200 int32_t fHeight;
Greg Daniel64cc9aa2018-10-19 13:54:56 -0400201
202 typedef SkDrawable INHERITED;
203};
204
Greg Daniel070cbaf2019-01-03 17:35:54 -0500205void draw_drawable_test(skiatest::Reporter* reporter, GrContext* context, GrContext* childContext) {
Greg Daniel64cc9aa2018-10-19 13:54:56 -0400206 GrVkGpu* gpu = static_cast<GrVkGpu*>(context->contextPriv().getGpu());
207
208 const SkImageInfo ii = SkImageInfo::Make(DEV_W, DEV_H, kRGBA_8888_SkColorType,
209 kPremul_SkAlphaType);
210 sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo,
211 ii, 0, kTopLeft_GrSurfaceOrigin, nullptr));
212 SkCanvas* canvas = surface->getCanvas();
213 canvas->clear(SK_ColorBLUE);
214
Greg Daniel070cbaf2019-01-03 17:35:54 -0500215 sk_sp<TestDrawable> drawable(new TestDrawable(gpu->vkInterface(), childContext, DEV_W, DEV_H));
Greg Daniel64cc9aa2018-10-19 13:54:56 -0400216 canvas->drawDrawable(drawable.get());
217
218 SkPaint paint;
219 paint.setColor(SK_ColorGREEN);
220 SkIRect rect = SkIRect::MakeLTRB(0, DEV_H/2, DEV_W, DEV_H);
221 canvas->drawIRect(rect, paint);
222
223 // read pixels
224 SkBitmap bitmap;
225 bitmap.allocPixels(ii);
226 canvas->readPixels(bitmap, 0, 0);
227
228 const uint32_t* canvasPixels = static_cast<const uint32_t*>(bitmap.getPixels());
229 bool failureFound = false;
230 SkPMColor expectedPixel;
Greg Daniel070cbaf2019-01-03 17:35:54 -0500231 for (int cy = 0; cy < DEV_H && !failureFound; ++cy) {
232 for (int cx = 0; cx < DEV_W && !failureFound; ++cx) {
Greg Daniel64cc9aa2018-10-19 13:54:56 -0400233 SkPMColor canvasPixel = canvasPixels[cy * DEV_W + cx];
234 if (cy < DEV_H / 2) {
235 if (cx < DEV_W / 2) {
236 expectedPixel = 0xFFFF0000; // Blue
237 } else {
238 expectedPixel = 0xFF0000FF; // Red
239 }
240 } else {
241 expectedPixel = 0xFF00FF00; // Green
242 }
243 if (expectedPixel != canvasPixel) {
244 failureFound = true;
245 ERRORF(reporter, "Wrong color at %d, %d. Got 0x%08x when we expected 0x%08x",
246 cx, cy, canvasPixel, expectedPixel);
247 }
248 }
249 }
250}
251
Greg Daniel64cc9aa2018-10-19 13:54:56 -0400252DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkDrawableTest, reporter, ctxInfo) {
Greg Daniel070cbaf2019-01-03 17:35:54 -0500253 draw_drawable_test(reporter, ctxInfo.grContext(), nullptr);
254}
255
256DEF_GPUTEST(VkDrawableImportTest, reporter, options) {
257 for (int typeInt = 0; typeInt < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++typeInt) {
258 sk_gpu_test::GrContextFactory::ContextType contextType =
259 (sk_gpu_test::GrContextFactory::ContextType) typeInt;
260 if (contextType != sk_gpu_test::GrContextFactory::kVulkan_ContextType) {
261 continue;
262 }
263 sk_gpu_test::GrContextFactory factory(options);
264 sk_gpu_test::ContextInfo ctxInfo = factory.getContextInfo(
265 contextType, sk_gpu_test::GrContextFactory::ContextOverrides::kDisableNVPR);
266 skiatest::ReporterContext ctx(
267 reporter, SkString(sk_gpu_test::GrContextFactory::ContextTypeName(contextType)));
268 if (ctxInfo.grContext()) {
269 sk_gpu_test::ContextInfo child =
270 factory.getSharedContextInfo(ctxInfo.grContext(), 0);
271 if (!child.grContext()) {
272 continue;
273 }
274
275 draw_drawable_test(reporter, ctxInfo.grContext(), child.grContext());
276 }
277 }
Greg Daniel64cc9aa2018-10-19 13:54:56 -0400278}
279
280#endif