Brian Osman | d927bd2 | 2019-12-18 11:23:12 -0500 | [diff] [blame] | 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 | |
| 8 | #include "tools/viewer/SkSLSlide.h" |
| 9 | |
Brian Osman | 173e153 | 2019-12-18 15:44:27 -0500 | [diff] [blame] | 10 | #include "include/effects/SkGradientShader.h" |
Brian Osman | 87e3bef | 2020-01-27 16:21:34 -0500 | [diff] [blame^] | 11 | #include "include/effects/SkPerlinNoiseShader.h" |
Brian Osman | 173e153 | 2019-12-18 15:44:27 -0500 | [diff] [blame] | 12 | #include "src/core/SkEnumerate.h" |
Brian Osman | d927bd2 | 2019-12-18 11:23:12 -0500 | [diff] [blame] | 13 | #include "tools/Resources.h" |
| 14 | #include "tools/viewer/ImGuiLayer.h" |
| 15 | |
Brian Osman | 107c666 | 2019-12-30 15:02:30 -0500 | [diff] [blame] | 16 | #include <algorithm> |
Brian Osman | d927bd2 | 2019-12-18 11:23:12 -0500 | [diff] [blame] | 17 | #include "imgui.h" |
| 18 | |
| 19 | using namespace sk_app; |
| 20 | |
| 21 | /////////////////////////////////////////////////////////////////////////////// |
| 22 | |
| 23 | static int InputTextCallback(ImGuiInputTextCallbackData* data) { |
| 24 | if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) { |
| 25 | SkString* s = (SkString*)data->UserData; |
| 26 | SkASSERT(data->Buf == s->writable_str()); |
| 27 | SkString tmp(data->Buf, data->BufTextLen); |
| 28 | s->swap(tmp); |
| 29 | data->Buf = s->writable_str(); |
| 30 | } |
| 31 | return 0; |
| 32 | } |
| 33 | |
| 34 | SkSLSlide::SkSLSlide() { |
| 35 | // Register types for serialization |
| 36 | fName = "SkSL"; |
| 37 | |
| 38 | fSkSL = |
| 39 | |
Brian Osman | 87e3bef | 2020-01-27 16:21:34 -0500 | [diff] [blame^] | 40 | "in fragmentProcessor fp;\n" |
Brian Osman | d927bd2 | 2019-12-18 11:23:12 -0500 | [diff] [blame] | 41 | "\n" |
| 42 | "void main(float x, float y, inout half4 color) {\n" |
Brian Osman | 87e3bef | 2020-01-27 16:21:34 -0500 | [diff] [blame^] | 43 | " color = sample(fp, float2(x, y));\n" |
Brian Osman | d927bd2 | 2019-12-18 11:23:12 -0500 | [diff] [blame] | 44 | "}\n"; |
Brian Osman | 173e153 | 2019-12-18 15:44:27 -0500 | [diff] [blame] | 45 | } |
| 46 | |
| 47 | void SkSLSlide::load(SkScalar winWidth, SkScalar winHeight) { |
| 48 | SkPoint points[] = { { 0, 0 }, { 256, 0 } }; |
| 49 | SkColor colors[] = { SK_ColorRED, SK_ColorGREEN }; |
| 50 | |
| 51 | sk_sp<SkShader> shader; |
| 52 | |
| 53 | shader = SkGradientShader::MakeLinear(points, colors, nullptr, 2, SkTileMode::kClamp); |
| 54 | fShaders.push_back(std::make_pair("Linear Gradient", shader)); |
| 55 | |
| 56 | shader = SkGradientShader::MakeRadial({ 128, 128 }, 128, colors, nullptr, 2, |
| 57 | SkTileMode::kClamp); |
| 58 | fShaders.push_back(std::make_pair("Radial Gradient", shader)); |
| 59 | |
| 60 | shader = SkGradientShader::MakeSweep(128, 128, colors, nullptr, 2); |
| 61 | fShaders.push_back(std::make_pair("Sweep Gradient", shader)); |
| 62 | |
| 63 | shader = GetResourceAsImage("images/mandrill_256.png")->makeShader(); |
| 64 | fShaders.push_back(std::make_pair("Mandrill", shader)); |
Brian Osman | d927bd2 | 2019-12-18 11:23:12 -0500 | [diff] [blame] | 65 | |
Brian Osman | 87e3bef | 2020-01-27 16:21:34 -0500 | [diff] [blame^] | 66 | shader = SkPerlinNoiseShader::MakeImprovedNoise(0.025f, 0.025f, 3, 0.0f); |
| 67 | fShaders.push_back(std::make_pair("Perlin Noise", shader)); |
| 68 | |
Brian Osman | d927bd2 | 2019-12-18 11:23:12 -0500 | [diff] [blame] | 69 | this->rebuild(); |
| 70 | } |
| 71 | |
Brian Osman | 173e153 | 2019-12-18 15:44:27 -0500 | [diff] [blame] | 72 | void SkSLSlide::unload() { |
| 73 | fEffect.reset(); |
| 74 | fInputs.reset(); |
| 75 | fChildren.reset(); |
| 76 | fShaders.reset(); |
| 77 | } |
| 78 | |
Brian Osman | d927bd2 | 2019-12-18 11:23:12 -0500 | [diff] [blame] | 79 | bool SkSLSlide::rebuild() { |
Brian Osman | 088913a | 2019-12-19 15:44:56 -0500 | [diff] [blame] | 80 | auto [effect, errorText] = SkRuntimeEffect::Make(fSkSL); |
| 81 | if (!effect) { |
Brian Osman | d927bd2 | 2019-12-18 11:23:12 -0500 | [diff] [blame] | 82 | return false; |
| 83 | } |
| 84 | |
| 85 | size_t oldSize = fEffect ? fEffect->inputSize() : 0; |
| 86 | fInputs.realloc(effect->inputSize()); |
| 87 | if (effect->inputSize() > oldSize) { |
| 88 | memset(fInputs.get() + oldSize, 0, effect->inputSize() - oldSize); |
| 89 | } |
Brian Osman | f72dedd | 2020-01-08 13:19:58 -0500 | [diff] [blame] | 90 | fChildren.resize_back(effect->children().count()); |
Brian Osman | 173e153 | 2019-12-18 15:44:27 -0500 | [diff] [blame] | 91 | for (auto& c : fChildren) { |
| 92 | if (!c) { |
| 93 | c = fShaders[0].second; |
| 94 | } |
| 95 | } |
| 96 | |
Brian Osman | d927bd2 | 2019-12-18 11:23:12 -0500 | [diff] [blame] | 97 | fEffect = effect; |
| 98 | return true; |
| 99 | } |
| 100 | |
| 101 | void SkSLSlide::draw(SkCanvas* canvas) { |
| 102 | canvas->clear(SK_ColorWHITE); |
| 103 | |
| 104 | ImGui::Begin("SkSL", nullptr, ImGuiWindowFlags_AlwaysVerticalScrollbar); |
| 105 | |
| 106 | // Edit box for shader code |
| 107 | ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize; |
| 108 | ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * 30); |
| 109 | if (ImGui::InputTextMultiline("Code", fSkSL.writable_str(), fSkSL.size() + 1, |
| 110 | boxSize, flags, InputTextCallback, &fSkSL)) { |
| 111 | this->rebuild(); |
| 112 | } |
| 113 | |
Brian Osman | 173e153 | 2019-12-18 15:44:27 -0500 | [diff] [blame] | 114 | if (!fEffect) { |
| 115 | ImGui::End(); |
| 116 | return; |
| 117 | } |
| 118 | |
Brian Osman | f72dedd | 2020-01-08 13:19:58 -0500 | [diff] [blame] | 119 | for (const auto& v : fEffect->inputs()) { |
Brian Osman | d927bd2 | 2019-12-18 11:23:12 -0500 | [diff] [blame] | 120 | switch (v.fType) { |
| 121 | case SkRuntimeEffect::Variable::Type::kBool: |
| 122 | ImGui::Checkbox(v.fName.c_str(), (bool*)(fInputs.get() + v.fOffset)); |
| 123 | break; |
| 124 | case SkRuntimeEffect::Variable::Type::kInt: |
| 125 | ImGui::DragInt(v.fName.c_str(), (int*)(fInputs.get() + v.fOffset)); |
| 126 | break; |
| 127 | case SkRuntimeEffect::Variable::Type::kFloat: |
| 128 | case SkRuntimeEffect::Variable::Type::kFloat2: |
| 129 | case SkRuntimeEffect::Variable::Type::kFloat3: |
| 130 | case SkRuntimeEffect::Variable::Type::kFloat4: { |
| 131 | int rows = ((int)v.fType - (int)SkRuntimeEffect::Variable::Type::kFloat) + 1; |
| 132 | float* f = (float*)(fInputs.get() + v.fOffset); |
| 133 | for (int c = 0; c < v.fCount; ++c, f += rows) { |
| 134 | SkString name = v.isArray() ? SkStringPrintf("%s[%d]", v.fName.c_str(), c) |
| 135 | : v.fName; |
| 136 | ImGui::PushID(c); |
| 137 | ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f); |
| 138 | ImGui::PopID(); |
| 139 | } |
| 140 | break; |
| 141 | } |
| 142 | case SkRuntimeEffect::Variable::Type::kFloat2x2: |
| 143 | case SkRuntimeEffect::Variable::Type::kFloat3x3: |
| 144 | case SkRuntimeEffect::Variable::Type::kFloat4x4: { |
| 145 | int rows = ((int)v.fType - (int)SkRuntimeEffect::Variable::Type::kFloat2x2) + 2; |
| 146 | int cols = rows; |
| 147 | float* f = (float*)(fInputs.get() + v.fOffset); |
| 148 | for (int e = 0; e < v.fCount; ++e) { |
| 149 | for (int c = 0; c < cols; ++c, f += rows) { |
| 150 | SkString name = v.isArray() |
| 151 | ? SkStringPrintf("%s[%d][%d]", v.fName.c_str(), e, c) |
| 152 | : SkStringPrintf("%s[%d]", v.fName.c_str(), c); |
| 153 | ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f); |
| 154 | } |
| 155 | } |
| 156 | break; |
| 157 | } |
| 158 | } |
| 159 | } |
| 160 | |
Brian Osman | f72dedd | 2020-01-08 13:19:58 -0500 | [diff] [blame] | 161 | for (const auto [i, name] : SkMakeEnumerate(fEffect->children())) { |
Brian Osman | 173e153 | 2019-12-18 15:44:27 -0500 | [diff] [blame] | 162 | auto curShader = std::find_if(fShaders.begin(), fShaders.end(), |
| 163 | [tgt = fChildren[i]](auto p) { return p.second == tgt; }); |
| 164 | SkASSERT(curShader!= fShaders.end()); |
| 165 | |
| 166 | if (ImGui::BeginCombo(name.c_str(), curShader->first)) { |
| 167 | for (const auto& namedShader : fShaders) { |
| 168 | if (ImGui::Selectable(namedShader.first, curShader->second == namedShader.second)) { |
| 169 | fChildren[i] = namedShader.second; |
| 170 | } |
| 171 | } |
| 172 | ImGui::EndCombo(); |
| 173 | } |
| 174 | } |
| 175 | |
Brian Osman | d927bd2 | 2019-12-18 11:23:12 -0500 | [diff] [blame] | 176 | ImGui::End(); |
| 177 | |
Brian Osman | 173e153 | 2019-12-18 15:44:27 -0500 | [diff] [blame] | 178 | auto inputs = SkData::MakeWithoutCopy(fInputs.get(), fEffect->inputSize()); |
Brian Osman | 93de162 | 2019-12-26 08:43:05 -0500 | [diff] [blame] | 179 | auto shader = fEffect->makeShader(std::move(inputs), fChildren.data(), fChildren.count(), |
| 180 | nullptr, false); |
| 181 | |
Brian Osman | d927bd2 | 2019-12-18 11:23:12 -0500 | [diff] [blame] | 182 | SkPaint p; |
| 183 | p.setShader(std::move(shader)); |
| 184 | canvas->drawRect({ 0, 0, 256, 256 }, p); |
| 185 | } |