From dbb24efcc5f224a0d9a73ad9b200daf8013df0a1 Mon Sep 17 00:00:00 2001 From: Jim Van Verth Date: Fri, 1 Sep 2017 11:01:51 -0400 Subject: Get viewer running on iOS Bug: skia: Change-Id: Ic8b25ca2ecf51cfc190ac01bc9282396905a33b3 Reviewed-on: https://skia-review.googlesource.com/40862 Reviewed-by: Brian Osman Commit-Queue: Jim Van Verth --- tools/viewer/sk_app/ios/GLWindowContext_ios.cpp | 109 ++++++++ .../viewer/sk_app/ios/RasterWindowContext_ios.cpp | 136 ++++++++++ tools/viewer/sk_app/ios/WindowContextFactory_ios.h | 38 +++ tools/viewer/sk_app/ios/Window_ios.cpp | 277 +++++++++++++++++++++ tools/viewer/sk_app/ios/Window_ios.h | 64 +++++ tools/viewer/sk_app/ios/main_ios.cpp | 58 +++++ 6 files changed, 682 insertions(+) create mode 100644 tools/viewer/sk_app/ios/GLWindowContext_ios.cpp create mode 100644 tools/viewer/sk_app/ios/RasterWindowContext_ios.cpp create mode 100644 tools/viewer/sk_app/ios/WindowContextFactory_ios.h create mode 100644 tools/viewer/sk_app/ios/Window_ios.cpp create mode 100644 tools/viewer/sk_app/ios/Window_ios.h create mode 100644 tools/viewer/sk_app/ios/main_ios.cpp (limited to 'tools/viewer') diff --git a/tools/viewer/sk_app/ios/GLWindowContext_ios.cpp b/tools/viewer/sk_app/ios/GLWindowContext_ios.cpp new file mode 100644 index 0000000000..30bacf5cea --- /dev/null +++ b/tools/viewer/sk_app/ios/GLWindowContext_ios.cpp @@ -0,0 +1,109 @@ + +/* + * 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 +#include "../GLWindowContext.h" +#include "SDL.h" +#include "WindowContextFactory_ios.h" +#include "gl/GrGLInterface.h" + +using sk_app::DisplayParams; +using sk_app::window_context_factory::IOSWindowInfo; +using sk_app::GLWindowContext; + +namespace { + +class GLWindowContext_ios : public GLWindowContext { +public: + GLWindowContext_ios(const IOSWindowInfo&, const DisplayParams&); + + ~GLWindowContext_ios() override; + + void onSwapBuffers() override; + + sk_sp onInitializeContext() override; + void onDestroyContext() override; + +private: + SDL_Window* fWindow; + SDL_GLContext fGLContext; + + typedef GLWindowContext INHERITED; +}; + +GLWindowContext_ios::GLWindowContext_ios(const IOSWindowInfo& info, const DisplayParams& params) + : INHERITED(params) + , fWindow(info.fWindow) + , fGLContext(nullptr) { + + // any config code here (particularly for msaa)? + + this->initializeContext(); +} + +GLWindowContext_ios::~GLWindowContext_ios() { + this->destroyContext(); +} + +sk_sp GLWindowContext_ios::onInitializeContext() { + SkASSERT(fWindow); + + fGLContext = SDL_GL_CreateContext(fWindow); + if (!fGLContext) { + SkDebugf("%s\n", SDL_GetError()); + return nullptr; + } + + if (0 == SDL_GL_MakeCurrent(fWindow, fGLContext)) { + glClearStencil(0); + glClearColor(0, 0, 0, 0); + glStencilMask(0xffffffff); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &fStencilBits); + SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &fSampleCount); + + SDL_GL_GetDrawableSize(fWindow, &fWidth, &fHeight); + glViewport(0, 0, fWidth, fHeight); + } else { + SkDebugf("MakeCurrent failed: %s\n", SDL_GetError()); + } + return sk_sp(GrGLCreateNativeInterface()); +} + +void GLWindowContext_ios::onDestroyContext() { + if (!fWindow || !fGLContext) { + return; + } + SDL_GL_DeleteContext(fGLContext); + fGLContext = nullptr; +} + + +void GLWindowContext_ios::onSwapBuffers() { + if (fWindow && fGLContext) { + SDL_GL_SwapWindow(fWindow); + } +} + +} // anonymous namespace + +namespace sk_app { +namespace window_context_factory { + +WindowContext* NewGLForIOS(const IOSWindowInfo& info, const DisplayParams& params) { + WindowContext* ctx = new GLWindowContext_ios(info, params); + if (!ctx->isValid()) { + delete ctx; + return nullptr; + } + return ctx; +} + +} // namespace window_context_factory +} // namespace sk_app diff --git a/tools/viewer/sk_app/ios/RasterWindowContext_ios.cpp b/tools/viewer/sk_app/ios/RasterWindowContext_ios.cpp new file mode 100644 index 0000000000..08b6560510 --- /dev/null +++ b/tools/viewer/sk_app/ios/RasterWindowContext_ios.cpp @@ -0,0 +1,136 @@ + +/* + * 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 +#include "../GLWindowContext.h" +#include "SDL.h" +#include "SkCanvas.h" +#include "SkColorFilter.h" +#include "WindowContextFactory_ios.h" +#include "gl/GrGLInterface.h" +#include "sk_tool_utils.h" + +using sk_app::DisplayParams; +using sk_app::window_context_factory::IOSWindowInfo; +using sk_app::GLWindowContext; + +namespace { + +// We use SDL to support Mac windowing mainly for convenience's sake. However, it +// does not allow us to support a purely raster backend because we have no hooks into +// the NSWindow's drawRect: method. Hence we use GL to handle the update. Should we +// want to avoid this, we will probably need to write our own windowing backend. + +class RasterWindowContext_ios : public GLWindowContext { +public: + RasterWindowContext_ios(const IOSWindowInfo&, const DisplayParams&); + + ~RasterWindowContext_ios() override; + + sk_sp getBackbufferSurface() override; + + void onSwapBuffers() override; + + sk_sp onInitializeContext() override; + void onDestroyContext() override; + +private: + SDL_Window* fWindow; + SDL_GLContext fGLContext; + sk_sp fBackbufferSurface; + + typedef GLWindowContext INHERITED; +}; + +RasterWindowContext_ios::RasterWindowContext_ios(const IOSWindowInfo& info, + const DisplayParams& params) + : INHERITED(params) + , fWindow(info.fWindow) + , fGLContext(nullptr) { + + // any config code here (particularly for msaa)? + + this->initializeContext(); +} + +RasterWindowContext_ios::~RasterWindowContext_ios() { + this->destroyContext(); +} + +sk_sp RasterWindowContext_ios::onInitializeContext() { + SkASSERT(fWindow); + + fGLContext = SDL_GL_CreateContext(fWindow); + if (!fGLContext) { + SkDebugf("%s\n", SDL_GetError()); + return nullptr; + } + + if (0 == SDL_GL_MakeCurrent(fWindow, fGLContext)) { + glClearStencil(0); + glClearColor(0, 0, 0, 0); + glStencilMask(0xffffffff); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &fStencilBits); + SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &fSampleCount); + + SDL_GL_GetDrawableSize(fWindow, &fWidth, &fHeight); + glViewport(0, 0, fWidth, fHeight); + } else { + SkDebugf("MakeCurrent failed: %s\n", SDL_GetError()); + } + + // make the offscreen image + SkImageInfo info = SkImageInfo::Make(fWidth, fHeight, fDisplayParams.fColorType, + kPremul_SkAlphaType, fDisplayParams.fColorSpace); + fBackbufferSurface = SkSurface::MakeRaster(info); + return sk_sp(GrGLCreateNativeInterface()); +} + +void RasterWindowContext_ios::onDestroyContext() { + if (!fWindow || !fGLContext) { + return; + } + fBackbufferSurface.reset(nullptr); + SDL_GL_DeleteContext(fGLContext); + fGLContext = nullptr; +} + +sk_sp RasterWindowContext_ios::getBackbufferSurface() { return fBackbufferSurface; } + +void RasterWindowContext_ios::onSwapBuffers() { + if (fWindow && fGLContext) { + // We made/have an off-screen surface. Get the contents as an SkImage: + sk_sp snapshot = fBackbufferSurface->makeImageSnapshot(); + + sk_sp gpuSurface = INHERITED::getBackbufferSurface(); + SkCanvas* gpuCanvas = gpuSurface->getCanvas(); + gpuCanvas->drawImage(snapshot, 0, 0); + gpuCanvas->flush(); + + SDL_GL_SwapWindow(fWindow); + } +} + +} // anonymous namespace + +namespace sk_app { +namespace window_context_factory { + +WindowContext* NewRasterForIOS(const IOSWindowInfo& info, const DisplayParams& params) { + WindowContext* ctx = new RasterWindowContext_ios(info, params); + if (!ctx->isValid()) { + delete ctx; + return nullptr; + } + return ctx; +} + +} // namespace window_context_factory +} // namespace sk_app diff --git a/tools/viewer/sk_app/ios/WindowContextFactory_ios.h b/tools/viewer/sk_app/ios/WindowContextFactory_ios.h new file mode 100644 index 0000000000..09999c4c83 --- /dev/null +++ b/tools/viewer/sk_app/ios/WindowContextFactory_ios.h @@ -0,0 +1,38 @@ + +/* + * 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 WindowContextFactory_ios_DEFINED +#define WindowContextFactory_ios_DEFINED + +#include "SDL.h" + +namespace sk_app { + +class WindowContext; +struct DisplayParams; + +namespace window_context_factory { + +struct IOSWindowInfo { + SDL_Window* fWindow; +}; + +inline WindowContext* NewVulkanForIOS(const IOSWindowInfo&, const DisplayParams&) { + // No Vulkan support on iOS. + return nullptr; +} + +WindowContext* NewGLForIOS(const IOSWindowInfo&, const DisplayParams&); + +WindowContext* NewRasterForIOS(const IOSWindowInfo&, const DisplayParams&); + +} // namespace window_context_factory + +} // namespace sk_app + +#endif diff --git a/tools/viewer/sk_app/ios/Window_ios.cpp b/tools/viewer/sk_app/ios/Window_ios.cpp new file mode 100644 index 0000000000..d37269703c --- /dev/null +++ b/tools/viewer/sk_app/ios/Window_ios.cpp @@ -0,0 +1,277 @@ +/* +* 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 "SkUtils.h" +#include "Timer.h" +#include "WindowContextFactory_ios.h" +#include "Window_ios.h" + +namespace sk_app { + +SkTDynamicHash Window_ios::gWindowMap; + +Window* Window::CreateNativeWindow(void*) { + Window_ios* window = new Window_ios(); + if (!window->initWindow()) { + delete window; + return nullptr; + } + + return window; +} + +bool Window_ios::initWindow() { + if (fRequestedDisplayParams.fMSAASampleCount != fMSAASampleCount) { + this->closeWindow(); + } + // we already have a window + if (fWindow) { + return true; + } + + constexpr int initialWidth = 1280; + constexpr int initialHeight = 960; + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); + + if (fRequestedDisplayParams.fMSAASampleCount > 0) { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, fRequestedDisplayParams.fMSAASampleCount); + } else { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); + } + // TODO: handle other display params + + uint32_t windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_ALLOW_HIGHDPI; + fWindow = SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + initialWidth, initialHeight, windowFlags); + + if (!fWindow) { + return false; + } + + fMSAASampleCount = fRequestedDisplayParams.fMSAASampleCount; + + // add to hashtable of windows + fWindowID = SDL_GetWindowID(fWindow); + gWindowMap.add(this); + + return true; +} + +void Window_ios::closeWindow() { + if (fWindow) { + gWindowMap.remove(fWindowID); + SDL_DestroyWindow(fWindow); + fWindowID = 0; + fWindow = nullptr; + } +} + +static Window::Key get_key(const SDL_Keysym& keysym) { + static const struct { + SDL_Keycode fSDLK; + Window::Key fKey; + } gPair[] = { + { SDLK_BACKSPACE, Window::Key::kBack }, + { SDLK_CLEAR, Window::Key::kBack }, + { SDLK_RETURN, Window::Key::kOK }, + { SDLK_UP, Window::Key::kUp }, + { SDLK_DOWN, Window::Key::kDown }, + { SDLK_LEFT, Window::Key::kLeft }, + { SDLK_RIGHT, Window::Key::kRight }, + { SDLK_TAB, Window::Key::kTab }, + { SDLK_PAGEUP, Window::Key::kPageUp }, + { SDLK_PAGEDOWN, Window::Key::kPageDown }, + { SDLK_HOME, Window::Key::kHome }, + { SDLK_END, Window::Key::kEnd }, + { SDLK_DELETE, Window::Key::kDelete }, + { SDLK_ESCAPE, Window::Key::kEscape }, + { SDLK_LSHIFT, Window::Key::kShift }, + { SDLK_RSHIFT, Window::Key::kShift }, + { SDLK_LCTRL, Window::Key::kCtrl }, + { SDLK_RCTRL, Window::Key::kCtrl }, + { SDLK_LALT, Window::Key::kOption }, + { SDLK_LALT, Window::Key::kOption }, + { 'A', Window::Key::kA }, + { 'C', Window::Key::kC }, + { 'V', Window::Key::kV }, + { 'X', Window::Key::kX }, + { 'Y', Window::Key::kY }, + { 'Z', Window::Key::kZ }, + }; + for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { + if (gPair[i].fSDLK == keysym.sym) { + return gPair[i].fKey; + } + } + return Window::Key::kNONE; +} + +static uint32_t get_modifiers(const SDL_Event& event) { + static const struct { + unsigned fSDLMask; + unsigned fSkMask; + } gModifiers[] = { + { KMOD_SHIFT, Window::kShift_ModifierKey }, + { KMOD_CTRL, Window::kControl_ModifierKey }, + { KMOD_ALT, Window::kOption_ModifierKey }, + }; + + auto modifiers = 0; + + switch (event.type) { + case SDL_KEYDOWN: + // fall through + case SDL_KEYUP: { + for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) { + if (event.key.keysym.mod & gModifiers[i].fSDLMask) { + modifiers |= gModifiers[i].fSkMask; + } + } + if (0 == event.key.repeat) { + modifiers |= Window::kFirstPress_ModifierKey; + } + break; + } + + default: { + SDL_Keymod mod = SDL_GetModState(); + for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) { + if (mod & gModifiers[i].fSDLMask) { + modifiers |= gModifiers[i].fSkMask; + } + } + break; + } + } + return modifiers; +} + +bool Window_ios::HandleWindowEvent(const SDL_Event& event) { + Window_ios* win = gWindowMap.find(event.window.windowID); + if (win && win->handleEvent(event)) { + return true; + } + + return false; +} + +bool Window_ios::handleEvent(const SDL_Event& event) { + switch (event.type) { + case SDL_WINDOWEVENT: + if (SDL_WINDOWEVENT_EXPOSED == event.window.event) { + this->onPaint(); + } else if (SDL_WINDOWEVENT_RESIZED == event.window.event) { + this->onResize(event.window.data1, event.window.data2); + } + break; + + case SDL_MOUSEBUTTONDOWN: + if (event.button.button == SDL_BUTTON_LEFT) { + this->onMouse(event.button.x, event.button.y, + Window::kDown_InputState, get_modifiers(event)); + } + break; + + case SDL_MOUSEBUTTONUP: + if (event.button.button == SDL_BUTTON_LEFT) { + this->onMouse(event.button.x, event.button.y, + Window::kUp_InputState, get_modifiers(event)); + } + break; + + case SDL_MOUSEMOTION: + this->onMouse(event.motion.x, event.motion.y, + Window::kMove_InputState, get_modifiers(event)); + break; + + case SDL_MOUSEWHEEL: + this->onMouseWheel(event.wheel.y, get_modifiers(event)); + break; + + case SDL_KEYDOWN: { + Window::Key key = get_key(event.key.keysym); + if (key != Window::Key::kNONE) { + if (!this->onKey(key, Window::kDown_InputState, get_modifiers(event))) { + if (event.key.keysym.sym == SDLK_ESCAPE) { + return true; + } + } + } + } break; + + case SDL_KEYUP: { + Window::Key key = get_key(event.key.keysym); + if (key != Window::Key::kNONE) { + (void) this->onKey(key, Window::kUp_InputState, + get_modifiers(event)); + } + } break; + + case SDL_TEXTINPUT: { + const char* textIter = &event.text.text[0]; + while (SkUnichar c = SkUTF8_NextUnichar(&textIter)) { + (void) this->onChar(c, get_modifiers(event)); + } + } break; + + default: + break; + } + + return false; +} + +void Window_ios::setTitle(const char* title) { + SDL_SetWindowTitle(fWindow, title); +} + +void Window_ios::show() { + SDL_ShowWindow(fWindow); +} + +bool Window_ios::attach(BackendType attachType) { + this->initWindow(); + + window_context_factory::IOSWindowInfo info; + info.fWindow = fWindow; + switch (attachType) { + case kRaster_BackendType: + fWindowContext = NewRasterForIOS(info, fRequestedDisplayParams); + break; + + case kNativeGL_BackendType: + default: + fWindowContext = NewGLForIOS(info, fRequestedDisplayParams); + break; + } + this->onBackendCreated(); + + return (SkToBool(fWindowContext)); +} + +void Window_ios::onInval() { + SDL_Event sdlevent; + sdlevent.type = SDL_WINDOWEVENT; + sdlevent.window.windowID = fWindowID; + sdlevent.window.event = SDL_WINDOWEVENT_EXPOSED; + SDL_PushEvent(&sdlevent); +} + +} // namespace sk_app diff --git a/tools/viewer/sk_app/ios/Window_ios.h b/tools/viewer/sk_app/ios/Window_ios.h new file mode 100644 index 0000000000..667fa74e82 --- /dev/null +++ b/tools/viewer/sk_app/ios/Window_ios.h @@ -0,0 +1,64 @@ +/* +* 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 Window_ios_DEFINED +#define Window_ios_DEFINED + +#include "../Window.h" +#include "SkChecksum.h" +#include "SkTDynamicHash.h" + +#include "SDL.h" + +namespace sk_app { + +class Window_ios : public Window { +public: + Window_ios() + : INHERITED() + , fWindow(nullptr) + , fWindowID(0) + , fMSAASampleCount(0) {} + ~Window_ios() override { this->closeWindow(); } + + bool initWindow(); + + void setTitle(const char*) override; + void show() override; + + bool attach(BackendType) override; + + void onInval() override; + + static bool HandleWindowEvent(const SDL_Event& event); + + static const Uint32& GetKey(const Window_ios& w) { + return w.fWindowID; + } + + static uint32_t Hash(const Uint32& winID) { + return winID; + } + +private: + bool handleEvent(const SDL_Event& event); + + void closeWindow(); + + static SkTDynamicHash gWindowMap; + + SDL_Window* fWindow; + Uint32 fWindowID; + + int fMSAASampleCount; + + typedef Window INHERITED; +}; + +} // namespace sk_app + +#endif diff --git a/tools/viewer/sk_app/ios/main_ios.cpp b/tools/viewer/sk_app/ios/main_ios.cpp new file mode 100644 index 0000000000..fe82c46485 --- /dev/null +++ b/tools/viewer/sk_app/ios/main_ios.cpp @@ -0,0 +1,58 @@ +/* +* 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" +#include "SkTHash.h" +#include "SDL.h" +#include "Timer.h" +#include "Window_ios.h" +#include "../Application.h" + +using sk_app::Application; + +int main(int argc, char* argv[]) { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) { + SkDebugf("Could not initialize SDL!\n"); + return 1; + } + + Application* app = Application::Create(argc, argv, nullptr); + + SDL_Event event; + bool done = false; + while (!done) { + while (SDL_PollEvent(&event)) { + switch (event.type) { + // events handled by the windows + case SDL_WINDOWEVENT: + case SDL_MOUSEMOTION: + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEWHEEL: + case SDL_KEYDOWN: + case SDL_KEYUP: + case SDL_TEXTINPUT: + done = sk_app::Window_ios::HandleWindowEvent(event); + break; + + case SDL_QUIT: + done = true; + break; + + default: + break; + } + } + + app->onIdle(); + } + delete app; + + SDL_Quit(); + + return 0; +} -- cgit v1.2.3