diff --git a/BUILD.gn b/BUILD.gn index cf6c5595c8..cd64d11303 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -2655,8 +2655,15 @@ if (skia_enable_tools) { if (skia_use_metal) { sources += [ "tools/sk_app/MetalWindowContext.mm" ] sources += [ "tools/sk_app/MetalWindowContext.h" ] + if (skia_enable_graphite) { + sources += [ "tools/sk_app/GraphiteMetalWindowContext.mm" ] + sources += [ "tools/sk_app/GraphiteMetalWindowContext.h" ] + } if (is_mac) { sources += [ "tools/sk_app/mac/MetalWindowContext_mac.mm" ] + if (skia_enable_graphite) { + sources += [ "tools/sk_app/mac/GraphiteMetalWindowContext_mac.mm" ] + } } else if (is_ios) { sources += [ "tools/sk_app/ios/MetalWindowContext_ios.mm" ] } diff --git a/tools/sk_app/GraphiteMetalWindowContext.h b/tools/sk_app/GraphiteMetalWindowContext.h new file mode 100644 index 0000000000..46c77327a5 --- /dev/null +++ b/tools/sk_app/GraphiteMetalWindowContext.h @@ -0,0 +1,56 @@ +/* + * Copyright 2021 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef GraphiteMetalWindowContext_DEFINED +#define GraphiteMetalWindowContext_DEFINED + +#include "include/core/SkRefCnt.h" +#include "include/ports/SkCFObject.h" + +#include "tools/sk_app/WindowContext.h" + +#import +#import + +class SkSurface; + +namespace sk_app { + +class GraphiteMetalWindowContext : public WindowContext { +public: + sk_sp getBackbufferSurface() override; + + bool isValid() override { return fValid; } + + void swapBuffers() override; + + void setDisplayParams(const DisplayParams& params) override; + + void activate(bool isActive) override; + +protected: + GraphiteMetalWindowContext(const DisplayParams&); + // This should be called by subclass constructor. It is also called when window/display + // parameters change. This will in turn call onInitializeContext(). + void initializeContext(); + virtual bool onInitializeContext() = 0; + + // This should be called by subclass destructor. It is also called when window/display + // parameters change prior to initializing a new Metal context. This will in turn call + // onDestroyContext(). + void destroyContext(); + virtual void onDestroyContext() = 0; + + bool fValid; + sk_cfp> fDevice; + sk_cfp> fQueue; + CAMetalLayer* fMetalLayer; + CFTypeRef fDrawableHandle; +}; + +} // namespace sk_app + +#endif diff --git a/tools/sk_app/GraphiteMetalWindowContext.mm b/tools/sk_app/GraphiteMetalWindowContext.mm new file mode 100644 index 0000000000..f7277e18f8 --- /dev/null +++ b/tools/sk_app/GraphiteMetalWindowContext.mm @@ -0,0 +1,127 @@ +/* + * Copyright 2021 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "include/core/SkSurface.h" +#include "src/core/SkMathPriv.h" +#include "tools/sk_app/GraphiteMetalWindowContext.h" + +#include "experimental/graphite/include/Context.h" +#include "experimental/graphite/include/mtl/MtlBackendContext.h" + +using sk_app::DisplayParams; +using sk_app::GraphiteMetalWindowContext; + +namespace sk_app { + +GraphiteMetalWindowContext::GraphiteMetalWindowContext(const DisplayParams& params) + : WindowContext(params) + , fValid(false) + , fDrawableHandle(nil) { + fDisplayParams.fMSAASampleCount = GrNextPow2(fDisplayParams.fMSAASampleCount); +} + +void GraphiteMetalWindowContext::initializeContext() { + SkASSERT(!fContext); + SkASSERT(!fGraphiteContext); + + fDevice.reset(MTLCreateSystemDefaultDevice()); + fQueue.reset([*fDevice newCommandQueue]); + + if (fDisplayParams.fMSAASampleCount > 1) { + if (@available(macOS 10.11, iOS 9.0, *)) { + if (![*fDevice supportsTextureSampleCount:fDisplayParams.fMSAASampleCount]) { + return; + } + } else { + return; + } + } + fSampleCount = fDisplayParams.fMSAASampleCount; + fStencilBits = 8; + + fValid = this->onInitializeContext(); + + skgpu::mtl::BackendContext backendContext = {}; + backendContext.fDevice.retain((GrMTLHandle)fDevice.get()); + backendContext.fQueue.retain((GrMTLHandle)fQueue.get()); + fGraphiteContext = skgpu::Context::MakeMetal(backendContext); + // TODO +// if (!fGraphiteContext && fDisplayParams.fMSAASampleCount > 1) { +// fDisplayParams.fMSAASampleCount /= 2; +// this->initializeContext(); +// return; +// } +} + +void GraphiteMetalWindowContext::destroyContext() { + if (fGraphiteContext) { + // TODO? + // in case we have outstanding refs to this (lua?) + // fGraphiteContext->abandonContext(); + fGraphiteContext.reset(); + } + + this->onDestroyContext(); + + fMetalLayer = nil; + fValid = false; + +#if GR_METAL_SDK_VERSION >= 230 + if (@available(macOS 11.0, iOS 14.0, *)) { + [fPipelineArchive release]; + } +#endif + fQueue.reset(); + fDevice.reset(); +} + +sk_sp GraphiteMetalWindowContext::getBackbufferSurface() { + sk_sp surface; + id currentDrawable = [fMetalLayer nextDrawable]; + + // TODO +// GrMtlTextureInfo fbInfo; +// fbInfo.fTexture.retain(currentDrawable.texture); +// +// GrBackendRenderTarget backendRT(fWidth, +// fHeight, +// fSampleCount, +// fbInfo); +// +// surface = SkSurface::MakeFromBackendRenderTarget(fContext.get(), backendRT, +// kTopLeft_GrSurfaceOrigin, +// kBGRA_8888_SkColorType, +// fDisplayParams.fColorSpace, +// &fDisplayParams.fSurfaceProps); + + fDrawableHandle = CFRetain((GrMTLHandle) currentDrawable); + + return surface; +} + +void GraphiteMetalWindowContext::swapBuffers() { + id currentDrawable = (id)fDrawableHandle; + + id commandBuffer([*fQueue commandBuffer]); + commandBuffer.label = @"Present"; + + [commandBuffer presentDrawable:currentDrawable]; + [commandBuffer commit]; + // ARC is off in sk_app, so we need to release the CF ref manually + CFRelease(fDrawableHandle); + fDrawableHandle = nil; +} + +void GraphiteMetalWindowContext::setDisplayParams(const DisplayParams& params) { + this->destroyContext(); + fDisplayParams = params; + this->initializeContext(); +} + +void GraphiteMetalWindowContext::activate(bool isActive) {} + +} //namespace sk_app diff --git a/tools/sk_app/Window.h b/tools/sk_app/Window.h index a2a63ee24d..ab22838ae5 100644 --- a/tools/sk_app/Window.h +++ b/tools/sk_app/Window.h @@ -24,6 +24,10 @@ class SkSurface; class SkSurfaceProps; class SkString; +namespace skgpu { +class Context; +} + namespace sk_app { class WindowContext; @@ -66,6 +70,9 @@ public: #endif #ifdef SK_METAL kMetal_BackendType, +#ifdef SK_GRAPHITE_ENABLED + kGraphiteMetal_BackendType, +#endif #endif #ifdef SK_DIRECT3D kDirect3D_BackendType, @@ -144,6 +151,7 @@ public: // Returns null if there is not a GPU backend or if the backend is not yet created. GrDirectContext* directContext() const; + skgpu::Context* graphiteContext() const; protected: Window(); diff --git a/tools/sk_app/WindowContext.cpp b/tools/sk_app/WindowContext.cpp index b470467386..581657a973 100644 --- a/tools/sk_app/WindowContext.cpp +++ b/tools/sk_app/WindowContext.cpp @@ -8,13 +8,14 @@ #include "tools/sk_app/WindowContext.h" #include "include/gpu/GrDirectContext.h" +#ifdef SK_GRAPHITE_ENABLED +#include "experimental/graphite/include/Context.h" +#endif namespace sk_app { WindowContext::WindowContext(const DisplayParams& params) - : fDisplayParams(params) - , fSampleCount(1) - , fStencilBits(0) {} + : fDisplayParams(params) {} WindowContext::~WindowContext() {} diff --git a/tools/sk_app/WindowContext.h b/tools/sk_app/WindowContext.h index f143dab013..79f6d72f35 100644 --- a/tools/sk_app/WindowContext.h +++ b/tools/sk_app/WindowContext.h @@ -14,6 +14,11 @@ class GrDirectContext; class SkSurface; +#ifdef SK_GRAPHITE_ENABLED +namespace skgpu { +class Context; +} +#endif namespace sk_app { @@ -37,6 +42,9 @@ public: virtual void setDisplayParams(const DisplayParams& params) = 0; GrDirectContext* directContext() const { return fContext.get(); } +#ifdef SK_GRAPHITE_ENABLED + skgpu::Context* graphiteContext() const { return fGraphiteContext.get(); } +#endif int width() const { return fWidth; } int height() const { return fHeight; } @@ -47,6 +55,9 @@ protected: virtual bool isGpuContext() { return true; } sk_sp fContext; +#if SK_GRAPHITE_ENABLED + sk_sp fGraphiteContext; +#endif int fWidth; int fHeight; @@ -55,8 +66,8 @@ protected: // parameters obtained from the native window // Note that the platform .cpp file is responsible for // initializing fSampleCount and fStencilBits! - int fSampleCount; - int fStencilBits; + int fSampleCount = 1; + int fStencilBits = 0; }; } // namespace sk_app diff --git a/tools/sk_app/mac/GraphiteMetalWindowContext_mac.mm b/tools/sk_app/mac/GraphiteMetalWindowContext_mac.mm new file mode 100644 index 0000000000..2ff6417a54 --- /dev/null +++ b/tools/sk_app/mac/GraphiteMetalWindowContext_mac.mm @@ -0,0 +1,109 @@ +/* + * Copyright 2021 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "tools/sk_app/GraphiteMetalWindowContext.h" +#include "tools/sk_app/mac/WindowContextFactory_mac.h" + +#import +#import + +using sk_app::DisplayParams; +using sk_app::window_context_factory::MacWindowInfo; +using sk_app::GraphiteMetalWindowContext; + +namespace { + +class GraphiteMetalWindowContext_mac : public GraphiteMetalWindowContext { +public: + GraphiteMetalWindowContext_mac(const MacWindowInfo&, const DisplayParams&); + + ~GraphiteMetalWindowContext_mac() override; + + bool onInitializeContext() override; + void onDestroyContext() override; + + void resize(int w, int h) override; + +private: + NSView* fMainView; + + using INHERITED = GraphiteMetalWindowContext; +}; + +GraphiteMetalWindowContext_mac::GraphiteMetalWindowContext_mac(const MacWindowInfo& info, + const DisplayParams& params) + : INHERITED(params) + , fMainView(info.fMainView) { + + // any config code here (particularly for msaa)? + + this->initializeContext(); +} + +GraphiteMetalWindowContext_mac::~GraphiteMetalWindowContext_mac() { + this->destroyContext(); +} + +bool GraphiteMetalWindowContext_mac::onInitializeContext() { + SkASSERT(nil != fMainView); + + fMetalLayer = [CAMetalLayer layer]; + fMetalLayer.device = fDevice.get(); + fMetalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; + + // resize ignores the passed values and uses the fMainView directly. + this->resize(0, 0); + + BOOL useVsync = fDisplayParams.fDisableVsync ? NO : YES; + fMetalLayer.displaySyncEnabled = useVsync; // TODO: need solution for 10.12 or lower + fMetalLayer.layoutManager = [CAConstraintLayoutManager layoutManager]; + fMetalLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable; + fMetalLayer.contentsGravity = kCAGravityTopLeft; + fMetalLayer.magnificationFilter = kCAFilterNearest; + NSColorSpace* cs = fMainView.window.colorSpace; + fMetalLayer.colorspace = cs.CGColorSpace; + + fMainView.layer = fMetalLayer; + fMainView.wantsLayer = YES; + + return true; +} + +void GraphiteMetalWindowContext_mac::onDestroyContext() { + fMainView.layer = nil; + fMainView.wantsLayer = NO; +} + +void GraphiteMetalWindowContext_mac::resize(int w, int h) { + CGFloat backingScaleFactor = sk_app::GetBackingScaleFactor(fMainView); + CGSize backingSize = fMainView.bounds.size; + backingSize.width *= backingScaleFactor; + backingSize.height *= backingScaleFactor; + + fMetalLayer.drawableSize = backingSize; + fMetalLayer.contentsScale = backingScaleFactor; + + fWidth = backingSize.width; + fHeight = backingSize.height; +} + +} // anonymous namespace + +namespace sk_app { +namespace window_context_factory { + +std::unique_ptr MakeGraphiteMetalForMac(const MacWindowInfo& info, + const DisplayParams& params) { + std::unique_ptr ctx(new GraphiteMetalWindowContext_mac(info, params)); + if (!ctx->isValid()) { + return nullptr; + } + return ctx; +} + +} // namespace window_context_factory +} // namespace sk_app diff --git a/tools/sk_app/mac/WindowContextFactory_mac.h b/tools/sk_app/mac/WindowContextFactory_mac.h index e772030b95..3e136a8f57 100644 --- a/tools/sk_app/mac/WindowContextFactory_mac.h +++ b/tools/sk_app/mac/WindowContextFactory_mac.h @@ -53,6 +53,9 @@ std::unique_ptr MakeDawnMTLForMac(const MacWindowInfo&, const Dis #ifdef SK_METAL std::unique_ptr MakeMetalForMac(const MacWindowInfo&, const DisplayParams&); +#ifdef SK_GRAPHITE_ENABLED +std::unique_ptr MakeGraphiteMetalForMac(const MacWindowInfo&, const DisplayParams&); +#endif #endif } // namespace window_context_factory diff --git a/tools/sk_app/mac/Window_mac.mm b/tools/sk_app/mac/Window_mac.mm index 4cdee9c2b6..621cbeeaab 100644 --- a/tools/sk_app/mac/Window_mac.mm +++ b/tools/sk_app/mac/Window_mac.mm @@ -136,6 +136,11 @@ bool Window_mac::attach(BackendType attachType) { case kMetal_BackendType: fWindowContext = MakeMetalForMac(info, fRequestedDisplayParams); break; +#ifdef SK_GRAPHITE_ENABLED + case kGraphiteMetal_BackendType: + fWindowContext = MakeGraphiteMetalForMac(info, fRequestedDisplayParams); + break; +#endif #endif #ifdef SK_GL case kNativeGL_BackendType: diff --git a/tools/viewer/Viewer.cpp b/tools/viewer/Viewer.cpp index 5731e14665..552319730d 100644 --- a/tools/viewer/Viewer.cpp +++ b/tools/viewer/Viewer.cpp @@ -200,6 +200,9 @@ const char* kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = { #endif #ifdef SK_METAL "Metal", +#ifdef SK_GRAPHITE_ENABLED + "Metal (Graphite)", +#endif #endif #ifdef SK_DIRECT3D "Direct3D", @@ -227,6 +230,11 @@ static sk_app::Window::BackendType get_backend_type(const char* str) { if (0 == strcmp(str, "mtl")) { return sk_app::Window::kMetal_BackendType; } else +#ifdef SK_GRAPHITE_ENABLED + if (0 == strcmp(str, "grmtl")) { + return sk_app::Window::kGraphiteMetal_BackendType; + } else +#endif #endif #ifdef SK_DIRECT3D if (0 == strcmp(str, "d3d")) { @@ -1881,6 +1889,11 @@ void Viewer::drawImGui() { #if defined(SK_METAL) ImGui::SameLine(); ImGui::RadioButton("Metal", &newBackend, sk_app::Window::kMetal_BackendType); +#if defined(SK_GRAPHITE_ENABLED) + ImGui::SameLine(); + ImGui::RadioButton("Metal (Graphite)", &newBackend, + sk_app::Window::kGraphiteMetal_BackendType); +#endif #endif #if defined(SK_DIRECT3D) ImGui::SameLine();