2019-12-18 16:23:12 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2019 Google LLC
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "tools/viewer/SkSLSlide.h"
|
|
|
|
|
2020-04-06 17:57:30 +00:00
|
|
|
#include "include/core/SkCanvas.h"
|
2020-12-11 17:51:06 +00:00
|
|
|
#include "include/core/SkFont.h"
|
2019-12-18 20:44:27 +00:00
|
|
|
#include "include/effects/SkGradientShader.h"
|
2020-01-27 21:21:34 +00:00
|
|
|
#include "include/effects/SkPerlinNoiseShader.h"
|
2019-12-18 20:44:27 +00:00
|
|
|
#include "src/core/SkEnumerate.h"
|
2019-12-18 16:23:12 +00:00
|
|
|
#include "tools/Resources.h"
|
2020-06-18 18:18:27 +00:00
|
|
|
#include "tools/viewer/Viewer.h"
|
2019-12-18 16:23:12 +00:00
|
|
|
|
2019-12-30 20:02:30 +00:00
|
|
|
#include <algorithm>
|
2020-12-11 16:21:35 +00:00
|
|
|
#include <cstdio>
|
2019-12-18 16:23:12 +00:00
|
|
|
#include "imgui.h"
|
|
|
|
|
|
|
|
using namespace sk_app;
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static int InputTextCallback(ImGuiInputTextCallbackData* data) {
|
|
|
|
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) {
|
|
|
|
SkString* s = (SkString*)data->UserData;
|
|
|
|
SkASSERT(data->Buf == s->writable_str());
|
|
|
|
SkString tmp(data->Buf, data->BufTextLen);
|
|
|
|
s->swap(tmp);
|
|
|
|
data->Buf = s->writable_str();
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkSLSlide::SkSLSlide() {
|
|
|
|
// Register types for serialization
|
|
|
|
fName = "SkSL";
|
|
|
|
|
|
|
|
fSkSL =
|
|
|
|
|
2020-11-04 20:40:50 +00:00
|
|
|
"uniform shader child;\n"
|
2019-12-18 16:23:12 +00:00
|
|
|
"\n"
|
2020-08-13 20:59:48 +00:00
|
|
|
"half4 main(float2 p) {\n"
|
|
|
|
" return sample(child, p);\n"
|
2019-12-18 16:23:12 +00:00
|
|
|
"}\n";
|
2020-03-06 20:23:54 +00:00
|
|
|
|
|
|
|
fCodeIsDirty = true;
|
2019-12-18 20:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SkSLSlide::load(SkScalar winWidth, SkScalar winHeight) {
|
|
|
|
SkPoint points[] = { { 0, 0 }, { 256, 0 } };
|
|
|
|
SkColor colors[] = { SK_ColorRED, SK_ColorGREEN };
|
|
|
|
|
|
|
|
sk_sp<SkShader> shader;
|
|
|
|
|
2020-07-10 18:08:56 +00:00
|
|
|
fShaders.push_back(std::make_pair("Null", nullptr));
|
|
|
|
|
2019-12-18 20:44:27 +00:00
|
|
|
shader = SkGradientShader::MakeLinear(points, colors, nullptr, 2, SkTileMode::kClamp);
|
|
|
|
fShaders.push_back(std::make_pair("Linear Gradient", shader));
|
|
|
|
|
2020-12-11 16:22:57 +00:00
|
|
|
shader = SkGradientShader::MakeRadial({ 256, 256 }, 256, colors, nullptr, 2,
|
2019-12-18 20:44:27 +00:00
|
|
|
SkTileMode::kClamp);
|
|
|
|
fShaders.push_back(std::make_pair("Radial Gradient", shader));
|
|
|
|
|
2020-12-11 16:22:57 +00:00
|
|
|
shader = SkGradientShader::MakeSweep(256, 256, colors, nullptr, 2);
|
2019-12-18 20:44:27 +00:00
|
|
|
fShaders.push_back(std::make_pair("Sweep Gradient", shader));
|
|
|
|
|
2020-12-10 19:55:43 +00:00
|
|
|
shader = GetResourceAsImage("images/mandrill_256.png")->makeShader(SkSamplingOptions());
|
2019-12-18 20:44:27 +00:00
|
|
|
fShaders.push_back(std::make_pair("Mandrill", shader));
|
2019-12-18 16:23:12 +00:00
|
|
|
|
2020-12-11 19:31:54 +00:00
|
|
|
fResolution = { winWidth, winHeight, 1.0f };
|
2019-12-18 16:23:12 +00:00
|
|
|
}
|
|
|
|
|
2019-12-18 20:44:27 +00:00
|
|
|
void SkSLSlide::unload() {
|
|
|
|
fEffect.reset();
|
|
|
|
fInputs.reset();
|
|
|
|
fChildren.reset();
|
|
|
|
fShaders.reset();
|
|
|
|
}
|
|
|
|
|
2020-06-18 18:18:27 +00:00
|
|
|
bool SkSLSlide::rebuild() {
|
2020-12-11 19:31:54 +00:00
|
|
|
// Some of the standard shadertoy inputs:
|
|
|
|
SkString sksl("uniform float3 iResolution;\n"
|
|
|
|
"uniform float iTime;\n"
|
|
|
|
"uniform float4 iMouse;\n");
|
2020-12-11 15:09:03 +00:00
|
|
|
sksl.append(fSkSL);
|
2020-12-11 16:21:35 +00:00
|
|
|
|
|
|
|
// It shouldn't happen, but it's possible to assert in the compiler, especially mid-edit.
|
|
|
|
// To guard against losing your work, write out the shader to a backup file, then remove it
|
|
|
|
// when we compile successfully.
|
|
|
|
constexpr char kBackupFile[] = "sksl.bak";
|
|
|
|
FILE* backup = fopen(kBackupFile, "w");
|
|
|
|
if (backup) {
|
|
|
|
fwrite(fSkSL.c_str(), 1, fSkSL.size(), backup);
|
|
|
|
fclose(backup);
|
|
|
|
}
|
2020-12-11 15:09:03 +00:00
|
|
|
auto [effect, errorText] = SkRuntimeEffect::Make(sksl);
|
2020-12-11 16:21:35 +00:00
|
|
|
if (backup) {
|
|
|
|
std::remove(kBackupFile);
|
|
|
|
}
|
|
|
|
|
2019-12-19 20:44:56 +00:00
|
|
|
if (!effect) {
|
2020-12-11 15:09:03 +00:00
|
|
|
Viewer::ShaderErrorHandler()->compileError(sksl.c_str(), errorText.c_str());
|
2019-12-18 16:23:12 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Remove 'in' variables from SkRuntimeEffect
Runtime effects previously allowed two kinds of global input variables:
'in' variables could be bool, int, or float. 'uniform' could be float,
vector, or matrix. Uniform variables worked like you'd expect, but 'in'
variables were baked into the program statically. There was a large
amount of machinery to make this work, and it meant that 'in' variables
needed to have values before we could make decisions about program
caching, and before we could catch some errors. It was also essentially
syntactic sugar over the client just inserting the value into their SkSL
as a string. Finally: No one was using the feature.
To simplify the mental model, and make the API much more predictable,
this CL removes 'in' variables entirely. We no longer need to
"specialize" runtime effect programs, which means we can catch more
errors up front (those not detected until optimization). All of the API
that referred to "inputs" (the previous term that unified 'in' and
'uniform') now just refers to "uniforms".
Bug: skia:10593
Change-Id: I971f620d868b259e652b3114f0b497c2620f4b0c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309050
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
2020-08-10 18:26:16 +00:00
|
|
|
size_t oldSize = fEffect ? fEffect->uniformSize() : 0;
|
|
|
|
fInputs.realloc(effect->uniformSize());
|
|
|
|
if (effect->uniformSize() > oldSize) {
|
|
|
|
memset(fInputs.get() + oldSize, 0, effect->uniformSize() - oldSize);
|
2019-12-18 16:23:12 +00:00
|
|
|
}
|
2020-01-08 18:19:58 +00:00
|
|
|
fChildren.resize_back(effect->children().count());
|
2019-12-18 20:44:27 +00:00
|
|
|
|
2019-12-18 16:23:12 +00:00
|
|
|
fEffect = effect;
|
2020-03-06 20:23:54 +00:00
|
|
|
fCodeIsDirty = false;
|
2019-12-18 16:23:12 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SkSLSlide::draw(SkCanvas* canvas) {
|
|
|
|
canvas->clear(SK_ColorWHITE);
|
|
|
|
|
|
|
|
ImGui::Begin("SkSL", nullptr, ImGuiWindowFlags_AlwaysVerticalScrollbar);
|
|
|
|
|
|
|
|
// Edit box for shader code
|
|
|
|
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize;
|
|
|
|
ImVec2 boxSize(-1.0f, ImGui::GetTextLineHeight() * 30);
|
2020-03-06 20:23:54 +00:00
|
|
|
if (ImGui::InputTextMultiline("Code", fSkSL.writable_str(), fSkSL.size() + 1, boxSize, flags,
|
|
|
|
InputTextCallback, &fSkSL)) {
|
|
|
|
fCodeIsDirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fCodeIsDirty || !fEffect) {
|
2020-06-18 18:18:27 +00:00
|
|
|
this->rebuild();
|
2019-12-18 16:23:12 +00:00
|
|
|
}
|
|
|
|
|
2019-12-18 20:44:27 +00:00
|
|
|
if (!fEffect) {
|
|
|
|
ImGui::End();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-11 19:31:54 +00:00
|
|
|
// Update fMousePos
|
|
|
|
ImVec2 mousePos = ImGui::GetMousePos();
|
|
|
|
if (ImGui::IsMouseDown(0)) {
|
|
|
|
fMousePos.x = mousePos.x;
|
|
|
|
fMousePos.y = mousePos.y;
|
|
|
|
}
|
|
|
|
if (ImGui::IsMouseClicked(0)) {
|
|
|
|
fMousePos.z = mousePos.x;
|
|
|
|
fMousePos.w = mousePos.y;
|
|
|
|
}
|
|
|
|
fMousePos.z = abs(fMousePos.z) * (ImGui::IsMouseDown(0) ? 1 : -1);
|
|
|
|
fMousePos.w = abs(fMousePos.w) * (ImGui::IsMouseClicked(0) ? 1 : -1);
|
|
|
|
|
Remove 'in' variables from SkRuntimeEffect
Runtime effects previously allowed two kinds of global input variables:
'in' variables could be bool, int, or float. 'uniform' could be float,
vector, or matrix. Uniform variables worked like you'd expect, but 'in'
variables were baked into the program statically. There was a large
amount of machinery to make this work, and it meant that 'in' variables
needed to have values before we could make decisions about program
caching, and before we could catch some errors. It was also essentially
syntactic sugar over the client just inserting the value into their SkSL
as a string. Finally: No one was using the feature.
To simplify the mental model, and make the API much more predictable,
this CL removes 'in' variables entirely. We no longer need to
"specialize" runtime effect programs, which means we can catch more
errors up front (those not detected until optimization). All of the API
that referred to "inputs" (the previous term that unified 'in' and
'uniform') now just refers to "uniforms".
Bug: skia:10593
Change-Id: I971f620d868b259e652b3114f0b497c2620f4b0c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309050
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
2020-08-10 18:26:16 +00:00
|
|
|
for (const auto& v : fEffect->uniforms()) {
|
2020-12-11 19:31:54 +00:00
|
|
|
char* data = fInputs.get() + v.fOffset;
|
|
|
|
if (v.fName.equals("iResolution")) {
|
|
|
|
memcpy(data, &fResolution, sizeof(fResolution));
|
|
|
|
continue;
|
|
|
|
}
|
2020-12-11 15:09:03 +00:00
|
|
|
if (v.fName.equals("iTime")) {
|
2020-12-11 19:31:54 +00:00
|
|
|
memcpy(data, &fSeconds, sizeof(fSeconds));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (v.fName.equals("iMouse")) {
|
|
|
|
memcpy(data, &fMousePos, sizeof(fMousePos));
|
2020-12-11 15:09:03 +00:00
|
|
|
continue;
|
|
|
|
}
|
2019-12-18 16:23:12 +00:00
|
|
|
switch (v.fType) {
|
Remove 'in' variables from SkRuntimeEffect
Runtime effects previously allowed two kinds of global input variables:
'in' variables could be bool, int, or float. 'uniform' could be float,
vector, or matrix. Uniform variables worked like you'd expect, but 'in'
variables were baked into the program statically. There was a large
amount of machinery to make this work, and it meant that 'in' variables
needed to have values before we could make decisions about program
caching, and before we could catch some errors. It was also essentially
syntactic sugar over the client just inserting the value into their SkSL
as a string. Finally: No one was using the feature.
To simplify the mental model, and make the API much more predictable,
this CL removes 'in' variables entirely. We no longer need to
"specialize" runtime effect programs, which means we can catch more
errors up front (those not detected until optimization). All of the API
that referred to "inputs" (the previous term that unified 'in' and
'uniform') now just refers to "uniforms".
Bug: skia:10593
Change-Id: I971f620d868b259e652b3114f0b497c2620f4b0c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309050
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
2020-08-10 18:26:16 +00:00
|
|
|
case SkRuntimeEffect::Uniform::Type::kFloat:
|
|
|
|
case SkRuntimeEffect::Uniform::Type::kFloat2:
|
|
|
|
case SkRuntimeEffect::Uniform::Type::kFloat3:
|
|
|
|
case SkRuntimeEffect::Uniform::Type::kFloat4: {
|
|
|
|
int rows = ((int)v.fType - (int)SkRuntimeEffect::Uniform::Type::kFloat) + 1;
|
2020-12-11 19:31:54 +00:00
|
|
|
float* f = reinterpret_cast<float*>(data);
|
2019-12-18 16:23:12 +00:00
|
|
|
for (int c = 0; c < v.fCount; ++c, f += rows) {
|
|
|
|
SkString name = v.isArray() ? SkStringPrintf("%s[%d]", v.fName.c_str(), c)
|
|
|
|
: v.fName;
|
|
|
|
ImGui::PushID(c);
|
|
|
|
ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f);
|
|
|
|
ImGui::PopID();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
Remove 'in' variables from SkRuntimeEffect
Runtime effects previously allowed two kinds of global input variables:
'in' variables could be bool, int, or float. 'uniform' could be float,
vector, or matrix. Uniform variables worked like you'd expect, but 'in'
variables were baked into the program statically. There was a large
amount of machinery to make this work, and it meant that 'in' variables
needed to have values before we could make decisions about program
caching, and before we could catch some errors. It was also essentially
syntactic sugar over the client just inserting the value into their SkSL
as a string. Finally: No one was using the feature.
To simplify the mental model, and make the API much more predictable,
this CL removes 'in' variables entirely. We no longer need to
"specialize" runtime effect programs, which means we can catch more
errors up front (those not detected until optimization). All of the API
that referred to "inputs" (the previous term that unified 'in' and
'uniform') now just refers to "uniforms".
Bug: skia:10593
Change-Id: I971f620d868b259e652b3114f0b497c2620f4b0c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309050
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
2020-08-10 18:26:16 +00:00
|
|
|
case SkRuntimeEffect::Uniform::Type::kFloat2x2:
|
|
|
|
case SkRuntimeEffect::Uniform::Type::kFloat3x3:
|
|
|
|
case SkRuntimeEffect::Uniform::Type::kFloat4x4: {
|
|
|
|
int rows = ((int)v.fType - (int)SkRuntimeEffect::Uniform::Type::kFloat2x2) + 2;
|
2019-12-18 16:23:12 +00:00
|
|
|
int cols = rows;
|
2020-12-11 19:31:54 +00:00
|
|
|
float* f = reinterpret_cast<float*>(data);
|
2019-12-18 16:23:12 +00:00
|
|
|
for (int e = 0; e < v.fCount; ++e) {
|
|
|
|
for (int c = 0; c < cols; ++c, f += rows) {
|
|
|
|
SkString name = v.isArray()
|
|
|
|
? SkStringPrintf("%s[%d][%d]", v.fName.c_str(), e, c)
|
|
|
|
: SkStringPrintf("%s[%d]", v.fName.c_str(), c);
|
|
|
|
ImGui::DragScalarN(name.c_str(), ImGuiDataType_Float, f, rows, 1.0f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-31 00:24:57 +00:00
|
|
|
for (const auto& [i, name] : SkMakeEnumerate(fEffect->children())) {
|
2019-12-18 20:44:27 +00:00
|
|
|
auto curShader = std::find_if(fShaders.begin(), fShaders.end(),
|
|
|
|
[tgt = fChildren[i]](auto p) { return p.second == tgt; });
|
|
|
|
SkASSERT(curShader!= fShaders.end());
|
|
|
|
|
|
|
|
if (ImGui::BeginCombo(name.c_str(), curShader->first)) {
|
|
|
|
for (const auto& namedShader : fShaders) {
|
|
|
|
if (ImGui::Selectable(namedShader.first, curShader->second == namedShader.second)) {
|
|
|
|
fChildren[i] = namedShader.second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndCombo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-10 18:08:56 +00:00
|
|
|
static SkColor4f gPaintColor { 1.0f, 1.0f, 1.0f , 1.0f };
|
|
|
|
ImGui::ColorEdit4("Paint Color", gPaintColor.vec());
|
|
|
|
|
2020-12-11 17:51:06 +00:00
|
|
|
ImGui::RadioButton("Fill", &fGeometry, kFill); ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("Circle", &fGeometry, kCircle); ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("RoundRect", &fGeometry, kRoundRect); ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("Capsule", &fGeometry, kCapsule); ImGui::SameLine();
|
|
|
|
ImGui::RadioButton("Text", &fGeometry, kText);
|
|
|
|
|
2019-12-18 16:23:12 +00:00
|
|
|
ImGui::End();
|
|
|
|
|
Remove 'in' variables from SkRuntimeEffect
Runtime effects previously allowed two kinds of global input variables:
'in' variables could be bool, int, or float. 'uniform' could be float,
vector, or matrix. Uniform variables worked like you'd expect, but 'in'
variables were baked into the program statically. There was a large
amount of machinery to make this work, and it meant that 'in' variables
needed to have values before we could make decisions about program
caching, and before we could catch some errors. It was also essentially
syntactic sugar over the client just inserting the value into their SkSL
as a string. Finally: No one was using the feature.
To simplify the mental model, and make the API much more predictable,
this CL removes 'in' variables entirely. We no longer need to
"specialize" runtime effect programs, which means we can catch more
errors up front (those not detected until optimization). All of the API
that referred to "inputs" (the previous term that unified 'in' and
'uniform') now just refers to "uniforms".
Bug: skia:10593
Change-Id: I971f620d868b259e652b3114f0b497c2620f4b0c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309050
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
2020-08-10 18:26:16 +00:00
|
|
|
auto inputs = SkData::MakeWithoutCopy(fInputs.get(), fEffect->uniformSize());
|
2019-12-26 13:43:05 +00:00
|
|
|
auto shader = fEffect->makeShader(std::move(inputs), fChildren.data(), fChildren.count(),
|
|
|
|
nullptr, false);
|
|
|
|
|
2019-12-18 16:23:12 +00:00
|
|
|
SkPaint p;
|
2020-07-10 18:08:56 +00:00
|
|
|
p.setColor4f(gPaintColor);
|
2019-12-18 16:23:12 +00:00
|
|
|
p.setShader(std::move(shader));
|
2020-12-11 17:51:06 +00:00
|
|
|
|
|
|
|
switch (fGeometry) {
|
|
|
|
case kFill:
|
|
|
|
canvas->drawPaint(p);
|
|
|
|
break;
|
|
|
|
case kCircle:
|
|
|
|
canvas->drawCircle({ 256, 256 }, 256, p);
|
|
|
|
break;
|
|
|
|
case kRoundRect:
|
|
|
|
canvas->drawRoundRect({ 0, 0, 512, 512 }, 64, 64, p);
|
|
|
|
break;
|
|
|
|
case kCapsule:
|
|
|
|
canvas->drawRoundRect({ 0, 224, 512, 288 }, 32, 32, p);
|
|
|
|
break;
|
|
|
|
case kText: {
|
|
|
|
SkFont font;
|
|
|
|
font.setSize(SkIntToScalar(96));
|
|
|
|
canvas->drawSimpleText("Hello World", strlen("Hello World"), SkTextEncoding::kUTF8, 0,
|
|
|
|
256, font, p);
|
|
|
|
} break;
|
|
|
|
default: break;
|
|
|
|
}
|
2019-12-18 16:23:12 +00:00
|
|
|
}
|
2020-12-11 15:09:03 +00:00
|
|
|
|
|
|
|
bool SkSLSlide::animate(double nanos) {
|
|
|
|
fSeconds = static_cast<float>(nanos * 1E-9);
|
|
|
|
return true;
|
|
|
|
}
|