Oh, in case it wasn't clear: the objects are exclusive write - there can be no readers while there are writers, and vice versa.
I'm not sure how you aren't seeing how immutable resources are bad. Almost all of the cases I (and many others) want WebGL in workers for require mutable resources. Resources being immutable makes them almost useless, except to offload what exists as single-threaded rendering today to a worker (which just adds additional latency and not much else).On Wed, Jul 18, 2012 at 1:37 PM, David Sheets <[email protected]> wrote:
On Wed, Jul 18, 2012 at 1:15 PM, Ben Vanik <[email protected]> wrote:Except for subrange mutation, I do not see how these use cases benefit
> All of them ;)
> Almost every use case of WebGL on workers needs mutable resources.
>
> The primary ones I've used in the past (and would love to use in WebGL with
> workers) is modifying buffer data in various worker threads. You always want
> to pool the buffers and double (or triple) buffer them when rendering. They
> may be populated on one worker and drawn on another (or the main thread).
> Same with textures in the case of either a render-to-texture done on a
> worker or a plain old upload, or the reverse of reading back the framebuffer
> on another worker for JS processing.
from shared mutable resources over immutable resources.
Immutable resources solves this problem.
> These cases would be possible with ownership transfer, and transferable
> arrays has shown that it's an easy concept to wrap ones head around and
> works well.
>
> One thing that would suffer, though, is the case of multiple readers. I may
> want to do some readback of a texture for GPGPU data, and could imagine
> sending that texture to multiple workers - each worker would then readPixels
> a separate region of the texture.
Immutable but shared resources solves this problem without using Nx GPU memory.
> The other thing that suffers with single ownership is reusing shared
> resources (shaders/buffers/textures) - I can imagine (and want) many cases
> where I'd have multiple workers rendering the same scene in different ways -
> whether it's a visualization tool or a game that does things like GPGPU
> occlusion culling in one worker and real rendering in another. Or
> megatexture processing using the same geometry buffers in one worker,
> readback of the framebuffer for processing in another, texture uploads in
> yet another, and final scene rendering on the main thread. If I had to
> duplicate all of my level geometry, rerun my CPU skinning, process my
> particle systems, and consume Nx as much GPU memory for each worker I'd be
> disappointed - especially on embedded devices.
What happens when I have 4 reader workers and 1 writer worker that
> Hmm... I feel like an acquisition model probably makes the most sense for
> these cases, although it's a bit more complex...
> - pass webgl resources to other workers like transferrable arrays (as
> additional args to postMessage)
> - a resource is not usable once received until acquired
> - some WebGLRenderingContext (or extension methods) to work with the
> objects:
> - acquireResource(obj, mode [READ_ONLY, READ_WRITE]) --> returns whether
> acquired
> - releaseResource(obj)
> - if you try to read or write an unacquired object, fail at API level
> - if you try to write to a read only object, fail at API level
> - multiple readers are allowed, only one writer
have all acquired the resource and then the writer mutates the
resource? Does it change underneath the readers?
To be clear, I am proposing: (1 read-write context, no sharing) -> (0
read-write contexts, arbitrary sharing)
> This allows multiple workers to render the same objects (all acquire as read
> only), removes the required implicit flush, and substitutes an explicit
> flush (an acquire for reading of a dirty object could flush, if that was
> tracked). Performance-wise most apps would likely acquire as read only once
> and never have to worry about it again (no synchronization across workers
> required), It's also not magic - meaning that it may require a few more
> calls, but it's very clear what's happening and why, and it's not possible
> to fall into some undiscoverable (or unavoidable) performance trap with
> implicit flushes.
>
>
> On Wed, Jul 18, 2012 at 12:52 PM, David Sheets <[email protected]> wrote:
>>
>> On Mon, Jul 16, 2012 at 6:03 PM, Gregg Tavares (社用) <[email protected]>
>> wrote:
>> > I agree that solution would work for 2+ contexts in a single webpage
>> > since
>> > webpages are single threaded. Is that enough for now? Do we care about
>> > WebWorkers where that solution will no longer be enough?
>> >
>> > Lemme throw out some ideas. They probably suck but maybe they'll jog
>> > some
>> > others.
>> >
>> > What if a resource could only be used in 1 context at a time and you had
>> > to
>> > transfer ownership to use it another context? That might solve this
>> > issue as
>> > well. Would that be too restrictive? What if an object could be used in
>> > any
>> > context but state only set in the context that "owns" it? Not sure that
>> > would help. In the case above it would mean any context could call
>> > UseProgram but only the context that owns it could call AttachShaders
>> > and
>> > LinkProgram since those change state. Of course that wouldn't solve
>> > every
>> > issue because UseProgram requires the results of LinkProgram.
>> >
>> > I'm just throwing that out there. The idea is probably not workable but
>> > it
>> > would be nice to try to design an WebGL API for WebWorkers that didn't
>> > have
>> > the ambiguity that OpenGL has as far as execution order. Whatever that
>> > solution is might also work for 2 contexts in the same page.
>>
>> What if a means is provided to make a given WebGL resource immutable?
>> Once the resource is 'frozen', no further modification would be
>> allowed but the resource could be shared between contexts or workers.
>>
>> What are the use cases for shared mutable WebGL resources?
>>
>> David
>>
>> > On Mon, Jul 16, 2012 at 5:36 PM, Ben Vanik <[email protected]> wrote:
>> >>
>> >> In previous systems I've worked with I've used implicit flushes when
>> >> swapping contexts and it made life much easier. Every call in the
>> >> graphics
>> >> layer basically had this:
>> >> void MyGraphicsAPI::SomeCall() {
>> >> MakeCurrent();
>> >> ...
>> >> };
>> >> void MyGraphicsAPI::MakeCurrent() {
>> >> if (threadCurrentContext != this) {
>> >> Flush();
>> >> }
>> >> // ... set global threadCurrentContext, etc
>> >> };
>> >>
>> >> Using some macro-fu it was made to be a single variable lookup and
>> >> branch
>> >> that predicted well per call, and in the common case of single-context
>> >> apps
>> >> had little impact on performance. In apps with multiple contexts it
>> >> made
>> >> life much easier when dealing with the sometimes long and hairy code
>> >> sequences that touched both (rare, but often unavoidable). Had the
>> >> flush not
>> >> been implicit it would have required a significant amount of
>> >> bookkeeping
>> >> logic that every piece of code that touched a context would have to
>> >> interact
>> >> with - yuck. It also meant that code could be moved between apps that
>> >> used
>> >> single contexts and multiple contexts without having to change, or the
>> >> app
>> >> could even decide at runtime with no ill effect. Explicit flushes would
>> >> have
>> >> made that a nightmare.
>> >>
>> >> The one downside of the implicit flushes is that it's easy to start
>> >> flushing all over the place without knowing about it. A simple counter
>> >> of
>> >> flushes/frame was enough to help warn that they were happening though,
>> >> as
>> >> the target was usually <5 and if you mess things up you would see
>> >> dozens.
>> >> It's also often much trickier to find missing explicit flushes that
>> >> cause
>> >> subtle and sometimes very hard to identify behavioral differences. Just
>> >> look
>> >> at the number of WebGL pages out there today that emit warnings and
>> >> imagine
>> >> what it'd be like with this x_x
>> >>
>> >>
>> >> On Mon, Jul 16, 2012 at 5:18 PM, Gregg Tavares (社用) <[email protected]>
>> >> wrote:
>> >>>
>> >>> So ........... I've been playing around with sharing resources across
>> >>> WebGL contexts and I'm running into issues and looking for solutions.
>> >>>
>> >>> The biggest issue is that GL is command buffer driven and so calling
>> >>> some
>> >>> GL function doesn't mean it's actually been executed. You have to call
>> >>> glFlush. This raises lots of issues of where a program might work on
>> >>> some
>> >>> browser / driver / platform combo but not others if the users forgets
>> >>> to
>> >>> call flush.
>> >>>
>> >>> For example, assume I have 2 contexts sharing resources. gl1, and gl2
>> >>>
>> >>>
>> >>> var vs = gl1.createShader(gl1.VERTEX_SHADER);
>> >>> var fs = gl1.createShader(gl1.FRAGMENT_SHADER);
>> >>> //...
>> >>> // assume shaders are validly compiled
>> >>> // ...
>> >>> var p = gl1.createProgram();
>> >>> gl1.attachShader(p, vs);
>> >>> gl1.attachShader(p, fs);
>> >>> gl2.linkProgram(p);
>> >>>
>> >>> I attached on gl1 but linked on gl2. There's is no guarantee in GL
>> >>> that
>> >>> that link will succeed because the 2 attach commands may have only
>> >>> been
>> >>> queued and not excuted in which case the linkProgram call will fail
>> >>> with
>> >>> "missing shaders". The correct code is
>> >>>
>> >>> var p = gl1.createProgram();
>> >>> gl1.attachShader(p, vs);
>> >>> gl1.attachShader(p, fs);
>> >>> gl1.flush(); // <--- important
>> >>> gl2.linkProgram(p);
>> >>>
>> >>> That seems unacceptable.
>> >>>
>> >>> 2 Approaches off the of my head
>> >>>
>> >>> 1) Try to make it just work without the flush
>> >>>
>> >>> One solution might be for the implementation to track which context
>> >>> last
>> >>> had a call. Any call to a different context causes an automatic
>> >>> implicit
>> >>> flush
>> >>>
>> >>> 2) Try to make it fail when not done correctly
>> >>>
>> >>> This solution would be to try to track of an object is "dirty" (state
>> >>> has
>> >>> been changed) and no flush has been issued since then for that object.
>> >>> When
>> >>> an object is used, if it's dirty then either a flush is issued (which
>> >>> is
>> >>> solution #1) or a error is generated. ("called function on dirty
>> >>> object, did
>> >>> you forget to call flush?")
>> >>>
>> >>> Neither solution seems ideal. Worse, whatever solution is chosen also
>> >>> has
>> >>> issues if we ever get WebGL in Workers.
>> >>>
>> >>> A 3rd solution is just to leave it with the flush required and
>> >>> forgetting
>> >>> means you get random success / failure. No such a great prospect. :-(
>> >>>
>> >>> Thoughts?
>> >>>
>> >>>
>> >>>
>> >>>
>> >>>
>> >>>
>> >>>
>> >>>
>> >>>
>> >>>
>> >>
>> >
>
>