Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkSLSlide.cpp
Go to the documentation of this file.
1/*
2* Copyright 2019 Google LLC
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7
9
13#include "include/core/SkData.h"
14#include "include/core/SkFont.h"
19#include "include/core/SkRect.h"
27#include "tools/DecodeUtils.h"
28#include "tools/Resources.h"
31#include "tools/viewer/Viewer.h"
32
33#include <algorithm>
34#include <cmath>
35#include <cstdio>
36#include <cstring>
37#include <string>
38#include <string_view>
39
40#include "imgui.h"
41
42using namespace sk_app;
43
44///////////////////////////////////////////////////////////////////////////////
45
46static int InputTextCallback(ImGuiInputTextCallbackData* data) {
47 if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) {
48 SkString* s = (SkString*)data->UserData;
49 SkASSERT(data->Buf == s->data());
50 SkString tmp(data->Buf, data->BufTextLen);
51 s->swap(tmp);
52 data->Buf = s->data();
53 }
54 return 0;
55}
56
58 // Register types for serialization
59 fName = "SkSL";
60
61 fSkSL =
62
63 "uniform shader child;\n"
64 "\n"
65 "half4 main(float2 p) {\n"
66 " return child.eval(p);\n"
67 "}\n";
68
69 fCodeIsDirty = true;
70}
71
72void SkSLSlide::load(SkScalar winWidth, SkScalar winHeight) {
73 SkPoint points[] = { { 0, 0 }, { 256, 0 } };
74 SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };
75
76 sk_sp<SkShader> shader;
77
78 fShaders.push_back(std::make_pair("Null", nullptr));
79
80 shader = SkGradientShader::MakeLinear(points, colors, nullptr, 2, SkTileMode::kClamp);
81 fShaders.push_back(std::make_pair("Linear Gradient", shader));
82
83 shader = SkGradientShader::MakeRadial({ 256, 256 }, 256, colors, nullptr, 2,
85 fShaders.push_back(std::make_pair("Radial Gradient", shader));
86
87 shader = SkGradientShader::MakeSweep(256, 256, colors, nullptr, 2);
88 fShaders.push_back(std::make_pair("Sweep Gradient", shader));
89
90 shader = ToolUtils::GetResourceAsImage("images/mandrill_256.png")
92 fShaders.push_back(std::make_pair("Mandrill", shader));
93
94 fResolution = { winWidth, winHeight, 1.0f };
95}
96
98 fEffect.reset();
99 fInputs.reset();
100 fChildren.clear();
101 fShaders.clear();
102}
103
104bool SkSLSlide::rebuild() {
105 // Some of the standard shadertoy inputs:
106 SkString sksl;
107 // TODO(skia:11209): This interferes with user-authored #version directives
108 if (fShadertoyUniforms) {
109 sksl = "uniform float3 iResolution;\n"
110 "uniform float iTime;\n"
111 "uniform float4 iMouse;\n";
112 }
113 sksl.append(fSkSL);
114
115 // It shouldn't happen, but it's possible to assert in the compiler, especially mid-edit.
116 // To guard against losing your work, write out the shader to a backup file, then remove it
117 // when we compile successfully.
118 constexpr char kBackupFile[] = "sksl.bak";
119 FILE* backup = fopen(kBackupFile, "w");
120 if (backup) {
121 fwrite(fSkSL.c_str(), 1, fSkSL.size(), backup);
122 fclose(backup);
123 }
124 auto [effect, errorText] = SkRuntimeEffect::MakeForShader(sksl);
125 if (backup) {
126 std::remove(kBackupFile);
127 }
128
129 if (!effect) {
130 Viewer::ShaderErrorHandler()->compileError(sksl.c_str(), errorText.c_str());
131 return false;
132 }
133
134 size_t oldSize = fEffect ? fEffect->uniformSize() : 0;
135 fInputs.realloc(effect->uniformSize());
136 if (effect->uniformSize() > oldSize) {
137 memset(fInputs.get() + oldSize, 0, effect->uniformSize() - oldSize);
138 }
139 fChildren.resize_back(effect->children().size());
140
141 fEffect = effect;
142 fCodeIsDirty = false;
143 return true;
144}
145
147 canvas->clear(SK_ColorWHITE);
148
149 ImGui::Begin("SkSL", nullptr, ImGuiWindowFlags_AlwaysVerticalScrollbar);
150
151 // Edit box for shader code
152 ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize;
153 ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * 30);
154 if (ImGui::InputTextMultiline("Code", fSkSL.data(), fSkSL.size() + 1, boxSize, flags,
155 InputTextCallback, &fSkSL)) {
156 fCodeIsDirty = true;
157 }
158
159 if (ImGui::Checkbox("ShaderToy Uniforms (iResolution/iTime/iMouse)", &fShadertoyUniforms)) {
160 fCodeIsDirty = true;
161 }
162
163 if (fCodeIsDirty || !fEffect) {
164 this->rebuild();
165 }
166
167 if (!fEffect) {
168 ImGui::End();
169 return;
170 }
171
172 bool writeTrace = false;
173 bool writeDump = false;
174 if (!canvas->recordingContext()) {
175 ImGui::InputInt2("Trace Coordinate (X/Y)", fTraceCoord);
176 writeTrace = ImGui::Button("Write Debug Trace (JSON)");
177 writeDump = ImGui::Button("Write Debug Dump (Human-Readable)");
178 }
179
180 // Update fMousePos
181 ImVec2 mousePos = ImGui::GetMousePos();
182 if (ImGui::IsMouseDown(0)) {
183 fMousePos.x = mousePos.x;
184 fMousePos.y = mousePos.y;
185 }
186 if (ImGui::IsMouseClicked(0)) {
187 fMousePos.z = mousePos.x;
188 fMousePos.w = mousePos.y;
189 }
190 fMousePos.z = std::abs(fMousePos.z) * (ImGui::IsMouseDown(0) ? 1 : -1);
191 fMousePos.w = std::abs(fMousePos.w) * (ImGui::IsMouseClicked(0) ? 1 : -1);
192
193 for (const SkRuntimeEffect::Uniform& v : fEffect->uniforms()) {
194 char* data = fInputs.get() + v.offset;
195 if (v.name == "iResolution") {
196 memcpy(data, &fResolution, sizeof(fResolution));
197 continue;
198 }
199 if (v.name == "iTime") {
200 memcpy(data, &fSeconds, sizeof(fSeconds));
201 continue;
202 }
203 if (v.name == "iMouse") {
204 memcpy(data, &fMousePos, sizeof(fMousePos));
205 continue;
206 }
207 switch (v.type) {
212 int rows = ((int)v.type - (int)SkRuntimeEffect::Uniform::Type::kFloat) + 1;
213 float* f = reinterpret_cast<float*>(data);
214 for (int c = 0; c < v.count; ++c, f += rows) {
215 SkString name = v.isArray()
216 ? SkStringPrintf("%.*s[%d]", (int)v.name.size(), v.name.data(), c)
217 : SkString(v.name);
218 ImGui::PushID(c);
219 ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f);
220 ImGui::PopID();
221 }
222 break;
223 }
227 int rows = ((int)v.type - (int)SkRuntimeEffect::Uniform::Type::kFloat2x2) + 2;
228 int cols = rows;
229 float* f = reinterpret_cast<float*>(data);
230 for (int e = 0; e < v.count; ++e) {
231 for (int c = 0; c < cols; ++c, f += rows) {
232 SkString name = v.isArray()
233 ? SkStringPrintf("%.*s[%d][%d]", (int)v.name.size(), v.name.data(), e, c)
234 : SkStringPrintf("%.*s[%d]", (int)v.name.size(), v.name.data(), c);
235 ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f);
236 }
237 }
238 break;
239 }
244 int rows = ((int)v.type - (int)SkRuntimeEffect::Uniform::Type::kInt) + 1;
245 int* i = reinterpret_cast<int*>(data);
246 for (int c = 0; c < v.count; ++c, i += rows) {
247 SkString name = v.isArray()
248 ? SkStringPrintf("%.*s[%d]", (int)v.name.size(), v.name.data(), c)
249 : SkString(v.name);
250 ImGui::PushID(c);
251 ImGui::DragScalarN(name.c_str(), ImGuiDataType_S32, i, rows, 1.0f);
252 ImGui::PopID();
253 }
254 break;
255 }
256 }
257 }
258
259 for (const SkRuntimeEffect::Child& c : fEffect->children()) {
260 auto curShader = std::find_if(
261 fShaders.begin(),
262 fShaders.end(),
263 [tgt = fChildren[c.index]](const std::pair<const char*, sk_sp<SkShader>>& p) {
264 return p.second == tgt;
265 });
266 SkASSERT(curShader != fShaders.end());
267
268 if (ImGui::BeginCombo(std::string(c.name).c_str(), curShader->first)) {
269 for (const auto& namedShader : fShaders) {
270 if (ImGui::Selectable(namedShader.first, curShader->second == namedShader.second)) {
271 fChildren[c.index] = namedShader.second;
272 }
273 }
274 ImGui::EndCombo();
275 }
276 }
277
278 static SkColor4f gPaintColor { 1.0f, 1.0f, 1.0f , 1.0f };
279 ImGui::ColorEdit4("Paint Color", gPaintColor.vec());
280
281 ImGui::RadioButton("Fill", &fGeometry, kFill); ImGui::SameLine();
282 ImGui::RadioButton("Circle", &fGeometry, kCircle); ImGui::SameLine();
283 ImGui::RadioButton("RoundRect", &fGeometry, kRoundRect); ImGui::SameLine();
284 ImGui::RadioButton("Capsule", &fGeometry, kCapsule); ImGui::SameLine();
285 ImGui::RadioButton("Text", &fGeometry, kText);
286
287 ImGui::End();
288
289 auto inputs = SkData::MakeWithoutCopy(fInputs.get(), fEffect->uniformSize());
290
291 canvas->save();
292
293 sk_sp<SkSL::DebugTrace> debugTrace;
294 auto shader = fEffect->makeShader(std::move(inputs), fChildren.data(), fChildren.size());
295 if (writeTrace || writeDump) {
296 SkIPoint traceCoord = {fTraceCoord[0], fTraceCoord[1]};
298 traceCoord);
299 shader = std::move(traced.shader);
300 debugTrace = std::move(traced.debugTrace);
301
302 // Reduce debug trace delay by clipping to a 4x4 rectangle for this paint, centered on the
303 // pixel to trace. A minor complication is that the canvas might have a transform applied to
304 // it, but we want to clip in device space. This can be worked around by resetting the
305 // canvas matrix temporarily.
306 SkM44 canvasMatrix = canvas->getLocalToDevice();
307 canvas->resetMatrix();
308 auto r = SkRect::MakeXYWH(fTraceCoord[0] - 1, fTraceCoord[1] - 1, 4, 4);
309 canvas->clipRect(r, SkClipOp::kIntersect);
310 canvas->setMatrix(canvasMatrix);
311 }
312 SkPaint p;
313 p.setColor4f(gPaintColor);
314 p.setShader(std::move(shader));
315
316 switch (fGeometry) {
317 case kFill:
318 canvas->drawPaint(p);
319 break;
320 case kCircle:
321 canvas->drawCircle({ 256, 256 }, 256, p);
322 break;
323 case kRoundRect:
324 canvas->drawRoundRect({ 0, 0, 512, 512 }, 64, 64, p);
325 break;
326 case kCapsule:
327 canvas->drawRoundRect({ 0, 224, 512, 288 }, 32, 32, p);
328 break;
329 case kText: {
331 font.setSize(SkIntToScalar(96));
332 canvas->drawSimpleText("Hello World", strlen("Hello World"), SkTextEncoding::kUTF8, 0,
333 256, font, p);
334 } break;
335 default: break;
336 }
337
338 canvas->restore();
339
340 if (debugTrace && writeTrace) {
341 SkFILEWStream traceFile("SkSLDebugTrace.json");
342 debugTrace->writeTrace(&traceFile);
343 }
344 if (debugTrace && writeDump) {
345 SkFILEWStream dumpFile("SkSLDebugTrace.dump.txt");
346 debugTrace->dump(&dumpFile);
347 }
348}
349
350bool SkSLSlide::animate(double nanos) {
351 fSeconds = static_cast<float>(nanos * 1E-9);
352 return true;
353}
static const int points[]
#define SkASSERT(cond)
Definition SkAssert.h:116
uint32_t SkColor
Definition SkColor.h:37
constexpr SkColor SK_ColorRED
Definition SkColor.h:126
constexpr SkColor SK_ColorGREEN
Definition SkColor.h:131
constexpr SkColor SK_ColorWHITE
Definition SkColor.h:122
@ kUTF8
uses bytes to represent UTF-8 or ASCII
static int InputTextCallback(ImGuiInputTextCallbackData *data)
Definition SkSLSlide.cpp:46
#define SkIntToScalar(x)
Definition SkScalar.h:57
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
Type::kYUV Type::kRGBA() int(0.7 *637)
void clipRect(const SkRect &rect, SkClipOp op, bool doAntiAlias)
void restore()
Definition SkCanvas.cpp:465
void drawSimpleText(const void *text, size_t byteLength, SkTextEncoding encoding, SkScalar x, SkScalar y, const SkFont &font, const SkPaint &paint)
virtual GrRecordingContext * recordingContext() const
void drawPaint(const SkPaint &paint)
SkM44 getLocalToDevice() const
void clear(SkColor color)
Definition SkCanvas.h:1199
void drawRoundRect(const SkRect &rect, SkScalar rx, SkScalar ry, const SkPaint &paint)
void resetMatrix()
int save()
Definition SkCanvas.cpp:451
void setMatrix(const SkM44 &matrix)
void drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint &paint)
static sk_sp< SkData > MakeWithoutCopy(const void *data, size_t length)
Definition SkData.h:116
static sk_sp< SkShader > MakeSweep(SkScalar cx, SkScalar cy, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, SkScalar startAngle, SkScalar endAngle, uint32_t flags, const SkMatrix *localMatrix)
static sk_sp< SkShader > MakeRadial(const SkPoint &center, SkScalar radius, const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
static sk_sp< SkShader > MakeLinear(const SkPoint pts[2], const SkColor colors[], const SkScalar pos[], int count, SkTileMode mode, uint32_t flags=0, const SkMatrix *localMatrix=nullptr)
sk_sp< SkShader > makeShader(SkTileMode tmx, SkTileMode tmy, const SkSamplingOptions &, const SkMatrix *localMatrix=nullptr) const
Definition SkImage.cpp:179
Definition SkM44.h:150
size_t uniformSize() const
SkSpan< const Child > children() const
sk_sp< SkShader > makeShader(sk_sp< const SkData > uniforms, sk_sp< SkShader > children[], size_t childCount, const SkMatrix *localMatrix=nullptr) const
SkSpan< const Uniform > uniforms() const
static Result MakeForShader(SkString sksl, const Options &)
static TracedShader MakeTraced(sk_sp< SkShader > shader, const SkIPoint &traceCoord)
void unload() override
Definition SkSLSlide.cpp:97
bool animate(double nanos) override
void load(SkScalar winWidth, SkScalar winHeight) override
Definition SkSLSlide.cpp:72
void draw(SkCanvas *canvas) override
size_t size() const
Definition SkString.h:131
const char * data() const
Definition SkString.h:132
void append(const char text[])
Definition SkString.h:203
const char * c_str() const
Definition SkString.h:133
SkString fName
Definition Slide.h:54
static GrContextOptions::ShaderErrorHandler * ShaderErrorHandler()
Definition Viewer.cpp:167
void reset(T *ptr=nullptr)
Definition SkRefCnt.h:310
virtual void compileError(const char *shader, const char *errors)
void realloc(size_t count)
T * reset(size_t count=0)
void resize_back(int newCount)
Definition SkTArray.h:338
int size() const
Definition SkTArray.h:416
float SkScalar
Definition extension.cpp:12
struct MyStruct s
FlutterSemanticsFlag flags
const char * name
Definition fuchsia.cc:50
sk_sp< SkImage > GetResourceAsImage(const char *resource)
Definition DecodeUtils.h:25
SkFont DefaultFont()
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
sk_sp< SkSL::DebugTrace > debugTrace
float w
Definition SkM44.h:99
float y
Definition SkM44.h:99
float x
Definition SkM44.h:99
float z
Definition SkM44.h:99