1 /*
2 * Copyright 2017 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/gpu/vk/GrVkVulkan.h"
9
10 #include "src/gpu/GrContextPriv.h"
11 #include "tests/Test.h"
12 #include "tools/gpu/GrContextFactory.h"
13
14 #include "include/core/SkCanvas.h"
15 #include "include/core/SkSurface.h"
16 #include "include/gpu/GrBackendSemaphore.h"
17 #include "include/gpu/GrBackendSurface.h"
18
19 #include "src/gpu/gl/GrGLGpu.h"
20 #include "src/gpu/gl/GrGLUtil.h"
21
22 #ifdef SK_VULKAN
23 #include "include/gpu/vk/GrVkTypes.h"
24 #include "src/gpu/vk/GrVkCommandPool.h"
25 #include "src/gpu/vk/GrVkGpu.h"
26 #include "src/gpu/vk/GrVkUtil.h"
27
28 #ifdef VK_USE_PLATFORM_WIN32_KHR
29 // windows wants to define this as CreateSemaphoreA or CreateSemaphoreW
30 #undef CreateSemaphore
31 #endif
32 #endif
33
34 static const int MAIN_W = 8, MAIN_H = 16;
35 static const int CHILD_W = 16, CHILD_H = 16;
36
check_pixels(skiatest::Reporter * reporter,const SkBitmap & bitmap)37 void check_pixels(skiatest::Reporter* reporter, const SkBitmap& bitmap) {
38 const uint32_t* canvasPixels = static_cast<const uint32_t*>(bitmap.getPixels());
39
40 bool failureFound = false;
41 SkPMColor expectedPixel;
42 for (int cy = 0; cy < CHILD_H && !failureFound; ++cy) {
43 for (int cx = 0; cx < CHILD_W && !failureFound; ++cx) {
44 SkPMColor canvasPixel = canvasPixels[cy * CHILD_W + cx];
45 if (cy < CHILD_H / 2) {
46 if (cx < CHILD_W / 2) {
47 expectedPixel = 0xFF0000FF; // Red
48 } else {
49 expectedPixel = 0xFFFF0000; // Blue
50 }
51 } else {
52 expectedPixel = 0xFF00FF00; // Green
53 }
54 if (expectedPixel != canvasPixel) {
55 failureFound = true;
56 ERRORF(reporter, "Wrong color at %d, %d. Got 0x%08x when we expected 0x%08x",
57 cx, cy, canvasPixel, expectedPixel);
58 }
59 }
60 }
61 }
62
draw_child(skiatest::Reporter * reporter,const sk_gpu_test::ContextInfo & childInfo,const GrBackendTexture & backendTexture,const GrBackendSemaphore & semaphore)63 void draw_child(skiatest::Reporter* reporter,
64 const sk_gpu_test::ContextInfo& childInfo,
65 const GrBackendTexture& backendTexture,
66 const GrBackendSemaphore& semaphore) {
67
68 childInfo.testContext()->makeCurrent();
69
70 const SkImageInfo childII = SkImageInfo::Make(CHILD_W, CHILD_H, kRGBA_8888_SkColorType,
71 kPremul_SkAlphaType);
72
73 GrContext* childCtx = childInfo.grContext();
74 sk_sp<SkSurface> childSurface(SkSurface::MakeRenderTarget(childCtx, SkBudgeted::kNo,
75 childII, 0, kTopLeft_GrSurfaceOrigin,
76 nullptr));
77
78 sk_sp<SkImage> childImage = SkImage::MakeFromTexture(childCtx,
79 backendTexture,
80 kTopLeft_GrSurfaceOrigin,
81 kRGBA_8888_SkColorType,
82 kPremul_SkAlphaType,
83 nullptr,
84 nullptr,
85 nullptr);
86
87 SkCanvas* childCanvas = childSurface->getCanvas();
88 childCanvas->clear(SK_ColorRED);
89
90 childSurface->wait(1, &semaphore);
91
92 childCanvas->drawImage(childImage, CHILD_W/2, 0);
93
94 SkPaint paint;
95 paint.setColor(SK_ColorGREEN);
96 SkIRect rect = SkIRect::MakeLTRB(0, CHILD_H/2, CHILD_W, CHILD_H);
97 childCanvas->drawIRect(rect, paint);
98
99 // read pixels
100 SkBitmap bitmap;
101 bitmap.allocPixels(childII);
102 childSurface->readPixels(bitmap, 0, 0);
103
104 check_pixels(reporter, bitmap);
105 }
106
107 enum class FlushType { kSurface, kImage, kContext };
108
surface_semaphore_test(skiatest::Reporter * reporter,const sk_gpu_test::ContextInfo & mainInfo,const sk_gpu_test::ContextInfo & childInfo1,const sk_gpu_test::ContextInfo & childInfo2,FlushType flushType)109 void surface_semaphore_test(skiatest::Reporter* reporter,
110 const sk_gpu_test::ContextInfo& mainInfo,
111 const sk_gpu_test::ContextInfo& childInfo1,
112 const sk_gpu_test::ContextInfo& childInfo2,
113 FlushType flushType) {
114 GrContext* mainCtx = mainInfo.grContext();
115 if (!mainCtx->priv().caps()->semaphoreSupport()) {
116 return;
117 }
118
119 const SkImageInfo ii = SkImageInfo::Make(MAIN_W, MAIN_H, kRGBA_8888_SkColorType,
120 kPremul_SkAlphaType);
121
122 sk_sp<SkSurface> mainSurface(SkSurface::MakeRenderTarget(mainCtx, SkBudgeted::kNo,
123 ii, 0, kTopLeft_GrSurfaceOrigin,
124 nullptr));
125 SkCanvas* mainCanvas = mainSurface->getCanvas();
126 auto blueSurface = mainSurface->makeSurface(ii);
127 blueSurface->getCanvas()->clear(SK_ColorBLUE);
128 auto blueImage = blueSurface->makeImageSnapshot();
129 blueSurface.reset();
130 mainCanvas->drawImage(blueImage, 0, 0);
131
132 SkAutoTArray<GrBackendSemaphore> semaphores(2);
133 #ifdef SK_VULKAN
134 if (GrBackendApi::kVulkan == mainInfo.backend()) {
135 // Initialize the secondary semaphore instead of having Ganesh create one internally
136 GrVkGpu* gpu = static_cast<GrVkGpu*>(mainCtx->priv().getGpu());
137 const GrVkInterface* interface = gpu->vkInterface();
138 VkDevice device = gpu->device();
139
140 VkSemaphore vkSem;
141
142 VkSemaphoreCreateInfo createInfo;
143 createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
144 createInfo.pNext = nullptr;
145 createInfo.flags = 0;
146 GR_VK_CALL_ERRCHECK(interface, CreateSemaphore(device, &createInfo, nullptr, &vkSem));
147
148 semaphores[1].initVulkan(vkSem);
149 }
150 #endif
151
152 GrFlushInfo info;
153 info.fNumSemaphores = 2;
154 info.fSignalSemaphores = semaphores.get();
155 switch (flushType) {
156 case FlushType::kSurface:
157 mainSurface->flush(SkSurface::BackendSurfaceAccess::kNoAccess, info);
158 break;
159 case FlushType::kImage:
160 blueImage->flush(mainCtx, info);
161 break;
162 case FlushType::kContext:
163 mainCtx->flush(info);
164 break;
165 }
166
167 sk_sp<SkImage> mainImage = mainSurface->makeImageSnapshot();
168 GrBackendTexture backendTexture = mainImage->getBackendTexture(false);
169
170 draw_child(reporter, childInfo1, backendTexture, semaphores[0]);
171
172 #ifdef SK_VULKAN
173 if (GrBackendApi::kVulkan == mainInfo.backend()) {
174 // In Vulkan we need to make sure we are sending the correct VkImageLayout in with the
175 // backendImage. After the first child draw the layout gets changed to SHADER_READ, so
176 // we just manually set that here.
177 GrVkImageInfo vkInfo;
178 SkAssertResult(backendTexture.getVkImageInfo(&vkInfo));
179 vkInfo.updateImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
180 }
181 #endif
182
183 draw_child(reporter, childInfo2, backendTexture, semaphores[1]);
184 }
185
DEF_GPUTEST(SurfaceSemaphores,reporter,options)186 DEF_GPUTEST(SurfaceSemaphores, reporter, options) {
187 #if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC)
188 static constexpr auto kNativeGLType = sk_gpu_test::GrContextFactory::kGL_ContextType;
189 #else
190 static constexpr auto kNativeGLType = sk_gpu_test::GrContextFactory::kGLES_ContextType;
191 #endif
192
193 for (int typeInt = 0; typeInt < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++typeInt) {
194 for (auto flushType : {FlushType::kSurface, FlushType::kImage, FlushType::kContext}) {
195 sk_gpu_test::GrContextFactory::ContextType contextType =
196 (sk_gpu_test::GrContextFactory::ContextType) typeInt;
197 // Use "native" instead of explicitly trying OpenGL and OpenGL ES. Do not use GLES on
198 // desktop since tests do not account for not fixing http://skbug.com/2809
199 if (contextType == sk_gpu_test::GrContextFactory::kGL_ContextType ||
200 contextType == sk_gpu_test::GrContextFactory::kGLES_ContextType) {
201 if (contextType != kNativeGLType) {
202 continue;
203 }
204 }
205 sk_gpu_test::GrContextFactory factory(options);
206 sk_gpu_test::ContextInfo ctxInfo = factory.getContextInfo(contextType);
207 if (!sk_gpu_test::GrContextFactory::IsRenderingContext(contextType)) {
208 continue;
209 }
210 skiatest::ReporterContext ctx(
211 reporter, SkString(sk_gpu_test::GrContextFactory::ContextTypeName(contextType)));
212 if (ctxInfo.grContext()) {
213 sk_gpu_test::ContextInfo child1 =
214 factory.getSharedContextInfo(ctxInfo.grContext(), 0);
215 sk_gpu_test::ContextInfo child2 =
216 factory.getSharedContextInfo(ctxInfo.grContext(), 1);
217 if (!child1.grContext() || !child2.grContext()) {
218 continue;
219 }
220
221 surface_semaphore_test(reporter, ctxInfo, child1, child2, flushType);
222 }
223 }
224 }
225 }
226
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(EmptySurfaceSemaphoreTest,reporter,ctxInfo)227 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(EmptySurfaceSemaphoreTest, reporter, ctxInfo) {
228 GrContext* ctx = ctxInfo.grContext();
229 if (!ctx->priv().caps()->semaphoreSupport()) {
230 return;
231 }
232
233 const SkImageInfo ii = SkImageInfo::Make(MAIN_W, MAIN_H, kRGBA_8888_SkColorType,
234 kPremul_SkAlphaType);
235
236 sk_sp<SkSurface> mainSurface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo,
237 ii, 0, kTopLeft_GrSurfaceOrigin,
238 nullptr));
239
240 // Flush surface once without semaphores to make sure there is no peneding IO for it.
241 mainSurface->flush();
242
243 GrBackendSemaphore semaphore;
244 GrSemaphoresSubmitted submitted = mainSurface->flushAndSignalSemaphores(1, &semaphore);
245 REPORTER_ASSERT(reporter, GrSemaphoresSubmitted::kYes == submitted);
246
247 if (GrBackendApi::kOpenGL == ctxInfo.backend()) {
248 GrGLGpu* gpu = static_cast<GrGLGpu*>(ctx->priv().getGpu());
249 const GrGLInterface* interface = gpu->glInterface();
250 GrGLsync sync = semaphore.glSync();
251 REPORTER_ASSERT(reporter, sync);
252 bool result;
253 GR_GL_CALL_RET(interface, result, IsSync(sync));
254 REPORTER_ASSERT(reporter, result);
255 }
256
257 #ifdef SK_VULKAN
258 if (GrBackendApi::kVulkan == ctxInfo.backend()) {
259 GrVkGpu* gpu = static_cast<GrVkGpu*>(ctx->priv().getGpu());
260 const GrVkInterface* interface = gpu->vkInterface();
261 VkDevice device = gpu->device();
262 VkQueue queue = gpu->queue();
263 GrVkCommandPool* cmdPool = gpu->cmdPool();
264 VkCommandBuffer cmdBuffer;
265
266 // Create Command Buffer
267 const VkCommandBufferAllocateInfo cmdInfo = {
268 VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // sType
269 nullptr, // pNext
270 cmdPool->vkCommandPool(), // commandPool
271 VK_COMMAND_BUFFER_LEVEL_PRIMARY, // level
272 1 // bufferCount
273 };
274
275 VkResult err = GR_VK_CALL(interface, AllocateCommandBuffers(device, &cmdInfo, &cmdBuffer));
276 if (err) {
277 return;
278 }
279
280 VkCommandBufferBeginInfo cmdBufferBeginInfo;
281 memset(&cmdBufferBeginInfo, 0, sizeof(VkCommandBufferBeginInfo));
282 cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
283 cmdBufferBeginInfo.pNext = nullptr;
284 cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
285 cmdBufferBeginInfo.pInheritanceInfo = nullptr;
286
287 GR_VK_CALL_ERRCHECK(interface, BeginCommandBuffer(cmdBuffer, &cmdBufferBeginInfo));
288 GR_VK_CALL_ERRCHECK(interface, EndCommandBuffer(cmdBuffer));
289
290 VkFenceCreateInfo fenceInfo;
291 VkFence fence;
292
293 memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo));
294 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
295 err = GR_VK_CALL(interface, CreateFence(device, &fenceInfo, nullptr, &fence));
296 SkASSERT(!err);
297
298 VkPipelineStageFlags waitStages = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
299 VkSubmitInfo submitInfo;
300 memset(&submitInfo, 0, sizeof(VkSubmitInfo));
301 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
302 submitInfo.pNext = nullptr;
303 submitInfo.waitSemaphoreCount = 1;
304 VkSemaphore vkSem = semaphore.vkSemaphore();
305 submitInfo.pWaitSemaphores = &vkSem;
306 submitInfo.pWaitDstStageMask = &waitStages;
307 submitInfo.commandBufferCount = 1;
308 submitInfo.pCommandBuffers = &cmdBuffer;
309 submitInfo.signalSemaphoreCount = 0;
310 submitInfo.pSignalSemaphores = nullptr;
311 GR_VK_CALL_ERRCHECK(interface, QueueSubmit(queue, 1, &submitInfo, fence));
312
313 err = GR_VK_CALL(interface, WaitForFences(device, 1, &fence, true, 3000000000));
314
315 REPORTER_ASSERT(reporter, err != VK_TIMEOUT);
316
317 GR_VK_CALL(interface, DestroyFence(device, fence, nullptr));
318 GR_VK_CALL(interface, DestroySemaphore(device, vkSem, nullptr));
319 // If the above test fails the wait semaphore will never be signaled which can cause the
320 // device to hang when tearing down (even if just tearing down GL). So we Fail here to
321 // kill things.
322 if (err == VK_TIMEOUT) {
323 SK_ABORT("Waiting on semaphore indefinitely");
324 }
325 }
326 #endif
327 }
328