@@ -12,9 +12,18 @@ defmodule Scenic.Cache.Base do
1212
1313 | Asset Class | Module |
1414 | ------------- | -----|
15- | Fonts | [Scenic.Cache.Font](Scenic.Cache.Font.html) |
16- | Font Metrics | [Scenic.Cache.FontMetrics](Scenic.Cache.FontMetrics.html) |
17- | Textures (images in a fill) | [Scenic.Cache.Texture](Scenic.Cache.Texture.html) |
15+ | Fonts | `Scenic.Cache.Static.Font` |
16+ | Font Metrics | `Scenic.Cache.Static.FontMetrics` |
17+ | Textures (images in a fill) | `Scenic.Cache.Static.Texture` |
18+ | Raw Pixel Maps | `Scenic.Cache.Dynamic.Texture` |
19+
20+ Some of the Cache support modules have moved
21+
22+ | Old Module | New Module |
23+ | ------------- | -----|
24+ | `Scenic.Cache.Hash` | `Scenic.Cache.Support.Hash` |
25+ | `Scenic.Cache.File` | `Scenic.Cache.Support.File` |
26+ | `Scenic.Cache.Supervisor` | `Scenic.Cache.Support.Supervisor` |
1827
1928 ## Overview
2029
@@ -182,129 +191,6 @@ defmodule Scenic.Cache.Base do
182191 @ behaviour Scenic.Cache.Base
183192 end
184193
185- @ moduledoc """
186- In memory cache for static #{ unquote ( using_opts ) [ :name ] } assets.
187-
188- Assets such as #{ unquote ( using_opts ) [ :name ] } tend to be relatively large compared to
189- other data. These assets are often used across multiple scenes and may need to be shared
190- with multiple drivers.
191-
192- These assets also tend to have a significant load cost. Fonts need to be rendered. Images
193- interpreted into their final binary form, etc.
194-
195- ## Goals
196-
197- Given this situation, the Cache module has multiple goals.
198- * __Reuse__ - assets used by multiple scenes should only be stored in memory once
199- * __Load Time__- loading cost should only be paid once
200- * __Copy time__ - assets are stored in ETS, so they don't need to be copied as they are used
201- * __Pub/Sub__ - Consumers of static assets (drivers...) should be notified when an asset is
202- loaded or changed. They should not poll the system.
203- * __Security__ - Base assets can become an attack vector. Helper modules are provided
204- to assist in verifying these files.
205-
206- ## Scope
207-
208- When a #{ unquote ( using_opts ) [ :name ] } is loaded into the cache, it is assigned a scope.
209- The scope is used to
210- determine how long to hold the asset in memory before it is unloaded. Scope is either
211- the atom `:global`, or a `pid`.
212-
213- The typical flow is that a scene will load a #{ unquote ( using_opts ) [ :name ] } into the cache.
214- A scope is automatically
215- defined that tracks the asset against the pid of the scene that loaded it. When the scene
216- is closed, the scope becomes empty and the asset is unloaded.
217-
218- If, while that scene is loaded, another scene (or any process...) attempts to load
219- the same asset into the cache, a second scope is added and the duplicate load is
220- skipped. When the first scene closes, the asset stays in memory as long as the second
221- scope remains valid.
222-
223- When a scene closes, it's scope stays valid for a short time in order to give the next
224- scene a chance to load its assets (or claim a scope) and possibly re-use the already
225- loaded assets.
226-
227- This is also useful in the event of a scene crashing and being restarted. The delay
228- in unloading the scope means that the replacement scene will use already loaded
229- assets instead of loading the same files again for no real benefit.
230-
231- When you load assets you can alternately provide your own scope instead of taking the
232- default, which is your processes pid. If you provide `:global`, then the asset will
233- stay in memory until you explicitly release it.
234-
235- ## Hashes
236-
237- At its simplest, accessing the cache is a key-value store. This cache is meant to be
238- static in nature, so the key is be a hash of the data.
239-
240- Why? Read below...
241-
242- ## Security
243-
244- A lesson learned the hard way is that static assets (fonts, images, etc) that your app
245- loads out of storage can easily become attack vectors.
246-
247- These formats are complicated! There is no guarantee (on any system) that a malformed
248- asset will not cause an error in the C code that interprets it. Again - these are complicated
249- and the renderers need to be fast...
250-
251- The solution is to compute a SHA hash of these files during build-time of your
252- and to store the result in your applications code itself. Then during run time, you
253- compare then pre-computed hash against the run-time of the asset being loaded.
254-
255- Please take advantage of the helper modules [`Cache.File`](Scenic.Cache.File.html),
256- [`Cache.Term`](Scenic.Cache.Term.html), and [`Cache.Hash`](Scenic.Cache.Hash.html) to
257- do this for you. These modules load files and insert them into the cache while checking
258- a precomputed hash.
259-
260- These scheme is much stronger when the application code itself is also signed and
261- verified, but that is an exercise for the packaging tools.
262-
263- Full Example:
264-
265- defmodule MyApp.MyScene do
266- use Scenic.Scene
267- import Scenic.Primitives
268-
269- # build the path to the static asset file (compile time)
270- @asset_path :code.priv_dir(:my_app) |> Path.join("/static/images/asset.jpg")
271-
272- # pre-compute the hash (compile time)
273- @asset_hash Scenic.Cache.Hash.file!( @asset_path, :sha )
274-
275- # build a graph that uses the asset (compile time)
276- @graph Scenic.Graph.build()
277- |> rect( {100, 100}, fill: {:image, @asset_hash} )
278-
279-
280- def init( _, _ ) do
281- # load the asset into the cache (run time)
282- Scenic.Cache.File.load(@asset_path, @asset_hash)
283-
284- {:ok, :some_state, push: @graph}
285- end
286-
287- end
288-
289- When assets are loaded this way, the `@asset_hash` term is also used as the key in
290- the cache. This has the additional benefit of allowing you to pre-compute
291- the graph itself, using the correct keys for the correct assets.
292-
293- ## Pub/Sub
294-
295- Drivers (or any process...) listen to the #{ unquote ( using_opts ) [ :name ] }
296- Cache via a simple pub/sub api.
297-
298- Because the graph, may be computed during compile time and pushed at some
299- other time than the assets are loaded, the drivers need to know when the assets
300- become available.
301-
302- Whenever any asset is loaded into the cache, messages are sent to any
303- subscribing processes along with the affected keys. This allows them to react in a
304- loosely-coupled way to how the assets are managed in your scene.
305-
306- """
307-
308194 unless unquote ( using_opts ) [ :name ] do
309195 raise "You must supply a :name option to the \" use Scenic.Cache.Base\" macro."
310196 end
@@ -373,8 +259,7 @@ defmodule Scenic.Cache.Base do
373259 * `scope` - Optional scope to track the lifetime of this asset against. Can be `:global`
374260 but is usually nil, which defaults to the pid of the calling process.
375261
376- Returns:
377- {:ok, hash}
262+ Returns: `{:ok, hash}`
378263 """
379264 @ spec put (
380265 hash :: Scenic.Cache.Base . hash ( ) ,
@@ -400,8 +285,7 @@ defmodule Scenic.Cache.Base do
400285 * `scope` - Optional scope to track the lifetime of this asset against. Can be `:global`
401286 but is usually nil, which defaults to the pid of the calling process.
402287
403- Returns:
404- {:ok, hash}
288+ Returns: `{:ok, hash}`
405289 """
406290 @ spec put_new (
407291 hash :: Scenic.Cache.Base . hash ( ) ,
@@ -692,46 +576,6 @@ defmodule Scenic.Cache.Base do
692576 GenServer . call ( service , { :put , normalize_scope ( scope ) , key , data } )
693577 end
694578
695- # # --------------------------------------------------------
696- # @doc """
697- # Insert an item into the Cache. If it is already in the cache, then it
698- # does nothing and returns {:ok, hash}
699-
700- # Parameters:
701- # * `key` - term to use as the retrieval key. Typically a hash of the data itself.
702- # * `data` - term to use as the stored data
703- # * `scope` - Optional scope to track the lifetime of this asset against. Can be `:global`
704- # but is usually nil, which defaults to the pid of the calling process.
705-
706- # ## Examples
707- # iex> Scenic.Cache.get("test_key")
708- # nil
709-
710- # iex> :ets.insert(:scenic_cache_key_table, {"test_key", 1, :test_data})
711- # ...> true
712- # ...> Scenic.Cache.get("test_key")
713- # :test_data
714- # """
715- # @spec put_new(
716- # service :: atom,
717- # hash :: Scenic.Cache.Base.hash(),
718- # data :: term(),
719- # scope :: :global | nil | GenServer.server()
720- # ) :: term()
721-
722- # def put_new(service, hash, data, scope \\ nil)
723- # when service != nil and (is_atom(service) or is_pid(service)) do
724- # scope = normalize_scope(scope)
725-
726- # case :ets.member(service, hash) do
727- # true ->
728- # {:ok, hash}
729-
730- # false ->
731- # GenServer.call(service, {:put_new, scope, hash, data})
732- # end
733- # end
734-
735579 # --------------------------------------------------------
736580 @ doc """
737581 Add a scope to an existing asset in the cache.
@@ -776,7 +620,7 @@ defmodule Scenic.Cache.Base do
776620 processes a chance to claim a scope before it is unloaded.
777621 """
778622
779- # returns :ok
623+ # returns ` :ok`
780624 @ spec release (
781625 service :: atom ,
782626 hash :: Scenic.Cache.Base . hash ( ) ,
@@ -815,9 +659,11 @@ defmodule Scenic.Cache.Base do
815659 Pass in the service, hash, and a scope.
816660
817661 returns one of:
818- {:ok, hash} # it is claimed by the given scope
819- {:ok, :global} # it is NOT claimed by the given scope, but is :global
820- {:error, :not_found} # it is not in the cache at all
662+ ```elixir
663+ {:ok, hash} # it is claimed by the given scope
664+ {:ok, :global} # it is NOT claimed by the given scope, but is :global
665+ {:error, :not_found} # it is not in the cache at all
666+ ```
821667 """
822668 @ spec status (
823669 service :: atom ,
@@ -855,7 +701,7 @@ defmodule Scenic.Cache.Base do
855701
856702 Pass in the service and a hash.
857703
858- returns true or false.
704+ returns ` true` or ` false` .
859705 """
860706 @ spec member? (
861707 service :: atom ,
@@ -870,7 +716,7 @@ defmodule Scenic.Cache.Base do
870716
871717 Pass in the service, hash, and scope.
872718
873- returns true or false.
719+ returns ` true` or ` false` .
874720 """
875721 @ spec claimed? (
876722 service :: atom ,
0 commit comments