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 | |
25 | base::tick_t startTick; |
26 | |
27 | // Shader from: |
28 | // https://shaders.skia.org/ |
29 | // https://twitter.com/notargs/status/1250468645030858753 |
30 | const char* shaderCode = R"( |
31 | uniform float3 iResolution; |
32 | uniform float iTime; |
33 | |
34 | float 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 | |
41 | half4 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 | |
51 | class ShaderWindow { |
52 | public: |
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 | |
106 | private: |
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 | |
124 | int 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 | |