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 intializers to work 9 10 #include "SkTypes.h" 11 12 #if SK_SUPPORT_GPU && defined(SK_VULKAN) 13 14 #include "vk/GrVkVulkan.h" 15 16 #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" 25 #include "vk/GrVkSecondaryCBDrawContext.h" 26 #include "vk/GrVkUtil.h" 27 28 using sk_gpu_test::GrContextFactory; 29 30 static const int DEV_W = 16, DEV_H = 16; 31 32 class TestDrawable : public SkDrawable { 33 public: 34 TestDrawable(const GrVkInterface* interface, GrContext* context, int32_t width, int32_t height) 35 : INHERITED() 36 , fInterface(interface) 37 , fContext(context) 38 , fWidth(width) 39 , fHeight(height) {} 40 41 ~TestDrawable() override {} 42 43 class DrawHandlerBasic : public GpuDrawHandler { 44 public: 45 DrawHandlerBasic(const GrVkInterface* interface, int32_t width, int32_t height) 46 : INHERITED() 47 , fInterface(interface) 48 , fWidth(width) 49 , fHeight(height) {} 50 ~DrawHandlerBasic() override {} 51 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; 72 attachment.colorAttachment = vkInfo.fColorAttachmentIndex; 73 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 91 typedef void (*DrawProc)(TestDrawable*, const SkMatrix&, const SkIRect&, 92 const SkImageInfo&, const GrVkDrawableInfo&); 93 typedef void (*SubmitProc)(TestDrawable*); 94 95 // Exercises the exporting of a secondary command buffer from one GrContext and then importing 96 // it into a second GrContext. We then draw to the secondary command buffer from the second 97 // GrContext. 98 class DrawHandlerImport : public GpuDrawHandler { 99 public: 100 DrawHandlerImport(TestDrawable* td, DrawProc drawProc, SubmitProc submitProc, 101 const SkMatrix& matrix, 102 const SkIRect& clipBounds, 103 const SkImageInfo& bufferInfo) 104 : INHERITED() 105 , fTestDrawable(td) 106 , fDrawProc(drawProc) 107 , fSubmitProc(submitProc) 108 , fMatrix(matrix) 109 , fClipBounds(clipBounds) 110 , fBufferInfo(bufferInfo) {} 111 ~DrawHandlerImport() override { 112 fSubmitProc(fTestDrawable); 113 } 114 115 void draw(const GrBackendDrawableInfo& info) override { 116 GrVkDrawableInfo vkInfo; 117 SkAssertResult(info.getVkDrawableInfo(&vkInfo)); 118 119 fDrawProc(fTestDrawable, fMatrix, fClipBounds, fBufferInfo, vkInfo); 120 } 121 private: 122 TestDrawable* fTestDrawable; 123 DrawProc fDrawProc; 124 SubmitProc fSubmitProc; 125 const SkMatrix fMatrix; 126 const SkIRect fClipBounds; 127 const SkImageInfo fBufferInfo; 128 129 typedef GpuDrawHandler INHERITED; 130 }; 131 132 // Helper function to test drawing to a secondary command buffer that we imported into the 133 // GrContext using a GrVkSecondaryCBDrawContext. 134 static void ImportDraw(TestDrawable* td, const SkMatrix& matrix, const SkIRect& clipBounds, 135 const SkImageInfo& bufferInfo, const GrVkDrawableInfo& info) { 136 td->fDrawContext = GrVkSecondaryCBDrawContext::Make(td->fContext, bufferInfo, info, nullptr); 137 if (!td->fDrawContext) { 138 return; 139 } 140 141 SkCanvas* canvas = td->fDrawContext->getCanvas(); 142 canvas->clipRect(SkRect::Make(clipBounds)); 143 canvas->setMatrix(matrix); 144 145 SkIRect rect = SkIRect::MakeXYWH(td->fWidth/2, 0, td->fWidth/4, td->fHeight); 146 SkPaint paint; 147 paint.setColor(SK_ColorRED); 148 canvas->drawIRect(rect, paint); 149 150 // Draw to an offscreen target so that we end up with a mix of "real" secondary command 151 // buffers and the imported secondary command buffer. 152 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(td->fContext, SkBudgeted::kYes, 153 bufferInfo); 154 surf->getCanvas()->clear(SK_ColorRED); 155 156 SkRect dstRect = SkRect::MakeXYWH(3*td->fWidth/4, 0, td->fWidth/4, td->fHeight); 157 SkIRect srcRect = SkIRect::MakeWH(td->fWidth/4, td->fHeight); 158 canvas->drawImageRect(surf->makeImageSnapshot(), srcRect, dstRect, &paint); 159 160 td->fDrawContext->flush(); 161 } 162 163 // Helper function to test waiting for the imported secondary command buffer to be submitted on 164 // its original context and then cleaning up the GrVkSecondaryCBDrawContext from this GrContext. 165 static void ImportSubmitted(TestDrawable* td) { 166 // Typical use case here would be to create a fence that we submit to the gpu and then wait 167 // on before releasing the GrVkSecondaryCBDrawContext resources. To simulate that for this 168 // test (and since we are running single threaded anyways), we will just force a sync of 169 // the gpu and cpu here. 170 td->fContext->contextPriv().getGpu()->testingOnly_flushGpuAndSync(); 171 172 td->fDrawContext->releaseResources(); 173 // We release the GrContext here manually to test that we waited long enough before 174 // releasing the GrVkSecondaryCBDrawContext. This simulates when a client is able to delete 175 // the GrContext it used to imported the secondary command buffer. If we had released the 176 // GrContext's resources earlier (before waiting on the gpu above), we would get vulkan 177 // validation layer errors saying we freed some vulkan objects while they were still in use 178 // on the GPU. 179 td->fContext->releaseResourcesAndAbandonContext(); 180 } 181 182 183 std::unique_ptr<GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi, 184 const SkMatrix& matrix, 185 const SkIRect& clipBounds, 186 const SkImageInfo& bufferInfo) override { 187 if (backendApi != GrBackendApi::kVulkan) { 188 return nullptr; 189 } 190 std::unique_ptr<GpuDrawHandler> draw; 191 if (fContext) { 192 draw.reset(new DrawHandlerImport(this, ImportDraw, ImportSubmitted, matrix, 193 clipBounds, bufferInfo)); 194 } else { 195 draw.reset(new DrawHandlerBasic(fInterface, fWidth, fHeight)); 196 } 197 return draw; 198 } 199 200 SkRect onGetBounds() override { 201 return SkRect::MakeLTRB(fWidth / 2, 0, fWidth, fHeight); 202 } 203 204 void onDraw(SkCanvas*) override { 205 SkASSERT(false); 206 } 207 208 private: 209 const GrVkInterface* fInterface; 210 GrContext* fContext; 211 sk_sp<GrVkSecondaryCBDrawContext> fDrawContext; 212 int32_t fWidth; 213 int32_t fHeight; 214 215 typedef SkDrawable INHERITED; 216 }; 217 218 void draw_drawable_test(skiatest::Reporter* reporter, GrContext* context, GrContext* childContext) { 219 GrVkGpu* gpu = static_cast<GrVkGpu*>(context->contextPriv().getGpu()); 220 221 const SkImageInfo ii = SkImageInfo::Make(DEV_W, DEV_H, kRGBA_8888_SkColorType, 222 kPremul_SkAlphaType); 223 sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, 224 ii, 0, kTopLeft_GrSurfaceOrigin, nullptr)); 225 SkCanvas* canvas = surface->getCanvas(); 226 canvas->clear(SK_ColorBLUE); 227 228 sk_sp<TestDrawable> drawable(new TestDrawable(gpu->vkInterface(), childContext, DEV_W, DEV_H)); 229 canvas->drawDrawable(drawable.get()); 230 231 SkPaint paint; 232 paint.setColor(SK_ColorGREEN); 233 SkIRect rect = SkIRect::MakeLTRB(0, DEV_H/2, DEV_W, DEV_H); 234 canvas->drawIRect(rect, paint); 235 236 // read pixels 237 SkBitmap bitmap; 238 bitmap.allocPixels(ii); 239 canvas->readPixels(bitmap, 0, 0); 240 241 const uint32_t* canvasPixels = static_cast<const uint32_t*>(bitmap.getPixels()); 242 bool failureFound = false; 243 SkPMColor expectedPixel; 244 for (int cy = 0; cy < DEV_H && !failureFound; ++cy) { 245 for (int cx = 0; cx < DEV_W && !failureFound; ++cx) { 246 SkPMColor canvasPixel = canvasPixels[cy * DEV_W + cx]; 247 if (cy < DEV_H / 2) { 248 if (cx < DEV_W / 2) { 249 expectedPixel = 0xFFFF0000; // Blue 250 } else { 251 expectedPixel = 0xFF0000FF; // Red 252 } 253 } else { 254 expectedPixel = 0xFF00FF00; // Green 255 } 256 if (expectedPixel != canvasPixel) { 257 failureFound = true; 258 ERRORF(reporter, "Wrong color at %d, %d. Got 0x%08x when we expected 0x%08x", 259 cx, cy, canvasPixel, expectedPixel); 260 } 261 } 262 } 263 } 264 265 DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkDrawableTest, reporter, ctxInfo) { 266 draw_drawable_test(reporter, ctxInfo.grContext(), nullptr); 267 } 268 269 DEF_GPUTEST(VkDrawableImportTest, reporter, options) { 270 for (int typeInt = 0; typeInt < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++typeInt) { 271 sk_gpu_test::GrContextFactory::ContextType contextType = 272 (sk_gpu_test::GrContextFactory::ContextType) typeInt; 273 if (contextType != sk_gpu_test::GrContextFactory::kVulkan_ContextType) { 274 continue; 275 } 276 sk_gpu_test::GrContextFactory factory(options); 277 sk_gpu_test::ContextInfo ctxInfo = factory.getContextInfo( 278 contextType, sk_gpu_test::GrContextFactory::ContextOverrides::kDisableNVPR); 279 skiatest::ReporterContext ctx( 280 reporter, SkString(sk_gpu_test::GrContextFactory::ContextTypeName(contextType))); 281 if (ctxInfo.grContext()) { 282 sk_gpu_test::ContextInfo child = 283 factory.getSharedContextInfo(ctxInfo.grContext(), 0); 284 if (!child.grContext()) { 285 continue; 286 } 287 288 draw_drawable_test(reporter, ctxInfo.grContext(), child.grContext()); 289 } 290 } 291 } 292 293 #endif 294