Add OpenGL context to Viewer.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1978573003

Review-Url: https://codereview.chromium.org/1978573003
This commit is contained in:
jvanverth 2016-05-17 12:15:48 -07:00 committed by Commit bot
parent c5fd5c41ab
commit 56a11e4d6f
19 changed files with 446 additions and 79 deletions

View File

@ -126,6 +126,7 @@ void GrVkBuffer::vkRelease(const GrVkGpu* gpu) {
void GrVkBuffer::vkAbandon() {
fResource->unrefAndAbandon();
fResource = nullptr;
fMapPtr = nullptr;
VALIDATE();
}

View File

@ -41,9 +41,15 @@ DEFINE_string2(match, m, nullptr,
"it is skipped unless some list entry starts with ~");
DEFINE_string(skps, "skps", "Directory to read skps from.");
const char *kBackendTypeStrings[sk_app::Window::kBackendTypeCount] = {
" [OpenGL]",
" [Vulkan]"
};
Viewer::Viewer(int argc, char** argv, void* platformData)
: fCurrentMeasurement(0)
, fDisplayStats(false)
, fBackendType(sk_app::Window::kVulkan_BackendType)
, fZoomCenterX(0.0f)
, fZoomCenterY(0.0f)
, fZoomLevel(0.0f)
@ -60,7 +66,7 @@ Viewer::Viewer(int argc, char** argv, void* platformData)
SkCommandLineFlags::Parse(argc, argv);
fWindow = Window::CreateNativeWindow(platformData);
fWindow->attach(Window::kVulkan_BackendType, DisplayParams());
fWindow->attach(fBackendType, DisplayParams());
// register callbacks
fCommands.attach(fWindow);
@ -103,6 +109,22 @@ Viewer::Viewer(int argc, char** argv, void* platformData)
this->changeZoomLevel(-1.f / 32.f);
fWindow->inval();
});
#ifndef SK_BUILD_FOR_ANDROID
fCommands.addCommand('d', "Modes", "Change rendering backend", [this]() {
fWindow->detach();
if (sk_app::Window::kVulkan_BackendType == fBackendType) {
fBackendType = sk_app::Window::kNativeGL_BackendType;
}
// TODO: get Vulkan -> OpenGL working without swapchain creation failure
//else if (sk_app::Window::kNativeGL_BackendType == fBackendType) {
// fBackendType = sk_app::Window::kVulkan_BackendType;
//}
fWindow->attach(fBackendType, DisplayParams());
this->updateTitle();
});
#endif
// set up slides
this->initSlides();
@ -179,6 +201,7 @@ void Viewer::updateTitle() {
if (kSRGB_SkColorProfileType == fWindow->getDisplayParams().fProfileType) {
title.append(" sRGB");
}
title.append(kBackendTypeStrings[fBackendType]);
fWindow->setTitle(title.c_str());
}
@ -233,7 +256,6 @@ void Viewer::updateMatrix(){
}
void Viewer::onPaint(SkCanvas* canvas) {
int count = canvas->save();
if (fWindow->supportsContentRect()) {

View File

@ -47,6 +47,8 @@ private:
bool fDisplayStats;
sk_app::Window::BackendType fBackendType;
// transform data
SkMatrix fLocalMatrix;
SkScalar fZoomCenterX;

View File

@ -15,11 +15,13 @@ struct DisplayParams {
DisplayParams()
: fColorType(kN32_SkColorType)
, fProfileType(kLinear_SkColorProfileType)
, fMSAASampleCount(0) {}
, fMSAASampleCount(0)
, fDeepColor(false) {}
SkColorType fColorType;
SkColorType fColorType;
SkColorProfileType fProfileType;
int fMSAASampleCount;
int fMSAASampleCount;
bool fDeepColor;
};
} // namespace sk_app

View File

@ -0,0 +1,112 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrContext.h"
#include "SkSurface.h"
#include "GLWindowContext.h"
#include "gl/GrGLDefines.h"
#include "gl/GrGLUtil.h"
#include "GrRenderTarget.h"
#include "GrContext.h"
#include "SkCanvas.h"
#include "SkImage_Base.h"
namespace sk_app {
GLWindowContext::GLWindowContext(void* platformData, const DisplayParams& params)
: WindowContext()
, fBackendContext(nullptr)
, fRenderTarget(nullptr)
, fSurface(nullptr) {
}
void GLWindowContext::initializeContext(void* platformData, const DisplayParams& params) {
this->onInitializeContext(platformData, params);
fDisplayParams = params;
SkAutoTUnref<const GrGLInterface> glInterface;
glInterface.reset(GrGLCreateNativeInterface());
fBackendContext.reset(GrGLInterfaceRemoveNVPR(glInterface.get()));
SkASSERT(nullptr == fContext);
fContext = GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)fBackendContext.get());
// We may not have real sRGB support (ANGLE, in particular), so check for
// that, and fall back to L32:
//
// ... and, if we're using a 10-bit/channel FB0, it doesn't do sRGB conversion on write,
// so pretend that it's non-sRGB 8888:
fPixelConfig = fContext->caps()->srgbSupport() &&
SkColorAndProfileAreGammaCorrect(fDisplayParams.fColorType,
fDisplayParams.fProfileType) &&
(fColorBits != 30) ? kSkiaGamma8888_GrPixelConfig : kSkia8888_GrPixelConfig;
}
void GLWindowContext::destroyContext() {
fSurface.reset(nullptr);
fRenderTarget.reset(nullptr);
if (fContext) {
// in case we have outstanding refs to this guy (lua?)
fContext->abandonContext();
fContext->unref();
fContext = nullptr;
}
fBackendContext.reset(nullptr);
this->onDestroyContext();
}
sk_sp<SkSurface> GLWindowContext::getBackbufferSurface() {
if (nullptr == fSurface) {
fActualColorBits = SkTMax(fColorBits, 24);
if (fContext) {
GrBackendRenderTargetDesc desc;
desc.fWidth = this->fWidth;
desc.fHeight = this->fHeight;
desc.fConfig = fPixelConfig;
desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
desc.fSampleCnt = fSampleCount;
desc.fStencilBits = fStencilBits;
GrGLint buffer;
GR_GL_CALL(fBackendContext, GetIntegerv(GR_GL_FRAMEBUFFER_BINDING, &buffer));
desc.fRenderTargetHandle = buffer;
fRenderTarget.reset(fContext->textureProvider()->wrapBackendRenderTarget(desc));
fSurface = this->createRenderSurface(fRenderTarget, fActualColorBits);
}
}
return fSurface;
}
void GLWindowContext::swapBuffers() {
this->presentRenderSurface(fSurface, fRenderTarget, fActualColorBits);
this->onSwapBuffers();
}
void GLWindowContext::resize(uint32_t w, uint32_t h) {
this->destroyContext();
this->initializeContext(nullptr, fDisplayParams);
}
void GLWindowContext::setDisplayParams(const DisplayParams& params) {
this->destroyContext();
this->initializeContext(nullptr, params);
}
} //namespace sk_app

