diff options
33 files changed, 580 insertions, 111 deletions
@@ -1432,36 +1432,6 @@ bool IsRenderingGLContextType(sk_gpu_test::GrContextFactory::ContextType type) { bool IsNullGLContextType(sk_gpu_test::GrContextFactory::ContextType type) { return type == GrContextFactory::kNullGL_ContextType; } -const char* ContextTypeName(GrContextFactory::ContextType contextType) { - switch (contextType) { - case GrContextFactory::kGL_ContextType: - return "OpenGL"; - case GrContextFactory::kGLES_ContextType: - return "OpenGLES"; - case GrContextFactory::kANGLE_D3D9_ES2_ContextType: - return "ANGLE D3D9 ES2"; - case GrContextFactory::kANGLE_D3D11_ES2_ContextType: - return "ANGLE D3D11 ES2"; - case GrContextFactory::kANGLE_D3D11_ES3_ContextType: - return "ANGLE D3D11 ES3"; - case GrContextFactory::kANGLE_GL_ES2_ContextType: - return "ANGLE GL ES2"; - case GrContextFactory::kANGLE_GL_ES3_ContextType: - return "ANGLE GL ES3"; - case GrContextFactory::kCommandBuffer_ContextType: - return "Command Buffer"; - case GrContextFactory::kMESA_ContextType: - return "Mesa"; - case GrContextFactory::kNullGL_ContextType: - return "Null GL"; - case GrContextFactory::kDebugGL_ContextType: - return "Debug GL"; - case GrContextFactory::kVulkan_ContextType: - return "Vulkan"; - } - SkDEBUGFAIL("Unreachable"); - return "Unknown"; -} #else bool IsGLContextType(int) { return false; } bool IsVulkanContextType(int) { return false; } @@ -1494,7 +1464,7 @@ void RunWithGPUTestContexts(GrContextTestFn* test, GrContextTypeFilterFn* contex if (contextTypeFilter && !(*contextTypeFilter)(contextType)) { continue; } - ReporterContext ctx(reporter, SkString(ContextTypeName(contextType))); + ReporterContext ctx(reporter, SkString(GrContextFactory::ContextTypeName(contextType))); if (ctxInfo.grContext()) { (*test)(reporter, ctxInfo); } diff --git a/gn/gpu.gni b/gn/gpu.gni index aa840694ce..5c6b238144 100644 --- a/gn/gpu.gni +++ b/gn/gpu.gni @@ -8,6 +8,7 @@ _src = get_path_info("../src", "abspath") _include = get_path_info("../include", "abspath") skia_gpu_sources = [ + "$_include/gpu/GrBackendSemaphore.h", "$_include/gpu/GrBackendSurface.h", "$_include/gpu/GrBlend.h", "$_include/gpu/GrCaps.h", diff --git a/gn/tests.gni b/gn/tests.gni index f961c84002..80af7146e7 100644 --- a/gn/tests.gni +++ b/gn/tests.gni @@ -232,6 +232,7 @@ tests_sources = [ "$_tests/StrokerTest.cpp", "$_tests/StrokeTest.cpp", "$_tests/SubsetPath.cpp", + "$_tests/SurfaceSemaphoreTest.cpp", "$_tests/SurfaceTest.cpp", "$_tests/SVGDeviceTest.cpp", "$_tests/SwizzlerTest.cpp", diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h index efa2428ccb..6fad21721e 100644 --- a/include/core/SkSurface.h +++ b/include/core/SkSurface.h @@ -15,6 +15,7 @@ class SkCanvas; class SkPaint; class GrBackendRenderTarget; +class GrBackendSemaphore; class GrContext; class GrRenderTarget; @@ -323,9 +324,33 @@ public: /** * Issue any pending surface IO to the current backend 3D API and resolve any surface MSAA. + * + * The flush calls below are the new preferred way to flush calls to a surface, and this call + * will eventually be removed. */ void prepareForExternalIO(); + /** + * Issue any pending surface IO to the current backend 3D API + */ + void flush(); + + /** + * Issue any pending surface IO to the current backend 3D API. After issuing all commands, we + * will issue numSemaphore semaphores for the gpu to signal. We will then fill in the array + * signalSemaphores with the info on the semaphores we submitted. The client is reposonsible for + * allocating enough space in signalSemaphores to handle numSemaphores of GrBackendSemaphores. + * The client will also take ownership of the returned underlying backend semaphores. + */ + void flushAndSignalSemaphores(int numSemaphores, GrBackendSemaphore* signalSemaphores); + + /** + * Inserts a list of GPU semaphores that the current backend 3D API must wait on before + * executing any more commands on the GPU for this surface. Skia will take ownership of the + * underlying semaphores and delete them once they have been signaled and waited on. + */ + void wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores); + protected: SkSurface(int width, int height, const SkSurfaceProps*); SkSurface(const SkImageInfo&, const SkSurfaceProps*); diff --git a/include/gpu/GrBackendSemaphore.h b/include/gpu/GrBackendSemaphore.h new file mode 100644 index 0000000000..668f6bb1c9 --- /dev/null +++ b/include/gpu/GrBackendSemaphore.h @@ -0,0 +1,69 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrBackendSemaphore_DEFINED +#define GrBackendSemaphore_DEFINED + +#include "GrTypes.h" + +#include "gl/GrGLTypes.h" + +#ifdef SK_VULKAN +#include "vk/GrVkTypes.h" +#endif + +/** + * Wrapper class for passing into and receiving data from Ganesh about a backend semaphore object. + */ +class GrBackendSemaphore { +public: + // For convenience we just set the backend here to OpenGL. The GrBackendSemaphore cannot be used + // until either initGL or initVulkan are called which will set the appropriate GrBackend. + GrBackendSemaphore() : fBackend(kOpenGL_GrBackend), fGLSync(0), fIsInitialized(false) {} + + void initGL(GrGLsync sync) { + fBackend = kOpenGL_GrBackend; + fGLSync = sync; + fIsInitialized = true; + } + +#ifdef SK_VULKAN + void initVulkan(VkSemaphore semaphore) { + fBackend = kVulkan_GrBackend; + fVkSemaphore = semaphore; + fIsInitialized = true; + } +#endif + + GrGLsync glSync() const { + if (!fIsInitialized || kOpenGL_GrBackend != fBackend) { + return 0; + } + return fGLSync; + } + +#ifdef SK_VULKAN + VkSemaphore vkSemaphore() const { + if (!fIsInitialized || kVulkan_GrBackend != fBackend) { + return VK_NULL_HANDLE; + } + return fVkSemaphore; + } +#endif + +private: + GrBackend fBackend; + union { + GrGLsync fGLSync; +#ifdef SK_VULKAN + VkSemaphore fVkSemaphore; +#endif + }; + bool fIsInitialized; +}; + +#endif diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h index 3c400a8b9c..418b8b418d 100644 --- a/src/gpu/GrGpu.h +++ b/src/gpu/GrGpu.h @@ -21,6 +21,7 @@ #include <map> class GrBackendRenderTarget; +class GrBackendSemaphore; class GrBuffer; class GrContext; struct GrContextOptions; @@ -376,7 +377,9 @@ public: virtual bool waitFence(GrFence, uint64_t timeout = 1000) = 0; virtual void deleteFence(GrFence) const = 0; - virtual sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore() = 0; + virtual sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned = true) = 0; + virtual sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore, + GrWrapOwnership ownership) = 0; virtual void insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush = false) = 0; virtual void waitSemaphore(sk_sp<GrSemaphore> semaphore) = 0; diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp index c5a115faeb..17a7db8c81 100644 --- a/src/gpu/GrRenderTargetContext.cpp +++ b/src/gpu/GrRenderTargetContext.cpp @@ -9,6 +9,7 @@ #include "../private/GrAuditTrail.h" #include "../private/SkShadowFlags.h" #include "GrAppliedClip.h" +#include "GrBackendSemaphore.h" #include "GrColor.h" #include "GrContextPriv.h" #include "GrDrawingManager.h" @@ -38,6 +39,7 @@ #include "ops/GrOvalOpFactory.h" #include "ops/GrRectOpFactory.h" #include "ops/GrRegionOp.h" +#include "ops/GrSemaphoreOp.h" #include "ops/GrShadowRRectOp.h" #include "ops/GrStencilPathOp.h" #include "text/GrAtlasTextContext.h" @@ -508,7 +510,7 @@ void GrRenderTargetContext::drawRect(const GrClip& clip, const SkStrokeRec& stroke = style->strokeRec(); if (stroke.getStyle() == SkStrokeRec::kFill_Style) { - + if (!fContext->caps()->useDrawInsteadOfClear()) { // Check if this is a full RT draw and can be replaced with a clear. We don't bother // checking cases where the RT is fully inside a stroke. @@ -1429,13 +1431,44 @@ void GrRenderTargetContext::drawImageLattice(const GrClip& clip, this->addLegacyMeshDrawOp(std::move(pipelineBuilder), clip, std::move(op)); } -void GrRenderTargetContext::prepareForExternalIO() { +void GrRenderTargetContext::prepareForExternalIO(int numSemaphores, + GrBackendSemaphore* backendSemaphores) { ASSERT_SINGLE_OWNER RETURN_IF_ABANDONED SkDEBUGCODE(this->validate();) GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::prepareForExternalIO"); + SkTArray<sk_sp<GrSemaphore>> semaphores(numSemaphores); + for (int i = 0; i < numSemaphores; ++i) { + semaphores.push_back(fContext->resourceProvider()->makeSemaphore(false)); + std::unique_ptr<GrOp> signalOp(GrSemaphoreOp::MakeSignal(semaphores.back(), + fRenderTargetProxy.get())); + this->getOpList()->addOp(std::move(signalOp), *this->caps()); + } + this->drawingManager()->prepareSurfaceForExternalIO(fRenderTargetProxy.get()); + + for (int i = 0; i < numSemaphores; ++i) { + semaphores[i]->setBackendSemaphore(&backendSemaphores[i]); + } +} + +void GrRenderTargetContext::waitOnSemaphores(int numSemaphores, + const GrBackendSemaphore* waitSemaphores) { + ASSERT_SINGLE_OWNER + RETURN_IF_ABANDONED + SkDEBUGCODE(this->validate();) + GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::waitOnSemaphores"); + + AutoCheckFlush acf(this->drawingManager()); + + SkTArray<sk_sp<GrSemaphore>> semaphores(numSemaphores); + for (int i = 0; i < numSemaphores; ++i) { + sk_sp<GrSemaphore> sema = fContext->resourceProvider()->wrapBackendSemaphore( + waitSemaphores[i], kAdopt_GrWrapOwnership); + std::unique_ptr<GrOp> waitOp(GrSemaphoreOp::MakeWait(sema, fRenderTargetProxy.get())); + this->getOpList()->addOp(std::move(waitOp), *this->caps()); + } } // Can 'path' be drawn as a pair of filled nested rectangles? diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h index bc226044dd..a2369dd4fc 100644 --- a/src/gpu/GrRenderTargetContext.h +++ b/src/gpu/GrRenderTargetContext.h @@ -19,6 +19,7 @@ #include "SkRefCnt.h" #include "SkSurfaceProps.h" +class GrBackendSemaphore; class GrClip; class GrDrawingManager; class GrDrawOp; @@ -302,7 +303,13 @@ public: * After this returns any pending surface IO will be issued to the backend 3D API and * if the surface has MSAA it will be resolved. */ - void prepareForExternalIO(); + void prepareForExternalIO(int numSemaphores, GrBackendSemaphore* backendSemaphores); + + /** + * The next time this GrRenderTargetContext is flushed, the gpu will wait on the passed in + * semaphores before executing any commands. + */ + void waitOnSemaphores(int numSemaphores, const GrBackendSemaphore* waitSemaphores); GrFSAAType fsaaType() const { return fRenderTargetProxy->fsaaType(); } const GrCaps* caps() const { return fContext->caps(); } diff --git a/src/gpu/GrResourceProvider.cpp b/src/gpu/GrResourceProvider.cpp index 52340bb0e9..dccfaae875 100644 --- a/src/gpu/GrResourceProvider.cpp +++ b/src/gpu/GrResourceProvider.cpp @@ -7,6 +7,7 @@ #include "GrResourceProvider.h" +#include "GrBackendSemaphore.h" #include "GrBuffer.h" #include "GrCaps.h" #include "GrContext.h" @@ -505,8 +506,14 @@ sk_sp<GrRenderTarget> GrResourceProvider::wrapBackendTextureAsRenderTarget( return this->gpu()->wrapBackendTextureAsRenderTarget(tex, origin, sampleCnt); } -sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrResourceProvider::makeSemaphore() { - return fGpu->makeSemaphore(); +sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrResourceProvider::makeSemaphore(bool isOwned) { + return fGpu->makeSemaphore(isOwned); +} + +sk_sp<GrSemaphore> GrResourceProvider::wrapBackendSemaphore(const GrBackendSemaphore& semaphore, + GrWrapOwnership ownership) { + ASSERT_SINGLE_OWNER + return this->isAbandoned() ? nullptr : fGpu->wrapBackendSemaphore(semaphore, ownership); } void GrResourceProvider::takeOwnershipOfSemaphore(sk_sp<GrSemaphore> semaphore) { diff --git a/src/gpu/GrResourceProvider.h b/src/gpu/GrResourceProvider.h index 50264ef908..a6ddbc55b0 100644 --- a/src/gpu/GrResourceProvider.h +++ b/src/gpu/GrResourceProvider.h @@ -8,13 +8,14 @@ #ifndef GrResourceProvider_DEFINED #define GrResourceProvider_DEFINED -#include "GrBackendSurface.h" #include "GrBuffer.h" #include "GrPathRange.h" #include "SkImageInfo.h" #include "SkScalerContext.h" class GrBackendRenderTarget; +class GrBackendSemaphore; +class GrBackendTexture; class GrGpu; class GrPath; class GrRenderTarget; @@ -227,7 +228,10 @@ public: */ GrGpuResource* findAndRefResourceByUniqueKey(const GrUniqueKey&); - sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(); + sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned = true); + + sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore&, + GrWrapOwnership = kBorrow_GrWrapOwnership); // Takes the GrSemaphore and sets the ownership of the semaphore to the GrGpu object used by // this class. This call is only used when passing a GrSemaphore from one context to another. diff --git a/src/gpu/GrSemaphore.h b/src/gpu/GrSemaphore.h index b4843ff780..f6148b015a 100644 --- a/src/gpu/GrSemaphore.h +++ b/src/gpu/GrSemaphore.h @@ -10,6 +10,7 @@ #include "SkRefCnt.h" +class GrBackendSemaphore; class GrGpu; class GrSemaphore : public SkRefCnt { @@ -20,9 +21,15 @@ private: // GrSemaphore should not be used with its old context. void resetGpu(const GrGpu* gpu) { fGpu = gpu; } + // The derived class will init the GrBackendSemaphore. This is used when flushing with signal + // semaphores so we can set the clients GrBackendSemaphore object after we've created the + // internal semaphore. + virtual void setBackendSemaphore(GrBackendSemaphore*) const = 0; + protected: explicit GrSemaphore(const GrGpu* gpu) : fGpu(gpu) {} + friend class GrRenderTargetContext; // setBackendSemaphore friend class GrResourceProvider; // resetGpu const GrGpu* fGpu; diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index be842baf67..ee6e88d33a 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1730,10 +1730,23 @@ bool SkGpuDevice::onShouldDisableLCD(const SkPaint& paint) const { return GrTextUtils::ShouldDisableLCD(paint); } +/////////////////////////////////////////////////////////////////////////////// + void SkGpuDevice::flush() { + this->flushAndSignalSemaphores(0, nullptr); +} + +void SkGpuDevice::flushAndSignalSemaphores(int numSemaphores, + GrBackendSemaphore* signalSemaphores) { + ASSERT_SINGLE_OWNER + + fRenderTargetContext->prepareForExternalIO(numSemaphores, signalSemaphores); +} + +void SkGpuDevice::wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) { ASSERT_SINGLE_OWNER - fRenderTargetContext->prepareForExternalIO(); + fRenderTargetContext->waitOnSemaphores(numSemaphores, waitSemaphores); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h index 0b60a96516..317533db82 100644 --- a/src/gpu/SkGpuDevice.h +++ b/src/gpu/SkGpuDevice.h @@ -118,6 +118,8 @@ public: sk_sp<SkSpecialImage> snapSpecial() override; void flush() override; + void flushAndSignalSemaphores(int numSemaphores, GrBackendSemaphore* signalSemaphores); + void wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores); bool onAccessPixels(SkPixmap*) override; diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index d52b73b2e8..95791f12bb 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -599,7 +599,7 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, } if (kGL_GrGLStandard == standard) { - if ((version >= GR_GL_VER(4, 0) || ctxInfo.hasExtension("GL_ARB_sample_shading")) && + if ((version >= GR_GL_VER(4, 0) || ctxInfo.hasExtension("GL_ARB_sample_shading")) && ctxInfo.vendor() != kIntel_GrGLVendor) { fSampleShadingSupport = true; } @@ -615,6 +615,12 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, } else if (version >= GR_GL_VER(3, 0)) { fFenceSyncSupport = true; } +#ifdef SK_BUILD_FOR_MAC + if (kIntel_GrGLVendor == ctxInfo.vendor()) { + // See skia:6770 + fFenceSyncSupport = false; + } +#endif // Safely moving textures between contexts requires fences. fCrossContextTextureSupport = fFenceSyncSupport; diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp index b3f8c2caa8..d40160044c 100644 --- a/src/gpu/gl/GrGLGpu.cpp +++ b/src/gpu/gl/GrGLGpu.cpp @@ -9,6 +9,7 @@ #include <cmath> #include "../private/GrGLSL.h" +#include "GrBackendSemaphore.h" #include "GrBackendSurface.h" #include "GrFixedClip.h" #include "GrGLBuffer.h" @@ -4288,10 +4289,16 @@ void GrGLGpu::deleteFence(GrFence fence) const { this->deleteSync((GrGLsync)fence); } -sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrGLGpu::makeSemaphore() { - return GrGLSemaphore::Make(this); +sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrGLGpu::makeSemaphore(bool isOwned) { + return GrGLSemaphore::Make(this, isOwned); } +sk_sp<GrSemaphore> GrGLGpu::wrapBackendSemaphore(const GrBackendSemaphore& semaphore, + GrWrapOwnership ownership) { + return GrGLSemaphore::MakeWrapped(this, semaphore.glSync(), ownership); +} + + void GrGLGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) { GrGLSemaphore* glSem = static_cast<GrGLSemaphore*>(semaphore.get()); @@ -4316,7 +4323,7 @@ void GrGLGpu::deleteSync(GrGLsync sync) const { sk_sp<GrSemaphore> GrGLGpu::prepareTextureForCrossContextUsage(GrTexture* texture) { // Set up a semaphore to be signaled once the data is ready, and flush GL - sk_sp<GrSemaphore> semaphore = this->makeSemaphore(); + sk_sp<GrSemaphore> semaphore = this->makeSemaphore(true); this->insertSemaphore(semaphore, true); return semaphore; diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h index f121df3f01..80a12eb7cc 100644 --- a/src/gpu/gl/GrGLGpu.h +++ b/src/gpu/gl/GrGLGpu.h @@ -166,7 +166,9 @@ public: bool waitFence(GrFence, uint64_t timeout) override; void deleteFence(GrFence) const override; - sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore() override; + sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override; + sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore, + GrWrapOwnership ownership) override; void insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) override; void waitSemaphore(sk_sp<GrSemaphore> semaphore) override; diff --git a/src/gpu/gl/GrGLSemaphore.h b/src/gpu/gl/GrGLSemaphore.h index f439ebd294..cfc3de9951 100644 --- a/src/gpu/gl/GrGLSemaphore.h +++ b/src/gpu/gl/GrGLSemaphore.h @@ -10,16 +10,26 @@ #include "GrSemaphore.h" -class GrGLGpu; +#include "GrBackendSemaphore.h" +#include "GrGLGpu.h" class GrGLSemaphore : public GrSemaphore { public: - static sk_sp<GrGLSemaphore> Make(const GrGLGpu* gpu) { - return sk_sp<GrGLSemaphore>(new GrGLSemaphore(gpu)); + static sk_sp<GrGLSemaphore> Make(const GrGLGpu* gpu, bool isOwned) { + return sk_sp<GrGLSemaphore>(new GrGLSemaphore(gpu, isOwned)); + } + + static sk_sp<GrGLSemaphore> MakeWrapped(const GrGLGpu* gpu, + GrGLsync sync, + GrWrapOwnership ownership) { + auto sema = sk_sp<GrGLSemaphore>(new GrGLSemaphore(gpu, + kBorrow_GrWrapOwnership != ownership)); + sema->setSync(sync); + return sema; } ~GrGLSemaphore() override { - if (fGpu) { + if (fIsOwned && fGpu) { static_cast<const GrGLGpu*>(fGpu)->deleteSync(fSync); } } @@ -28,9 +38,14 @@ public: void setSync(const GrGLsync& sync) { fSync = sync; } private: - GrGLSemaphore(const GrGLGpu* gpu) : INHERITED(gpu), fSync(0) {} + GrGLSemaphore(const GrGLGpu* gpu, bool isOwned) : INHERITED(gpu), fSync(0), fIsOwned(isOwned) {} + + void setBackendSemaphore(GrBackendSemaphore* backendSemaphore) const override { + backendSemaphore->initGL(fSync); + } GrGLsync fSync; + bool fIsOwned; typedef GrSemaphore INHERITED; }; diff --git a/src/gpu/ops/GrSemaphoreOp.cpp b/src/gpu/ops/GrSemaphoreOp.cpp index e83096d52a..f50d9c056d 100644 --- a/src/gpu/ops/GrSemaphoreOp.cpp +++ b/src/gpu/ops/GrSemaphoreOp.cpp @@ -14,15 +14,17 @@ class GrSignalSemaphoreOp final : public GrSemaphoreOp { public: DEFINE_OP_CLASS_ID - static std::unique_ptr<GrSignalSemaphoreOp> Make(sk_sp<GrSemaphore> semaphore) { - return std::unique_ptr<GrSignalSemaphoreOp>(new GrSignalSemaphoreOp(std::move(semaphore))); + static std::unique_ptr<GrSignalSemaphoreOp> Make(sk_sp<GrSemaphore> semaphore, + GrRenderTargetProxy* proxy) { + return std::unique_ptr<GrSignalSemaphoreOp>(new GrSignalSemaphoreOp(std::move(semaphore), + proxy)); } const char* name() const override { return "SignalSemaphore"; } private: - explicit GrSignalSemaphoreOp(sk_sp<GrSemaphore> semaphore) - : INHERITED(ClassID(), std::move(semaphore)) {} + explicit GrSignalSemaphoreOp(sk_sp<GrSemaphore> semaphore, GrRenderTargetProxy* proxy) + : INHERITED(ClassID(), std::move(semaphore), proxy) {} void onExecute(GrOpFlushState* state) override { state->gpu()->insertSemaphore(fSemaphore); @@ -35,15 +37,17 @@ class GrWaitSemaphoreOp final : public GrSemaphoreOp { public: DEFINE_OP_CLASS_ID - static std::unique_ptr<GrWaitSemaphoreOp> Make(sk_sp<GrSemaphore> semaphore) { - return std::unique_ptr<GrWaitSemaphoreOp>(new GrWaitSemaphoreOp(std::move(semaphore))); + static std::unique_ptr<GrWaitSemaphoreOp> Make(sk_sp<GrSemaphore> semaphore, + GrRenderTargetProxy* proxy) { + return std::unique_ptr<GrWaitSemaphoreOp>(new GrWaitSemaphoreOp(std::move(semaphore), + proxy)); } const char* name() const override { return "WaitSemaphore"; } private: - explicit GrWaitSemaphoreOp(sk_sp<GrSemaphore> semaphore) - : INHERITED(ClassID(), std::move(semaphore)) {} + explicit GrWaitSemaphoreOp(sk_sp<GrSemaphore> semaphore, GrRenderTargetProxy* proxy) + : INHERITED(ClassID(), std::move(semaphore), proxy) {} void onExecute(GrOpFlushState* state) override { state->gpu()->waitSemaphore(fSemaphore); @@ -54,12 +58,14 @@ private: //////////////////////////////////////////////////////////////////////////////// -std::unique_ptr<GrSemaphoreOp> GrSemaphoreOp::MakeSignal(sk_sp<GrSemaphore> semaphore) { - return GrSignalSemaphoreOp::Make(std::move(semaphore)); +std::unique_ptr<GrSemaphoreOp> GrSemaphoreOp::MakeSignal(sk_sp<GrSemaphore> semaphore, + GrRenderTargetProxy* proxy) { + return GrSignalSemaphoreOp::Make(std::move(semaphore), proxy); } -std::unique_ptr<GrSemaphoreOp> GrSemaphoreOp::MakeWait(sk_sp<GrSemaphore> semaphore) { - return GrWaitSemaphoreOp::Make(std::move(semaphore)); +std::unique_ptr<GrSemaphoreOp> GrSemaphoreOp::MakeWait(sk_sp<GrSemaphore> semaphore, + GrRenderTargetProxy* proxy) { + return GrWaitSemaphoreOp::Make(std::move(semaphore), proxy); } diff --git a/src/gpu/ops/GrSemaphoreOp.h b/src/gpu/ops/GrSemaphoreOp.h index a88b66cfc7..af9566aefd 100644 --- a/src/gpu/ops/GrSemaphoreOp.h +++ b/src/gpu/ops/GrSemaphoreOp.h @@ -10,18 +10,24 @@ #include "GrOp.h" +#include "GrRenderTargetProxy.h" #include "GrSemaphore.h" #include "SkRefCnt.h" class GrSemaphoreOp : public GrOp { public: - static std::unique_ptr<GrSemaphoreOp> MakeSignal(sk_sp<GrSemaphore> semaphore); + static std::unique_ptr<GrSemaphoreOp> MakeSignal(sk_sp<GrSemaphore> semaphore, + GrRenderTargetProxy* proxy); - static std::unique_ptr<GrSemaphoreOp> MakeWait(sk_sp<GrSemaphore> semaphore); + static std::unique_ptr<GrSemaphoreOp> MakeWait(sk_sp<GrSemaphore> semaphore, + GrRenderTargetProxy* proxy); protected: - GrSemaphoreOp(uint32_t classId, sk_sp<GrSemaphore> semaphore) - : INHERITED(classId), fSemaphore(std::move(semaphore)) {} + GrSemaphoreOp(uint32_t classId, sk_sp<GrSemaphore> semaphore, GrRenderTargetProxy* proxy) + : INHERITED(classId), fSemaphore(std::move(semaphore)) { + this->setBounds(SkRect::MakeIWH(proxy->width(), proxy->height()), + HasAABloat::kNo, IsZeroArea::kNo); + } sk_sp<GrSemaphore> fSemaphore; diff --git a/src/gpu/vk/GrVkCommandBuffer.cpp b/src/gpu/vk/GrVkCommandBuffer.cpp index ea0a02cb34..63642d4ed0 100644 --- a/src/gpu/vk/GrVkCommandBuffer.cpp +++ b/src/gpu/vk/GrVkCommandBuffer.cpp @@ -402,7 +402,7 @@ void GrVkPrimaryCommandBuffer::submitToQueue( const GrVkGpu* gpu, VkQueue queue, GrVkGpu::SyncQueue sync, - const GrVkSemaphore::Resource* signalSemaphore, + SkTArray<const GrVkSemaphore::Resource*>& signalSemaphores, SkTArray<const GrVkSemaphore::Resource*>& waitSemaphores) { SkASSERT(!fIsActive); @@ -418,23 +418,20 @@ void GrVkPrimaryCommandBuffer::submitToQueue( GR_VK_CALL(gpu->vkInterface(), ResetFences(gpu->device(), 1, &fSubmitFence)); } - if (signalSemaphore) { - this->addResource(signalSemaphore); + int signalCount = signalSemaphores.count(); + SkTArray<VkSemaphore> vkSignalSem(signalCount); + for (int i = 0; i < signalCount; ++i) { + this->addResource(signalSemaphores[i]); + vkSignalSem.push_back(signalSemaphores[i]->semaphore()); } int waitCount = waitSemaphores.count(); SkTArray<VkSemaphore> vkWaitSems(waitCount); SkTArray<VkPipelineStageFlags> vkWaitStages(waitCount); - if (waitCount) { - for (int i = 0; i < waitCount; ++i) { - this->addResource(waitSemaphores[i]); - vkWaitSems.push_back(waitSemaphores[i]->semaphore()); - vkWaitStages.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); - } - } - SkTArray<VkSemaphore> vkSignalSem; - if (signalSemaphore) { - vkSignalSem.push_back(signalSemaphore->semaphore()); + for (int i = 0; i < waitCount; ++i) { + this->addResource(waitSemaphores[i]); + vkWaitSems.push_back(waitSemaphores[i]->semaphore()); + vkWaitStages.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); } VkSubmitInfo submitInfo; diff --git a/src/gpu/vk/GrVkCommandBuffer.h b/src/gpu/vk/GrVkCommandBuffer.h index 1f3c4a50e5..b387885fff 100644 --- a/src/gpu/vk/GrVkCommandBuffer.h +++ b/src/gpu/vk/GrVkCommandBuffer.h @@ -298,7 +298,7 @@ public: const VkImageResolve* regions); void submitToQueue(const GrVkGpu* gpu, VkQueue queue, GrVkGpu::SyncQueue sync, - const GrVkSemaphore::Resource* signalSemaphore, + SkTArray<const GrVkSemaphore::Resource*>& signalSemaphores, SkTArray<const GrVkSemaphore::Resource*>& waitSemaphores); bool finished(const GrVkGpu* gpu) const; diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp index f76f06cfc0..b10e9ed59d 100644 --- a/src/gpu/vk/GrVkGpu.cpp +++ b/src/gpu/vk/GrVkGpu.cpp @@ -7,6 +7,7 @@ #include "GrVkGpu.h" +#include "GrBackendSemaphore.h" #include "GrBackendSurface.h" #include "GrContextOptions.h" #include "GrGeometryProcessor.h" @@ -191,6 +192,12 @@ void GrVkGpu::destroyResources() { } fSemaphoresToWaitOn.reset(); + for (int i = 0; i < fSemaphoresToSignal.count(); ++i) { + fSemaphoresToSignal[i]->unref(this); + } + fSemaphoresToSignal.reset(); + + fCopyManager.destroyResources(this); // must call this just before we destroy the command pool and VkDevice @@ -226,12 +233,16 @@ void GrVkGpu::disconnect(DisconnectType type) { for (int i = 0; i < fSemaphoresToWaitOn.count(); ++i) { fSemaphoresToWaitOn[i]->unrefAndAbandon(); } + for (int i = 0; i < fSemaphoresToSignal.count(); ++i) { + fSemaphoresToSignal[i]->unrefAndAbandon(); + } fCopyManager.abandonResources(); // must call this just before we destroy the command pool and VkDevice fResourceProvider.abandonResources(); } fSemaphoresToWaitOn.reset(); + fSemaphoresToSignal.reset(); #ifdef SK_ENABLE_VK_LAYERS fCallback = VK_NULL_HANDLE; #endif @@ -249,17 +260,20 @@ GrGpuCommandBuffer* GrVkGpu::createCommandBuffer( return new GrVkGpuCommandBuffer(this, colorInfo, stencilInfo); } -void GrVkGpu::submitCommandBuffer(SyncQueue sync, - const GrVkSemaphore::Resource* signalSemaphore) { +void GrVkGpu::submitCommandBuffer(SyncQueue sync) { SkASSERT(fCurrentCmdBuffer); fCurrentCmdBuffer->end(this); - fCurrentCmdBuffer->submitToQueue(this, fQueue, sync, signalSemaphore, fSemaphoresToWaitOn); + fCurrentCmdBuffer->submitToQueue(this, fQueue, sync, fSemaphoresToSignal, fSemaphoresToWaitOn); for (int i = 0; i < fSemaphoresToWaitOn.count(); ++i) { fSemaphoresToWaitOn[i]->unref(this); } fSemaphoresToWaitOn.reset(); + for (int i = 0; i < fSemaphoresToSignal.count(); ++i) { + fSemaphoresToSignal[i]->unref(this); + } + fSemaphoresToSignal.reset(); fResourceProvider.checkCommandBuffers(); @@ -1937,15 +1951,25 @@ void GrVkGpu::deleteFence(GrFence fence) const { VK_CALL(DestroyFence(this->device(), (VkFence)fence, nullptr)); } -sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrVkGpu::makeSemaphore() { - return GrVkSemaphore::Make(this); +sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrVkGpu::makeSemaphore(bool isOwned) { + return GrVkSemaphore::Make(this, isOwned); +} + +sk_sp<GrSemaphore> GrVkGpu::wrapBackendSemaphore(const GrBackendSemaphore& semaphore, + GrWrapOwnership ownership) { + return GrVkSemaphore::MakeWrapped(this, semaphore.vkSemaphore(), ownership); } -void GrVkGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore, bool /*flush*/) { +void GrVkGpu::insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) { GrVkSemaphore* vkSem = static_cast<GrVkSemaphore*>(semaphore.get()); - // We *always* flush, so ignore that parameter - this->submitCommandBuffer(kSkip_SyncQueue, vkSem->getResource()); + const GrVkSemaphore::Resource* resource = vkSem->getResource(); + resource->ref(); + fSemaphoresToSignal.push_back(resource); + + if (flush) { + this->submitCommandBuffer(kSkip_SyncQueue); + } } void GrVkGpu::waitSemaphore(sk_sp<GrSemaphore> semaphore) { diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h index f81daea336..236b34a3c1 100644 --- a/src/gpu/vk/GrVkGpu.h +++ b/src/gpu/vk/GrVkGpu.h @@ -131,7 +131,9 @@ public: bool waitFence(GrFence, uint64_t timeout) override; void deleteFence(GrFence) const override; - sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore() override; + sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override; + sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore, + GrWrapOwnership ownership) override; void insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) override; void waitSemaphore(sk_sp<GrSemaphore> semaphore) override; @@ -208,12 +210,11 @@ private: // Ends and submits the current command buffer to the queue and then creates a new command // buffer and begins it. If sync is set to kForce_SyncQueue, the function will wait for all - // work in the queue to finish before returning. If the signalSemaphore is not VK_NULL_HANDLE, - // we will signal the semaphore at the end of this command buffer. If this GrVkGpu object has - // any semaphores in fSemaphoresToWaitOn, we will add those wait semaphores to this command - // buffer when submitting. - void submitCommandBuffer(SyncQueue sync, - const GrVkSemaphore::Resource* signalSemaphore = nullptr); + // work in the queue to finish before returning. If this GrVkGpu object has any semaphores in + // fSemaphoreToSignal, we will add those signal semaphores to the submission of this command + // buffer. If this GrVkGpu object has any semaphores in fSemaphoresToWaitOn, we will add those + // wait semaphores to the submission of this command buffer. + void submitCommandBuffer(SyncQueue sync); void internalResolveRenderTarget(GrRenderTarget* target, bool requiresSubmit); @@ -267,6 +268,7 @@ private: GrVkPrimaryCommandBuffer* fCurrentCmdBuffer; SkSTArray<1, const GrVkSemaphore::Resource*> fSemaphoresToWaitOn; + SkSTArray<1, const GrVkSemaphore::Resource*> fSemaphoresToSignal; VkPhysicalDeviceMemoryProperties fPhysDevMemProps; diff --git a/src/gpu/vk/GrVkSemaphore.cpp b/src/gpu/vk/GrVkSemaphore.cpp index d84635f85e..d201458b27 100644 --- a/src/gpu/vk/GrVkSemaphore.cpp +++ b/src/gpu/vk/GrVkSemaphore.cpp @@ -7,6 +7,7 @@ #include "GrVkSemaphore.h" +#include "GrBackendSemaphore.h" #include "GrVkGpu.h" #include "GrVkUtil.h" @@ -15,7 +16,7 @@ #undef CreateSemaphore #endif -sk_sp<GrVkSemaphore> GrVkSemaphore::Make(const GrVkGpu* gpu) { +sk_sp<GrVkSemaphore> GrVkSemaphore::Make(const GrVkGpu* gpu, bool isOwned) { VkSemaphoreCreateInfo createInfo; memset(&createInfo, 0, sizeof(VkFenceCreateInfo)); createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; @@ -25,11 +26,22 @@ sk_sp<GrVkSemaphore> GrVkSemaphore::Make(const GrVkGpu* gpu) { GR_VK_CALL_ERRCHECK(gpu->vkInterface(), CreateSemaphore(gpu->device(), &createInfo, nullptr, &semaphore)); - return sk_sp<GrVkSemaphore>(new GrVkSemaphore(gpu, semaphore)); + return sk_sp<GrVkSemaphore>(new GrVkSemaphore(gpu, semaphore, isOwned)); } -GrVkSemaphore::GrVkSemaphore(const GrVkGpu* gpu, VkSemaphore semaphore) : INHERITED(gpu) { - fResource = new Resource(semaphore); +sk_sp<GrVkSemaphore> GrVkSemaphore::MakeWrapped(const GrVkGpu* gpu, + VkSemaphore semaphore, + GrWrapOwnership ownership) { + if (VK_NULL_HANDLE == semaphore) { + return nullptr; + } + return sk_sp<GrVkSemaphore>(new GrVkSemaphore(gpu, semaphore, + kBorrow_GrWrapOwnership != ownership)); +} + +GrVkSemaphore::GrVkSemaphore(const GrVkGpu* gpu, VkSemaphore semaphore, bool isOwned) + : INHERITED(gpu) { + fResource = new Resource(semaphore, isOwned); } GrVkSemaphore::~GrVkSemaphore() { @@ -41,7 +53,13 @@ GrVkSemaphore::~GrVkSemaphore() { } void GrVkSemaphore::Resource::freeGPUData(const GrVkGpu* gpu) const { - GR_VK_CALL(gpu->vkInterface(), - DestroySemaphore(gpu->device(), fSemaphore, nullptr)); + if (fIsOwned) { + GR_VK_CALL(gpu->vkInterface(), + DestroySemaphore(gpu->device(), fSemaphore, nullptr)); + } +} + +void GrVkSemaphore::setBackendSemaphore(GrBackendSemaphore* backendSemaphore) const { + backendSemaphore->initVulkan(fResource->semaphore()); } diff --git a/src/gpu/vk/GrVkSemaphore.h b/src/gpu/vk/GrVkSemaphore.h index 0a3bc1742b..b99eb944f7 100644 --- a/src/gpu/vk/GrVkSemaphore.h +++ b/src/gpu/vk/GrVkSemaphore.h @@ -13,17 +13,23 @@ #include "vk/GrVkTypes.h" +class GrBackendSemaphore; class GrVkGpu; class GrVkSemaphore : public GrSemaphore { public: - static sk_sp<GrVkSemaphore> Make(const GrVkGpu* gpu); + static sk_sp<GrVkSemaphore> Make(const GrVkGpu* gpu, bool isOwned); + + static sk_sp<GrVkSemaphore> MakeWrapped(const GrVkGpu* gpu, + VkSemaphore semaphore, + GrWrapOwnership); ~GrVkSemaphore() override; class Resource : public GrVkResource { public: - Resource(VkSemaphore semaphore) : INHERITED(), fSemaphore(semaphore) {} + Resource(VkSemaphore semaphore, bool isOwned) + : INHERITED(), fSemaphore(semaphore), fIsOwned(isOwned) {} ~Resource() override {} @@ -38,6 +44,7 @@ public: void freeGPUData(const GrVkGpu* gpu) const override; VkSemaphore fSemaphore; + bool fIsOwned; typedef GrVkResource INHERITED; }; @@ -45,7 +52,9 @@ public: const Resource* getResource() const { return fResource; } private: - GrVkSemaphore(const GrVkGpu* gpu, VkSemaphore semaphore); + GrVkSemaphore(const GrVkGpu* gpu, VkSemaphore semaphore, bool isOwned); + + void setBackendSemaphore(GrBackendSemaphore*) const override; const Resource* fResource; diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp index cd2a5f185f..fbd9f836d6 100644 --- a/src/image/SkSurface.cpp +++ b/src/image/SkSurface.cpp @@ -184,7 +184,19 @@ bool SkSurface::getRenderTargetHandle(GrBackendObject* obj, BackendHandleAccess } void SkSurface::prepareForExternalIO() { - asSB(this)->onPrepareForExternalIO(); + this->flush(); +} + +void SkSurface::flush() { + asSB(this)->onFlush(0, nullptr); +} + +void SkSurface::flushAndSignalSemaphores(int numSemaphores, GrBackendSemaphore* signalSemaphores) { + return asSB(this)->onFlush(numSemaphores, signalSemaphores); +} + +void SkSurface::wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) { + asSB(this)->onWait(numSemaphores, waitSemaphores); } ////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h index 1b0f9ff975..264a86fc9d 100644 --- a/src/image/SkSurface_Base.h +++ b/src/image/SkSurface_Base.h @@ -77,8 +77,17 @@ public: /** * Issue any pending surface IO to the current backend 3D API and resolve any surface MSAA. + * Inserts the requested number of semaphores for the gpu to signal when work is complete on the + * gpu and inits the array of GrBackendSemaphores with the signaled semaphores. */ - virtual void onPrepareForExternalIO() {} + virtual void onFlush(int numSemaphores, GrBackendSemaphore* signalSemaphores) {} + + /** + * Caused the current backend 3D API to wait on the passed in semaphores before executing new + * commands on the gpu. Any previously submitting commands will not be blocked by these + * semaphores. + */ + virtual void onWait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) {} inline SkCanvas* getCachedCanvas(); inline sk_sp<SkImage> refCachedImage(); diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp index d55888767a..0f4b2cb84e 100644 --- a/src/image/SkSurface_Gpu.cpp +++ b/src/image/SkSurface_Gpu.cpp @@ -166,8 +166,12 @@ void SkSurface_Gpu::onDiscard() { fDevice->accessRenderTargetContext()->discard(); } -void SkSurface_Gpu::onPrepareForExternalIO() { - fDevice->flush(); +void SkSurface_Gpu::onFlush(int numSemaphores, GrBackendSemaphore* signalSemaphores) { + fDevice->flushAndSignalSemaphores(numSemaphores, signalSemaphores); +} + +void SkSurface_Gpu::onWait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) { + fDevice->wait(numSemaphores, waitSemaphores); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/image/SkSurface_Gpu.h b/src/image/SkSurface_Gpu.h index cc8b87dc41..e22ae10bd8 100644 --- a/src/image/SkSurface_Gpu.h +++ b/src/image/SkSurface_Gpu.h @@ -26,7 +26,8 @@ public: sk_sp<SkImage> onNewImageSnapshot() override; void onCopyOnWrite(ContentChangeMode) override; void onDiscard() override; - void onPrepareForExternalIO() override; + void onFlush(int numSemaphores, GrBackendSemaphore* signalSemaphores) override; + void onWait(int numSemaphores, const GrBackendSemaphore* waitSemaphores) override; SkGpuDevice* getDevice() { return fDevice.get(); } diff --git a/tests/OnFlushCallbackTest.cpp b/tests/OnFlushCallbackTest.cpp index f4dd25f3fe..81cd5daa1d 100644 --- a/tests/OnFlushCallbackTest.cpp +++ b/tests/OnFlushCallbackTest.cpp @@ -9,6 +9,7 @@ #if SK_SUPPORT_GPU +#include "GrBackendSemaphore.h" #include "GrClip.h" #include "GrContextPriv.h" #include "GrDefaultGeoProcFactory.h" @@ -575,7 +576,7 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(OnFlushCallbackTest, reporter, ctxInfo) { rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r); } - rtc->prepareForExternalIO(); + rtc->prepareForExternalIO(0, nullptr); SkBitmap readBack; readBack.allocN32Pixels(kFinalWidth, kFinalHeight); diff --git a/tests/SurfaceSemaphoreTest.cpp b/tests/SurfaceSemaphoreTest.cpp new file mode 100644 index 0000000000..72e8e05401 --- /dev/null +++ b/tests/SurfaceSemaphoreTest.cpp @@ -0,0 +1,175 @@ +/* + * Copyright 2017 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" + +#if SK_SUPPORT_GPU +#include "GrContextFactory.h" +#include "GrTest.h" +#include "Test.h" + +#include "GrBackendSemaphore.h" +#include "GrBackendSurface.h" +#include "SkCanvas.h" +#include "SkSurface.h" + +#ifdef SK_VULKAN +#include "vk/GrVkTypes.h" +#endif + +static const int MAIN_W = 8, MAIN_H = 16; +static const int CHILD_W = 16, CHILD_H = 16; + +void check_pixels(skiatest::Reporter* reporter, const SkBitmap& bitmap) { + const uint32_t* canvasPixels = static_cast<const uint32_t*>(bitmap.getPixels()); + + bool failureFound = false; + SkPMColor expectedPixel; + for (int cy = 0; cy < CHILD_H && !failureFound; ++cy) { + for (int cx = 0; cx < CHILD_W && !failureFound; ++cx) { + SkPMColor canvasPixel = canvasPixels[cy * CHILD_W + cx]; + if (cy < CHILD_H / 2) { + if (cx < CHILD_W / 2) { + expectedPixel = 0xFF0000FF; // Red + } else { + expectedPixel = 0xFFFF0000; // Blue + } + } else { + expectedPixel = 0xFF00FF00; // Green + } + if (expectedPixel != canvasPixel) { + failureFound = true; + ERRORF(reporter, "Wrong color at %d, %d. Got 0x%08x when we expected 0x%08x", + cx, cy, canvasPixel, expectedPixel); + } + } + } +} + +void draw_child(skiatest::Reporter* reporter, + const sk_gpu_test::ContextInfo& childInfo, + const GrBackendObject& backendImage, + const GrBackendSemaphore& semaphore) { + GrBackendTexture backendTexture = GrTest::CreateBackendTexture(childInfo.backend(), + MAIN_W, MAIN_H, + kRGBA_8888_GrPixelConfig, + backendImage); + + childInfo.testContext()->makeCurrent(); + + const SkImageInfo childII = SkImageInfo::Make(CHILD_W, CHILD_H, kRGBA_8888_SkColorType, + kPremul_SkAlphaType); + + GrContext* childCtx = childInfo.grContext(); + sk_sp<SkSurface> childSurface(SkSurface::MakeRenderTarget(childCtx, SkBudgeted::kNo, + childII, 0, kTopLeft_GrSurfaceOrigin, + nullptr)); + + sk_sp<SkImage> childImage = SkImage::MakeFromTexture(childCtx, + backendTexture, + kTopLeft_GrSurfaceOrigin, + kPremul_SkAlphaType, + nullptr); + + SkCanvas* childCanvas = childSurface->getCanvas(); + childCanvas->clear(SK_ColorRED); + + childSurface->wait(1, &semaphore); + + childCanvas->drawImage(childImage, CHILD_W/2, 0); + + SkPaint paint; + paint.setColor(SK_ColorGREEN); + SkIRect rect = SkIRect::MakeLTRB(0, CHILD_H/2, CHILD_W, CHILD_H); + childCanvas->drawIRect(rect, paint); + + // read pixels + SkBitmap bitmap; + bitmap.allocPixels(childII); + childCanvas->readPixels(bitmap, 0, 0); + + check_pixels(reporter, bitmap); +} + +void surface_semaphore_test(skiatest::Reporter* reporter, + const sk_gpu_test::ContextInfo& mainInfo, + const sk_gpu_test::ContextInfo& childInfo1, + const sk_gpu_test::ContextInfo& childInfo2) { + GrContext* mainCtx = mainInfo.grContext(); + if (!mainCtx->caps()->fenceSyncSupport()) { + return; + } + + const SkImageInfo ii = SkImageInfo::Make(MAIN_W, MAIN_H, kRGBA_8888_SkColorType, + kPremul_SkAlphaType); + + sk_sp<SkSurface> mainSurface(SkSurface::MakeRenderTarget(mainCtx, SkBudgeted::kNo, + ii, 0, kTopLeft_GrSurfaceOrigin, + nullptr)); + SkCanvas* mainCanvas = mainSurface->getCanvas(); + mainCanvas->clear(SK_ColorBLUE); + + SkAutoTArray<GrBackendSemaphore> semaphores(2); + + mainSurface->flushAndSignalSemaphores(2, semaphores.get()); + + sk_sp<SkImage> mainImage = mainSurface->makeImageSnapshot(); + GrBackendObject backendImage = mainImage->getTextureHandle(false); + + draw_child(reporter, childInfo1, backendImage, semaphores[0]); + +#ifdef SK_VULKAN + if (kVulkan_GrBackend == mainInfo.backend()) { + // In Vulkan we need to make sure we are sending the correct VkImageLayout in with the + // backendImage. After the first child draw the layout gets changed to SHADER_READ, so + // we just manually set that here. + GrVkImageInfo* vkInfo = (GrVkImageInfo*)backendImage; + vkInfo->updateImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + } +#endif + + draw_child(reporter, childInfo2, backendImage, semaphores[1]); +} + +DEF_GPUTEST(SurfaceSemaphores, reporter, factory) { +#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC) + static constexpr auto kNativeGLType = sk_gpu_test::GrContextFactory::kGL_ContextType; +#else + static constexpr auto kNativeGLType = sk_gpu_test::GrContextFactory::kGLES_ContextType; +#endif + + for (int typeInt = 0; typeInt < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++typeInt) { + sk_gpu_test::GrContextFactory::ContextType contextType = + (sk_gpu_test::GrContextFactory::ContextType) typeInt; + // Use "native" instead of explicitly trying OpenGL and OpenGL ES. Do not use GLES on + // desktop since tests do not account for not fixing http://skbug.com/2809 + if (contextType == sk_gpu_test::GrContextFactory::kGL_ContextType || + contextType == sk_gpu_test::GrContextFactory::kGLES_ContextType) { + if (contextType != kNativeGLType) { + continue; + } + } + sk_gpu_test::ContextInfo ctxInfo = factory->getContextInfo( + contextType, sk_gpu_test::GrContextFactory::ContextOverrides::kDisableNVPR); + if (!sk_gpu_test::GrContextFactory::IsRenderingContext(contextType)) { + continue; + } + skiatest::ReporterContext ctx( + reporter, SkString(sk_gpu_test::GrContextFactory::ContextTypeName(contextType))); + if (ctxInfo.grContext()) { + sk_gpu_test::ContextInfo child1 = factory->getSharedContextInfo(ctxInfo.grContext(), 0); + sk_gpu_test::ContextInfo child2 = factory->getSharedContextInfo(ctxInfo.grContext(), 1); + if (!child1.grContext() || !child2.grContext()) { + continue; + } + + surface_semaphore_test(reporter, ctxInfo, child1, child2); + } + } +} + +#endif diff --git a/tools/gpu/GrContextFactory.h b/tools/gpu/GrContextFactory.h index dff8d5180c..508249b8d8 100644 --- a/tools/gpu/GrContextFactory.h +++ b/tools/gpu/GrContextFactory.h @@ -83,6 +83,37 @@ public: } } + static const char* ContextTypeName(ContextType contextType) { + switch (contextType) { + case kGL_ContextType: + return "OpenGL"; + case kGLES_ContextType: + return "OpenGLES"; + case kANGLE_D3D9_ES2_ContextType: + return "ANGLE D3D9 ES2"; + case kANGLE_D3D11_ES2_ContextType: + return "ANGLE D3D11 ES2"; + case kANGLE_D3D11_ES3_ContextType: + return "ANGLE D3D11 ES3"; + case kANGLE_GL_ES2_ContextType: + return "ANGLE GL ES2"; + case kANGLE_GL_ES3_ContextType: + return "ANGLE GL ES3"; + case kCommandBuffer_ContextType: + return "Command Buffer"; + case kMESA_ContextType: + return "Mesa"; + case kNullGL_ContextType: + return "Null GL"; + case kDebugGL_ContextType: + return "Debug GL"; + case kVulkan_ContextType: + return "Vulkan"; + } + SkDEBUGFAIL("Unreachable"); + return "Unknown"; + } + explicit GrContextFactory(const GrContextOptions& opts); GrContextFactory(); diff --git a/tools/gpu/GrTest.cpp b/tools/gpu/GrTest.cpp index f182a966bc..93e5f6e3b9 100644 --- a/tools/gpu/GrTest.cpp +++ b/tools/gpu/GrTest.cpp @@ -346,7 +346,9 @@ public: bool waitFence(GrFence, uint64_t) override { return true; } void deleteFence(GrFence) const override {} - sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore() override { return nullptr; } + sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT makeSemaphore(bool isOwned) override { return nullptr; } + sk_sp<GrSemaphore> wrapBackendSemaphore(const GrBackendSemaphore& semaphore, + GrWrapOwnership ownership) override { return nullptr; } void insertSemaphore(sk_sp<GrSemaphore> semaphore, bool flush) override {} void waitSemaphore(sk_sp<GrSemaphore> semaphore) override {} sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override { return nullptr; } |