はじめに
あのあといろいろ追加されたので追記という形で記事にします。p5.js/2.0.1のレファレンス:
そうです。あのあと2.0.1になりました。最新版:
とはいえリンク先をたどっても、デモが付いていなかったりして何が変更されたのかいまいち分かりませんでした。まあそのうちわかることもあるかと思います。
現在もデフォルトは1.11.5です。しかし予告されているように来年の夏ごろには2.0以降がデフォルトになります。1.11.5以前のバージョンでしかコードを書きたくない場合、更新後はいちいち過去バージョンに戻さなければならなくなります。面倒なので今のうちに慣れておいた方がいい場合もあるかもしれないです。とはいえ作品によっては過去バージョンがいい場合もあるでしょう。どのみち、作品制作時に勝手にバージョンが変更されるというわけでは無いので、毎回の制作に集中する上では全く問題にならないとも言えます。
いろいろ
2.0.1でもimage()とcreateVector()のバグは直っていない
前回紹介した2つのフレンドリーエラーによるバグは2.0.1でも直っていません。
なおいずれも捕捉済みで、issueが提示されています。
createVector:
image:
しかしいずれもactiveではないので、いつ解決するのか見通しが見えません。興味が無いのでしょうか?
なお、このようにissueを上げようとしてもすでに捕捉済みの場合というのは多いので、提出する際はきちんとすでに捕捉されているかどうかをできる範囲で調べるようにしましょう。自分がバグを直したときも、直した後にも関わらず、リリースしてもいないのに直ってないじゃないかという質問をされたことがあったりしました。
ちなみにimage関数の第一引数がcreateGraphicsで作ったやつだとエラーが出るのはグローバルの場合だけです。オフキャンバスの関数として呼び出す場合、エラーは生じません。
とはいえ最終的には地に落とさないといけないのであれですが。
createVectorについてですが、使う人は毎フレーム何百回とか何千回とか呼んでるわけです。今のままこれがデフォルトになったら地獄ですよ。興味が無いとか言ってないで早く直すべきです。開発サイドは新機能にばかり目が行ってしまっていて、従来通りのスケッチを描きたい人にあまりにも冷たい印象を受けます。ほんとうにデフォルトの設定にする気があるのか疑問です。もういつまでもオプションのままでいいような気がします。
オフキャンバスの関数としてのcolorMode()が廃止されてグローバルで変える仕様になった
どういうことかというと、以前はcreateGraphicsで作ったキャンバスにcolorModeを指定する場合、それの関数として呼び出していたわけです。
グローバルで呼び出しても変化がありませんでした。それはグローバルのfill()などに影響します。
それが2.0以降で変更になりました。2.0以降は、そもそもgr自身によりcolorMode()という関数を扱うことができません。
このように変化しません。そして、グローバルで呼び出すとgrのcolorMode「も」変更されます。
すべて共通になったというわけですね。なお有志の方が調べてくれましたがblendModeは個別のままのようです。
まあこれはそもそもコンテキスト属性で、p5が関係ないので、ある意味当然かもしれません。同じ理由で本来コンテキスト属性であるfontSizeやfillStyle,storkeStyleに該当するあらゆる属性はグラフィック固有のままです。colorModeはp5が絡むので、仕様を変えてもいいということになったのかもしれないですね。
該当するissueが見つかりませんでした。気が向いたら探してみてください。探す必要性を感じないのでスルーします。
オフキャンバスのtext関連の関数の戻り値が変更され、チェインで書けなくなった
以前の仕様では、createGraphicsで作ったグラフィックにtext関数を実行させる場合、戻り値はグラフィック自身になっていました。そのため、チェインで書けたりしました。
2.0以降、この戻り値が変更され、これは不可能になりました。
ざっくりいうと、textの戻り値はなく、それ以外はp5のインスタンスが返るようになりました。これも、いつ行われたのかは不明です。まあ特に不便では無いので、どうでもいいですね。
textBounds()がフォントの関数では無くなり、フォントをロードしてなくても使えるようになった
以前のtextBoundsはフォントを指定しないと使えないという不便さがありました。
また、絵文字でバグるのも合わせて使い勝手が悪かったです。それが変更になりました、
このようにフォントを用意しなくても使えるようになりました。なお絵文字にも対応しており、小さい場合は小さくもなります。
ふい字などのカスタムと絵文字が混じる場合でも安心です。
便利ですね~
OpenProcessingのエディタで2.0以降を指定すると、キャンバス外の色がbacckground()などに追従しなくなる
具体的にはこういうこと:
background()とか使うと、画面外に色が漏れてしまうわけです。キャンバスの境目が見えづらいので不便です。
それが解消されています。
OpenProcessingでp5.Soundのリンクが間違っている
具体的にはこういうことです。
素直に従来のp5.Soundのスケッチを2.0で実行するとバグります。これを解消するには以下のサイトからCDNを取得すればよいようです。
しかし、一部の関数が使えなくなったりしているので、これが正しいやり方かどうかはちょっと分かんないです。このデモの場合はきちんと動いています。なお、現在も解消されていません。
色関連の関数(fill,strokeなど)が重くなった
一部の関数が2.0以降、重くなっています。とはいえ、毎フレーム2000回とか3000回とか呼び出す場合でないとその差を実感できないレベルなので、普通にスケッチを描いている限りはあまり気にならないかもしれません。とはいえ実際に重くなっているので、ここに書いておこうと思います。調べましたが特にissueも上がっていないようです。
ベンチマークテストのテンプレート
こんな感じで調べています。
function setup() {
noCanvas();
// 各種準備を実行する
let rp = 20;
let sum = 0;
const elapseds = [];
while(rp--){
let s=millis();
for(let i=0; i<4000; i++){
// 計測したい処理をここに記述する
}
const elapsed = millis()-s;
elapseds.push(elapsed);
sum += elapsed;
}
showElapseds(elapseds);
console.log(`ave:${sum/20}`);
}
function showElapseds(a){
const res = [];
for(let i=0; i<20; i++){
res.push(a[i].toFixed(3));
}
for(let y=0; y<4; y++){
console.log(`${res[y*5]}, ${res[y*5+1]}, ${res[y*5+2]}, ${res[y*5+3]}, ${res[y*5+4]}`);
}
}
たとえばsin関数のベンチマークを取るとこんな感じで表示されます。
// 計測したい処理部分
const si = sin(3.14);
1.11.5の場合
0.300, 0.300, 0.200, 0.100, 0.000
0.100, 0.000, 0.200, 0.100, 0.000
0.100, 0.100, 0.000, 0.200, 0.000
0.100, 0.100, 0.100, 0.000, 0.100
ave:0.10499999523162842
2.0.1の場合
4.000, 1.700, 1.700, 1.600, 1.700
1.600, 4.200, 1.100, 1.300, 1.500
1.000, 1.100, 1.100, 1.000, 4.500
1.300, 1.100, 1.000, 0.900, 1.000
ave:1.7200000047683717
なんか調べたら若干遅くなってる..?なお
const si = Math.sin(3.14);
では...
0.100, 0.000, 0.100, 0.000, 0.100
0.000, 0.100, 0.000, 0.000, 0.000
0.000, 0.000, 0.100, 0.000, 0.000
0.000, 0.000, 0.000, 0.000, 0.000
ave:0.025000011920928954
特に理由が無ければネイティブの方がいいでしょうね。毎フレーム何万回も呼び出す場合などは特に。
というわけで、サクサク調べていきましょう。
colorオブジェクトを作る
まずは手始めに、色オブジェクトを作ってみましょう。
// 計測部分
const col = color("#c0ffee");
コーヒーあんま飲まないですが。さて、1.11.5の場合:
3.100, 2.100, 2.300, 2.200, 1.800
4.700, 1.300, 1.300, 1.200, 1.800
1.500, 2.300, 1.800, 1.700, 1.600
4.500, 1.700, 1.600, 1.500, 1.500
ave:2.074999988079071
もちろん誤差もあるので絶対ではないです。何回か試して、これくらいかなぁ、というものを上げています。
2.0.1の場合:
24.100, 17.300, 12.900, 13.300, 12.300
12.000, 12.000, 11.700, 12.000, 12.100
12.100, 11.800, 11.800, 11.700, 11.900
12.000, 11.600, 12.000, 12.000, 12.300
ave:12.94500000476837
ただこれは誤差の範囲を超えています。何度試しても、1.11.5では4すら超えません。確実に遅くなっています。1フレームはどのスケッチでも大体16ミリ秒前後なので、4000回も呼び出すとそのうち75%がこの処理に費やされてしまいます。
fill()を実行する(数指定)
数でfillを指定します。
// 計測部分
fill(11,22,200);
1.11.5の場合
3.500, 1.900, 1.900, 1.800, 1.600
1.300, 1.400, 4.400, 1.000, 1.200
1.100, 1.400, 1.100, 1.400, 1.200
1.200, 1.200, 1.300, 1.200, 4.300
ave:1.7699999928474426
2.0.1の場合
20.400, 16.500, 12.100, 11.600, 11.400
11.300, 11.900, 11.100, 11.500, 11.300
10.900, 11.200, 11.100, 11.100, 10.500
13.200, 11.600, 11.200, 11.700, 12.300
ave:12.194999992847443
どうもfill内部で色オブジェクトを作ってる影響で、それに関する関数は軒並み遅くなってるようです。
fill()を実行する(オブジェクト指定)
次に、あらかじめオブジェクトを作っておきます。
// 準備部分
const precol = color("navy");
そしてこれを計測部分でfillの引数として使います。
// 計測部分
fill(precol);
さてどうなるでしょう。1.11.5の場合:
2.300, 1.400, 1.500, 1.400, 1.100
1.100, 1.000, 1.100, 1.100, 0.900
0.900, 0.800, 0.800, 0.900, 0.800
0.900, 1.100, 0.900, 3.900, 1.000
ave:1.2450000047683716
なんと数指定の場合より速くなりました。ちょっと意外ですね。あらかじめ作っておくと嬉しいのでしょうか。
2.0.1ではどうでしょう。
37.000, 24.600, 24.400, 23.500, 23.500
22.900, 40.600, 22.400, 24.100, 22.100
24.100, 23.400, 23.500, 24.100, 23.600
24.300, 24.000, 23.900, 23.500, 30.600
ave:25.50499999523163
ちょっとあり得ないですね。なぜここまで遅くなるのか...
strokeの場合
strokeで同じことをした場合の結果をざっくり載せておきます。
1.11.5で数指定
stroke(11,22,200);
// 1.11.5:
3.300, 1.900, 1.800, 5.800, 1.400
1.300, 1.400, 1.500, 1.200, 1.300
1.100, 1.400, 1.500, 1.600, 1.700
1.400, 1.300, 1.500, 1.600, 1.600
ave:1.7800000071525575
// 2.0.1:
23.200, 15.800, 13.900, 33.800, 12.900
11.400, 11.800, 11.600, 11.300, 11.300
11.100, 11.300, 11.000, 10.700, 11.100
10.800, 11.100, 11.000, 11.300, 10.900
ave:13.364999997615815
stroke(col);
// 1.11.5
2.400, 1.500, 2.100, 1.600, 1.200
1.100, 1.100, 5.700, 0.900, 0.800
0.900, 0.800, 0.700, 0.900, 0.800
1.000, 0.800, 0.800, 0.800, 0.900
ave:1.3400000095367433
// 2.0.1
36.400, 24.500, 23.500, 23.600, 22.900
23.000, 23.600, 22.800, 22.800, 22.900
23.300, 21.800, 39.100, 23.100, 22.500
23.000, 21.800, 22.100, 21.500, 21.200
ave:24.27000000476837
おまけ:drawingContextを使う場合
ちなみにネイティブの処理ではどうなっているかというと、
drawingContext.fillStyle = "navy";
0.500, 0.300, 0.300, 0.300, 0.200
0.200, 0.300, 0.300, 0.300, 0.200
0.300, 0.200, 0.300, 0.300, 0.200
0.400, 0.200, 0.300, 0.300, 0.300
ave:0.28500000238418577
drawingContext.strokeStyle = "teal";
0.400, 0.300, 0.300, 0.300, 0.200
0.300, 0.300, 0.200, 0.300, 0.300
0.200, 0.300, 0.200, 0.400, 0.100
0.400, 0.200, 0.300, 0.200, 0.300
ave:0.275
アホみたいに速いです。ただ色を決めるだけの処理がなぜこんなに遅くなるのか疑問です。簡易スマホみたいに、余計な機能は良いから速さが欲しい人のためのメソッドがあったら便利な気がします。いちいちdrawingContextを呼び出すのは手間ですから。
randomとnoise
ついでにrandomとnoiseも調べておきます。
const r = random();
// 1.11.5
0.500, 0.300, 0.300, 0.400, 0.100
0.300, 0.300, 0.100, 0.200, 0.100
0.100, 0.100, 0.100, 0.100, 0.100
0.200, 0.100, 0.100, 0.100, 0.200
ave:0.19000000953674318
// 2.0.1
0.800, 0.500, 0.500, 0.300, 0.300
0.200, 0.300, 0.200, 0.400, 0.200
0.200, 0.200, 0.200, 0.200, 0.200
0.200, 0.300, 0.200, 0.200, 0.200
ave:0.2899999856948853
あんま変わんないですね。noiseについて:
const n = noise(1,2,3);
// 1.11.5
2.300, 0.600, 0.500, 0.400, 0.500
0.500, 0.600, 0.300, 0.400, 0.300
0.300, 0.300, 0.300, 0.300, 0.200
0.300, 0.400, 0.300, 0.300, 0.300
ave:0.46999999284744265
// 2.0.1
9.700, 14.900, 13.300, 5.400, 4.800
4.800, 4.400, 5.100, 4.500, 4.700
4.400, 4.400, 4.500, 4.400, 4.600
4.500, 4.400, 4.500, 4.400, 4.600
ave:5.814999985694885
noise遅くなってますね。まあ4000回程度なら気にならないレベルかと思います。
lerpColor
これはあんま変わんなかったんですが、一応上げておきます。
// 準備:
const col = color("navy");
const other = color("teal");
// 計測部分
const l = lerpColor(col,other,0.5);
// 1.11.5
8.100, 5.800, 9.800, 5.500, 6.700
5.700, 5.800, 5.600, 6.000, 6.000
5.600, 5.500, 6.000, 5.400, 5.300
5.100, 5.200, 5.400, 5.400, 5.700
ave:5.979999995231628
// 2.0.1
19.800, 16.300, 11.200, 14.200, 13.600
10.800, 14.100, 13.900, 29.900, 10.800
12.800, 10.400, 13.700, 13.400, 10.100
13.200, 13.200, 10.300, 13.100, 13.000
ave:13.889999985694885
ほぼ1フレーム弱ですね。まあlerpColorを毎フレーム4000回も呼び出すことは無いと思うので、あんま気にならないかもですね。lerpColor自体がそれなりに重い処理なので、それがさらに重くなった感じですかね。
おわりに
利点しか伝えないのでは片手落ちだと思います。なのでパフォーマンスについて触れることにしました。もっともほとんど気にならないと思うので、安心して使ったらいいと思います。
自分が機能実装した時、1.6のベジエ曲線に色を付ける話の時ですが、ベンチマークについて釘を刺されたのを覚えています。なのでパフォーマンスを計測してプレゼンの材料に使いました。その結果実装してもらえました。本来仕様変更はそのくらい慎重にやるものですが、何があったんでしょうか。
ここまでお読みいただいてありがとうございました。
色についての補足
色について詳細な議論がなされたようです。
ざっくり言うと、現在の(~1.11.5という意味)色オブジェクトは表現力に問題があり、CSSの色を十二分に表現できないからダメ、改善が必要、ということだそうです。で、colorModeにも何らかの変更が必要、これもちょっと問題がある、とのことです。で、破壊的な変更になるので2.0以降が望ましい、とのことで2.0以降の色の仕様を変えることが決まって、詳細な議論が展開されています。しかし画像がほとんど出てこないのできちんと読まないと何が問題なのかわからなさそうです。
そもそも1.11.5までのp5の色の仕様の何がそんなに問題なのかがピンと来ないですが...
ここでやってるベンチマークにしても、これは自分の富士通のノートパソコンで ここに上げたコードでそのまま 実験した結果を上げているだけです。このやり方でいいかどうかも分かんないですし、正式リリースまでに速度が改善されるかもしれません。未知の部分が多いです。来年にならなければ、すべてが明らかになることはないでしょう。
先のissueにも「パフォーマンスが凄いから採用しよう」みたいな内容が出てくるので、何か勘違いをしている可能性もあります。慎重になるべきだと思います。
さらなる追記
2.0のパフォーマンスがおかしいという話は一応、議論されたようです。
そして改善されたことになっています。
それでこの速度なので、諦めるしかなさそうです。1.11.5とも仲良くやっていきましょう。以上です。
色
labやoklch形式が使えるようになりました。それが遠因となってfillが遅くなったようなんです、が、なぜかレンダリングは全体的にわずかながら遅くなっているので、問題はそれほど単純な話では無いようです。ハッキングして色々やったんですが、なぜか一番重いはずのカラーオブジェクトの構築処理を省いても遅かったので、全体の機構に問題が...?詳しくないので正直分かりません。偉い人たちに、おまかせするしかないです。