View File

@ -0,0 +1,63 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GLWindowContext_DEFINED
#define GLWindowContext_DEFINED
#include "gl/GrGLInterface.h"
#include "SkRefCnt.h"
#include "GrRenderTarget.h"
#include "SkSurface.h"
#include "WindowContext.h"
class GrContext;
namespace sk_app {
class GLWindowContext : public WindowContext {
public:
// This is defined in the platform .cpp file
static GLWindowContext* Create(void* platformData, const DisplayParams& params);
sk_sp<SkSurface> getBackbufferSurface() override;
bool isValid() override { return SkToBool(fBackendContext.get()); }
void resize(uint32_t w, uint32_t h) override;
void swapBuffers() override;
void setDisplayParams(const DisplayParams& params) override;
GrBackendContext getBackendContext() override {
return (GrBackendContext) fBackendContext.get();
}
protected:
GLWindowContext(void*, const DisplayParams&);
void initializeContext(void*, const DisplayParams&);
virtual void onInitializeContext(void*, const DisplayParams&) = 0;
void destroyContext();
virtual void onDestroyContext() = 0;
virtual void onSwapBuffers() = 0;
SkAutoTUnref<const GrGLInterface> fBackendContext;
sk_sp<GrRenderTarget> fRenderTarget;
sk_sp<SkSurface> fSurface;
// parameters obtained from the native window
int fSampleCount;
int fStencilBits;
int fColorBits;
int fActualColorBits;
};
} // namespace sk_app
#endif

