1// LAF Library
2// Copyright (C) 2022 Igara Studio S.A.
3//
4// This file is released under the terms of the MIT license.
5// Read LICENSE.txt for more information.
6
7#include "os/os.h"
8
9#ifndef LAF_SKIA
10 #error You need Skia library to compile this example
11#endif
12
13#if !SK_SUPPORT_GPU
14 #error You need Skia with GPU support to compile this example
15#endif
16
17#include "base/time.h"
18#include "os/skia/skia_surface.h"
19
20#include "include/core/SkCanvas.h"
21#include "include/effects/SkRuntimeEffect.h"
22
23#include <cstdio>
24
25base::tick_t startTick;
26
27// Shader from:
28// https://shaders.skia.org/
29// https://twitter.com/notargs/status/1250468645030858753
30const char* shaderCode = R"(
31uniform float3 iResolution;
32uniform float iTime;
33
34float f(vec3 p) {
35 p.z -= iTime * 10.0;
36 float a = p.z * .1;
37 p.xy *= mat2(cos(a), sin(a), -sin(a), cos(a));
38 return .1 - length(cos(p.xy) + sin(p.yz));
39}
40
41half4 main(vec2 fragcoord) {
42 vec3 d = .5 - fragcoord.xy1 / iResolution.y;
43 vec3 p = vec3(0);
44 for (int i = 0; i < 32; i++) {
45 p += f(p) * d;
46 }
47 return ((sin(p) + vec3(2, 5, 9)) / length(p)).xyz1;
48}
49)";
50
51class ShaderWindow {
52public:
53 ShaderWindow(os::System* system)
54 : m_builder(SkRuntimeEffect::MakeForShader(SkString(shaderCode)).effect) {
55 m_window = system->makeWindow(256, 256);
56 m_window->setCursor(os::NativeCursor::Arrow);
57 m_window->setTitle("Shader");
58 repaint();
59 m_window->setVisible(true);
60 }
61
62 bool processEvent(const os::Event& ev) {
63 switch (ev.type()) {
64
65 case os::Event::CloseWindow:
66 return false;
67
68 case os::Event::ResizeWindow:
69 repaint();
70 break;
71
72 case os::Event::KeyDown:
73 if (ev.scancode() == os::kKeyEsc)
74 return false;
75 else if (ev.scancode() == os::kKeyG) {
76 os::instance()->setGpuAcceleration(
77 !os::instance()->gpuAcceleration());
78 }
79 break;
80
81 default:
82 // Do nothing
83 break;
84 }
85 return true;
86 }
87
88 void repaint() {
89 os::Surface* surface = m_window->surface();
90 os::SurfaceLock lock(surface);
91
92 SkCanvas* canvas = &static_cast<os::SkiaSurface*>(surface)->canvas();
93 skiaPaint(canvas);
94
95 if (m_window->isGpuAccelerated()) {
96 os::Paint p;
97 p.color(gfx::rgba(0, 0, 0));
98 os::draw_text(surface, nullptr, "GPU", gfx::Point(12, 12),
99 &p, os::TextAlign::Center);
100 }
101
102 m_window->invalidate();
103 m_window->swapBuffers();
104 }
105
106private:
107
108 void skiaPaint(SkCanvas* canvas) {
109 SkImageInfo ii = canvas->imageInfo();
110 m_builder.uniform("iResolution") = SkV3{float(ii.width()),
111 float(ii.height()), 0.0f};
112 m_builder.uniform("iTime") =
113 float((base::current_tick() - startTick) / 1000.0f);
114
115 SkPaint p;
116 p.setShader(m_builder.makeShader());
117 canvas->drawPaint(p);
118 }
119
120 os::WindowRef m_window;
121 SkRuntimeShaderBuilder m_builder;
122};
123
124int app_main(int argc, char* argv[])
125{
126 os::SystemRef system = os::make_system();
127 system->setAppMode(os::AppMode::GUI);
128 system->setGpuAcceleration(true);
129
130 ShaderWindow window(system.get());
131
132 system->handleWindowResize = [&window](os::Window* win){
133 window.repaint();
134 };
135
136 system->finishLaunching();
137 system->activateApp();
138
139 startTick = base::current_tick();
140
141 os::EventQueue* queue = system->eventQueue();
142 auto t = startTick;
143 double paintDelay = 0.0;
144
145 while (true) {
146 os::Event ev;
147
148 ASSERT(paintDelay >= 0.0);
149 const double waitSecs =
150 (base::current_tick() - t) / 1000.0 * 60.0 + paintDelay;
151
152 queue->getEvent(ev, waitSecs);
153 if (!window.processEvent(ev))
154 break;
155
156 auto now = base::current_tick();
157 paintDelay -= (now - t) / 1000.0;
158 if (paintDelay < 0.0) {
159 auto paintStart = now;
160
161 window.repaint();
162
163 now = base::current_tick();
164 paintDelay = (now - paintStart) / 1000.0;
165 }
166 t = now;
167 }
168
169 return 0;
170}
171