View File

@ -7,6 +7,7 @@
*/
#include "GrContext.h"
#include "GrRenderTarget.h"
#include "SkSurface.h"
#include "VulkanWindowContext.h"
@ -25,8 +26,12 @@
namespace sk_app {
VulkanWindowContext::VulkanWindowContext(void* platformData, const DisplayParams& params)
: fSurface(VK_NULL_HANDLE)
: WindowContext()
, fSurface(VK_NULL_HANDLE)
, fSwapchain(VK_NULL_HANDLE)
, fImages(nullptr)
, fImageLayouts(nullptr)
, fSurfaces(nullptr)
, fCommandPool(VK_NULL_HANDLE)
, fBackbuffers(nullptr) {
@ -250,6 +255,7 @@ void VulkanWindowContext::createBuffers(VkFormat format) {
// set up initial image layouts and create surfaces
fImageLayouts = new VkImageLayout[fImageCount];
fRenderTargets = new sk_sp<GrRenderTarget>[fImageCount];
fSurfaces = new sk_sp<SkSurface>[fImageCount];
for (uint32_t i = 0; i < fImageCount; ++i) {
fImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
@ -269,10 +275,9 @@ void VulkanWindowContext::createBuffers(VkFormat format) {
desc.fSampleCnt = 0;
desc.fStencilBits = 0;
desc.fRenderTargetHandle = (GrBackendObject) &info;
SkSurfaceProps props(GrPixelConfigIsSRGB(fPixelConfig)
? SkSurfaceProps::kGammaCorrect_Flag : 0,
kUnknown_SkPixelGeometry);
fSurfaces[i] = SkSurface::MakeFromBackendRenderTarget(fContext, desc, &props);
fRenderTargets[i].reset(fContext->textureProvider()->wrapBackendRenderTarget(desc));
fSurfaces[i] = this->createRenderSurface(fRenderTargets[i], 24);
}
// create the command pool for the command buffers
@ -361,8 +366,11 @@ void VulkanWindowContext::destroyBuffers() {
delete[] fBackbuffers;
fBackbuffers = nullptr;
// Does this actually free the surfaces?
delete[] fSurfaces;
fSurfaces = nullptr;
delete[] fRenderTargets;
fRenderTargets = nullptr;
delete[] fImageLayouts;
fImageLayouts = nullptr;
delete[] fImages;
@ -398,7 +406,8 @@ void VulkanWindowContext::destroyContext() {
fSurface = VK_NULL_HANDLE;
}
delete fContext;
fContext->abandonContext();
fContext->unref();
fBackendContext.reset(nullptr);
}
@ -419,7 +428,7 @@ VulkanWindowContext::BackbufferInfo* VulkanWindowContext::getAvailableBackbuffer
return backbuffer;
}
SkSurface* VulkanWindowContext::getBackbufferSurface() {
sk_sp<SkSurface> VulkanWindowContext::getBackbufferSurface() {
BackbufferInfo* backbuffer = this->getAvailableBackbuffer();
SkASSERT(backbuffer);
@ -510,14 +519,16 @@ SkSurface* VulkanWindowContext::getBackbufferSurface() {
QueueSubmit(fBackendContext->fQueue, 1, &submitInfo,
backbuffer->fUsageFences[0]));
return fSurfaces[backbuffer->fImageIndex].get();
return sk_ref_sp(fSurfaces[backbuffer->fImageIndex].get());
}
void VulkanWindowContext::swapBuffers() {
BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
this->presentRenderSurface(fSurfaces[backbuffer->fImageIndex],
fRenderTargets[backbuffer->fImageIndex], 24);
VkImageLayout layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;

View File

@ -13,8 +13,7 @@
#include "vk/GrVkBackendContext.h"
#include "WindowContext.h"
class SkSurface;
class GrContext;
class GrRenderTarget;
namespace sk_app {
@ -35,18 +34,15 @@ public:
return ctx;
}
SkSurface* getBackbufferSurface() override;
sk_sp<SkSurface> getBackbufferSurface() override;
void swapBuffers() override;
bool makeCurrent() override { return true; }
bool isValid() override { return SkToBool(fBackendContext.get()); }
void resize(uint32_t w, uint32_t h) override {
this->createSwapchain(w, h, fDisplayParams);
}
const DisplayParams& getDisplayParams() override { return fDisplayParams; }
void setDisplayParams(const DisplayParams& params) override {
this->createSwapchain(fWidth, fHeight, params);
}
@ -99,23 +95,21 @@ private:
VkPtr<PFN_vkQueuePresentKHR> fQueuePresentKHR;
VkPtr<PFN_vkCreateSharedSwapchainsKHR> fCreateSharedSwapchainsKHR;
GrContext* fContext;
VkSurfaceKHR fSurface;
VkSwapchainKHR fSwapchain;
uint32_t fPresentQueueIndex;
VkQueue fPresentQueue;
int fWidth;
int fHeight;
DisplayParams fDisplayParams;
GrPixelConfig fPixelConfig;
uint32_t fImageCount;
VkImage* fImages; // images in the swapchain
VkImageLayout* fImageLayouts; // layouts of these images when not color attachment
sk_sp<SkSurface>* fSurfaces; // wrapped surface for those images
VkCommandPool fCommandPool;
BackbufferInfo* fBackbuffers;
uint32_t fCurrentBackbufferIndex;
uint32_t fImageCount;
VkImage* fImages; // images in the swapchain
VkImageLayout* fImageLayouts; // layouts of these images when not color attachment
sk_sp<GrRenderTarget>* fRenderTargets; // wrapped rendertargets for those images
sk_sp<SkSurface>* fSurfaces; // surfaces client renders to (may not be based on rts)
VkCommandPool fCommandPool;
BackbufferInfo* fBackbuffers;
uint32_t fCurrentBackbufferIndex;
};
} // namespace sk_app

View File

@ -53,7 +53,7 @@ bool Window::onMouse(int x, int y, InputState state, uint32_t modifiers) {
}
void Window::onPaint() {
SkSurface* backbuffer = fWindowContext->getBackbufferSurface();
sk_sp<SkSurface> backbuffer = fWindowContext->getBackbufferSurface();
if (backbuffer) {
// draw into the canvas of this surface
SkCanvas* canvas = backbuffer->getCanvas();
@ -66,7 +66,6 @@ void Window::onPaint() {
} else {
// try recreating testcontext
}
}
void Window::onResize(uint32_t w, uint32_t h) {

View File

@ -32,12 +32,17 @@ public:
virtual bool supportsContentRect() const { return false; }
virtual SkRect getContentRect() { return SkRect::MakeEmpty(); }
enum BackEndType {
enum BackendType {
kNativeGL_BackendType,
kVulkan_BackendType
kVulkan_BackendType,
kLast_BackendType = kVulkan_BackendType
};
enum {
kBackendTypeCount = kLast_BackendType + 1
};
virtual bool attach(BackEndType attachType, const DisplayParams& params) = 0;
virtual bool attach(BackendType attachType, const DisplayParams& params) = 0;
void detach();
// input handling

View File

@ -9,30 +9,54 @@
#include "DisplayParams.h"
#include "GrTypes.h"
#include "SkRefCnt.h"
#include "SkSurfaceProps.h"
class GrContext;
class SkSurface;
class GrRenderTarget;
namespace sk_app {
// TODO: fill this out with an interface
class WindowContext {
public:
WindowContext() : fContext(nullptr)
, fSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType) {}
virtual ~WindowContext() {}
virtual SkSurface* getBackbufferSurface() = 0;
virtual sk_sp<SkSurface> getBackbufferSurface() = 0;
virtual void swapBuffers() = 0;
virtual bool makeCurrent() = 0;
virtual bool isValid() = 0;
virtual void resize(uint32_t w, uint32_t h) = 0;
virtual const DisplayParams& getDisplayParams() = 0;
const DisplayParams& getDisplayParams() { return fDisplayParams; }
virtual void setDisplayParams(const DisplayParams& params) = 0;
SkSurfaceProps getSurfaceProps() const { return fSurfaceProps; }
void setSurfaceProps(const SkSurfaceProps& props) {
fSurfaceProps = props;
}
virtual GrBackendContext getBackendContext() = 0;
sk_sp<SkSurface> createRenderSurface(sk_sp<GrRenderTarget>, int colorBits);
void presentRenderSurface(sk_sp<SkSurface> renderSurface, sk_sp<GrRenderTarget> rt,
int colorBits);
protected:
virtual bool isGpuContext() { return true; }
GrContext* fContext;
int fWidth;
int fHeight;
DisplayParams fDisplayParams;
GrPixelConfig fPixelConfig;
SkSurfaceProps fSurfaceProps;
};
} // namespace sk_app

View File

@ -41,7 +41,7 @@ void Window_android::setTitle(const char* title) {
fSkiaAndroidApp->setTitle(title);
}
bool Window_android::attach(BackEndType attachType, const DisplayParams& params) {
bool Window_android::attach(BackendType attachType, const DisplayParams& params) {
if (kVulkan_BackendType != attachType) {
return false;
}

View File

@ -26,7 +26,7 @@ public:
void setTitle(const char*) override;
void show() override {}
bool attach(BackEndType attachType, const DisplayParams& params) override;
bool attach(BackendType attachType, const DisplayParams& params) override;
void inval() override;
bool scaleContentToFit() const override { return true; }

View File

@ -0,0 +1,110 @@
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GLWindowContext_win.h"
#include <GL/gl.h>
// windows stuff
#include "win/SkWGL.h"
#include "Window_win.h"
namespace sk_app {
// platform-dependent create
GLWindowContext* GLWindowContext::Create(void* platformData, const DisplayParams& params) {
GLWindowContext_win* ctx = new GLWindowContext_win(platformData, params);
if (!ctx->isValid()) {
delete ctx;
return nullptr;
}
return ctx;
}
GLWindowContext_win::GLWindowContext_win(void* platformData, const DisplayParams& params)
: GLWindowContext(platformData, params)
, fHWND(0)
, fHGLRC(NULL) {
// any config code here (particularly for msaa)?
this->initializeContext(platformData, params);
}
GLWindowContext_win::~GLWindowContext_win() {
this->destroyContext();
}
void GLWindowContext_win::onInitializeContext(void* platformData, const DisplayParams& params) {
ContextPlatformData_win* winPlatformData =
reinterpret_cast<ContextPlatformData_win*>(platformData);
if (winPlatformData) {
fHWND = winPlatformData->fHWnd;
}
HDC dc = GetDC(fHWND);
fHGLRC = SkCreateWGLContext(dc, params.fMSAASampleCount, params.fDeepColor,
kGLPreferCompatibilityProfile_SkWGLContextRequest);
if (NULL == fHGLRC) {
return;
}
if (wglMakeCurrent(dc, fHGLRC)) {
glClearStencil(0);
glClearColor(0, 0, 0, 0);
glStencilMask(0xffffffff);
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
// use DescribePixelFormat to get the stencil and color bit depth.
int pixelFormat = GetPixelFormat(dc);
PIXELFORMATDESCRIPTOR pfd;
DescribePixelFormat(dc, pixelFormat, sizeof(pfd), &pfd);
fStencilBits = pfd.cStencilBits;
// pfd.cColorBits includes alpha, so it will be 32 in 8/8/8/8 and 10/10/10/2
fColorBits = pfd.cRedBits + pfd.cGreenBits + pfd.cBlueBits;
// Get sample count if the MSAA WGL extension is present
SkWGLExtensions extensions;
if (extensions.hasExtension(dc, "WGL_ARB_multisample")) {
static const int kSampleCountAttr = SK_WGL_SAMPLES;
extensions.getPixelFormatAttribiv(dc,
pixelFormat,
0,
1,
&kSampleCountAttr,
&fSampleCount);
} else {
fSampleCount = 0;
}
RECT rect;
GetClientRect(fHWND, &rect);
fWidth = rect.right - rect.left;
fHeight = rect.bottom - rect.top;
glViewport(0, 0, fWidth, fHeight);
}
}
void GLWindowContext_win::onDestroyContext() {
wglMakeCurrent(wglGetCurrentDC(), NULL);
wglDeleteContext(fHGLRC);
fHGLRC = NULL;
}
void GLWindowContext_win::onSwapBuffers() {
HDC dc = GetDC((HWND)fHWND);
SwapBuffers(dc);
ReleaseDC((HWND)fHWND, dc);
}
} //namespace sk_app

View File

@ -0,0 +1,37 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GLWindowContext_win_DEFINED
#define GLWindowContext_win_DEFINED
#include <windows.h>
#include "../GLWindowContext.h"
namespace sk_app {
class GLWindowContext_win : public GLWindowContext {
public:
friend GLWindowContext* GLWindowContext::Create(void* platformData, const DisplayParams&);
~GLWindowContext_win() override;
void onSwapBuffers() override;
void onInitializeContext(void*, const DisplayParams&) override;
void onDestroyContext() override;
private:
GLWindowContext_win(void*, const DisplayParams&);
HWND fHWND;
HGLRC fHGLRC;
};
}
#endif

View File

@ -6,7 +6,8 @@
* found in the LICENSE file.
*/
#include "VulkanWindowContext_win.h"
#include "../VulkanWindowContext.h"
#include "Window_win.h"
#include "vk/GrVkInterface.h"
#include "vk/GrVkUtil.h"

View File

@ -1,28 +0,0 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef VULKANTESTCONTEXT_WIN_DEFINED
#define VULKANTESTCONTEXT_WIN_DEFINED
#ifdef SK_VULKAN
#include <windows.h>
#include "../VulkanWindowContext.h"
namespace sk_app {
// for Windows
struct ContextPlatformData_win {
HINSTANCE fHInstance;
HWND fHWnd;
};
}
#endif // SK_VULKAN
#endif

View File

@ -12,7 +12,8 @@
#include <windowsx.h>
#include "SkUtils.h"
#include "VulkanWindowContext_win.h"
#include "../GLWindowContext.h"
#include "../VulkanWindowContext.h"
namespace sk_app {
@ -264,16 +265,21 @@ void Window_win::show() {
}
bool Window_win::attach(BackEndType attachType, const DisplayParams& params) {
if (kVulkan_BackendType != attachType) {
return false;
}
bool Window_win::attach(BackendType attachType, const DisplayParams& params) {
ContextPlatformData_win platformData;
platformData.fHInstance = fHInstance;
platformData.fHWnd = fHWnd;
fWindowContext = VulkanWindowContext::Create((void*)&platformData, params);
switch (attachType) {
case kNativeGL_BackendType:
default:
fWindowContext = GLWindowContext::Create((void*)&platformData, params);
break;
case kVulkan_BackendType:
fWindowContext = VulkanWindowContext::Create((void*)&platformData, params);
break;
}
return (SkToBool(fWindowContext));
}

View File

@ -13,6 +13,12 @@
namespace sk_app {
// for Windows
struct ContextPlatformData_win {
HINSTANCE fHInstance;
HWND fHWnd;
};
class Window_win : public Window {
public:
Window_win() : Window() {}
@ -23,7 +29,7 @@ public:
void setTitle(const char*) override;
void show() override;
bool attach(BackEndType attachType, const DisplayParams& params) override;
bool attach(BackendType attachType, const DisplayParams& params) override;
void inval() override;