Skip to content

Conversation

@toji
Copy link
Member

@toji toji commented Apr 22, 2021

A variant of #1314, Kai's original PR to pluralize requestAdapters(). Also serves as an alternative to #1634, in that this PR adds an isSoftware attribute to GPUAdapter. During editors discussion we felt that this approach would be more straightforward and useful than trying to provide developers with an increasingly complex combination of hints to try provoking the desired adapter configuration to be returned.

This PR also explicitly states that only a single hardware and software adapter are allowed to be returned from any call to requestAdapters() in order to avoid fingerprinting concerns for the time being, though there's an issue in the text to indicate that we should consider gating the ability to return more behind the permissions API or similar.


Preview | Diff

@toji toji requested review from kainino0x and kvark April 22, 2021 18:30
@Kangz
Copy link
Contributor

Kangz commented Apr 22, 2021

Very nice!

@kainino0x
Copy link
Contributor

Would be best to update ErrorHandling.md as in #1314, but this can wait until the PR has been agreed upon.

@litherum
Copy link
Contributor

litherum commented May 6, 2021

Part 1: Fingerprinting

(Most of this section is actually about #1314, but that's closed, so I'll post it here instead.)

Using the GPU for fingerprinting is very real, and very common. https://jonatron.github.io/webgl-fingerprinting/ is quite insightful. The W3C Technical Architecture Group presented a finding about why unsanctioned tracking is harmful. The TAG has has a design principle regarding device enumeration.

A primary way fingerprinting here can be limited is a two-pronged approach:

  1. Limiting the number of adapters exposed by the implementation
  2. Limiting entropy of each adapter - e.g. bucketing multiple similar devices together and making them appear identical (https://github.com/gpuweb/gpuweb/blob/main/explainer/index.bs#L1144)

An API which returns all the devices fails item 1 above. It would also encourage applications to iterate through the entire list, interrogating each device, in order to pick the device it wants to run on. The problem here is that this calling pattern is exactly the same calling pattern a fingerprinter would use. We wouldn't be able to differentiate the good guys from the bad guys.

Even if an implementation wanted to somehow inject an artificial limit into such a design (possibly by subclassing Array in Javascript and having the subclass dynamically produce adapters), that implementation wouldn't be able to, because of the the calling pattern the app uses. Fundamentally, the application developer would think they're not operating under a limit, so the developer would have no incentive to try to limit the number of adapters they interrogate.

An API where the implementation returns multiple adapters at the same time loses priority information about which criterion the application cares most about, and which criteria it cares less about. Requiring a back-and-forth conversation, instead of giving away the keys to the castle with a single call, is a principle of good privacy design: websites should only get access to the information they need, and no more.

Part 2: This proposal

Part 2a: The future

The text of this proposal seems to attempt to resolve the problems described above. However, the spirit of this proposal still violates the above principles. This proposal still returns a list of multiple adapters, but adds this text:

{{GPU/requestAdapters()}} must return no more than one hardware adapter and no more than one software adapter

By returning a list of devices, this proposal still encourages web authors to iterate over the list. By adding a new isSoftware property on the adapter, and not as part of an argument to requestAdapter(), this proposal is specifically requiring that applications with preferences about software/hardware interrogate adapters.

This is a step in the wrong direction. Not only that, but this proposal specifically specifically mentions this wrong direction as its intended path forward:

we should consider gating the ability to return more [adapters]

As above, this proposal is encouraging authors to iterate, because the return type is a list, and interrogate each device, because that's how they can check if it's a software device or not. We are not convinced that a single line in the specification will be enough to educate authors that they are, in fact, operating under limits, and should try to restrict the devices they ask for. Also, as above, returning multiple adapters with a single call loses priority information - information that would be present in a back-and-forth conversation with the app.

We are also worried that returning a list makes it easier for browsers to willfully violate the specification and return all adapters. If the requirement is simply a line of prose in the spec, browsers can choose to ignore it, without breaking compatibility with existing Javascript content. However, if the requirement is baked into the type of the returned object, that is a contract which cannot be broken without also breaking compatibility with existing content. If the dominant browser does indeed willfully violate the specification and return all devices, content will be written that performs the problematic access pattern described above. And, because of this, non-dominant browsers would be forced to match their behavior, in order to maintain compatibility of the web.

Part 2b: The present

Beyond these future-looking fears, we have concerns about this proposal, even if it was never amended or violated. Philosophically, a list is the wrong data structure to return a hardware adapter and a software adapter. It would be too easy to write code like:

var adapters = GPU.requestAdapters(...);
return adapters[1]; // Index 1 is the software adapter

which is incorrect if the machine only has a single software adapter.

A natural improvement would be to split requestAdapters() into a requestSoftwareAdapter() and a requestHardwareAdapter() so authors know what they're getting by name, and it's obvious what it means if one of them returns null.

But even that isn't sufficient, because it doesn't make sense for the distinction between hardware/software to use a different mechanism for low-power/high-performance. These axes are (philosophically) independent - it would be reasonable to expose a high-power software implementation that uses the P-cores of the CPU instead of the E-cores. Keeping these mechanisms orthogonal means that, for every possible value of GPURequestAdapterOptions, the browser would have to figure out if there's a hardware adapter for those options and if there's a software adapter for those options. So treating hardware/software as just one more criterion inside the GPUReqeustAdapterOptions makes sense; and it would be silly to have requestHighPowerHardwareAdapter() because of the exponential blowup. So, that leads us to...

Part 3: Another proposal

On 4-12-2021 and confirmed again on 4-19-2021, the Community Group resolved to go in the direction of an allowSoftware field on the GPURequestAdapterOptions. This solves all of the above concerns, and would be a great path forward for supporting software adapters.

@litherum
Copy link
Contributor

litherum commented May 6, 2021

this approach would be more straightforward and useful

increasingly complex combination of hints

@toji @kainino0x I don't quite understand these claims. Do you think you could explain them? Also, if you believe allowSoftware is insufficient, could you explain why?

@toji
Copy link
Member Author

toji commented May 10, 2021

@toji @kainino0x I don't quite understand these claims. Do you think you could explain them?

We can generally assume that the number of options that affect the returned adapter will grow over time. Some concrete examples are the potential for a xrCompatible option to match the WebGL context behavior with WebXR devices, and possibly a computeOnly option to enable use of hardware that does have compute capabilities but not rendering. (We don't have an example of such hardware at this point, but it's been discussed within the group previously.)

One of the concerns, if the options are stated in terms of "allowing" the given behavior or merely affecting it's priority is that it gets pretty confusing fairly quickly about what takes precedence when. For example: If I allow software devices and I allow compute only devices, what floats to the top? Does software take priority or compute? Or a device that's both? We can definitely define it in an algorithm, but it's difficult to get an intuitive sense for the precedent simply from the dictionary args.

Also, if you believe allowSoftware is insufficient, could you explain why?

Following from the previous point, the phrasing of allowSoftware is a bit ambiguous as to how it affects the adapters that are returned. Additionally, it seems to imply that you won't get a software adapter unless requested, which goes against the desire that developers have to intentionally opt out of receiving a software device in order to offer the broadest compatibility by default.

Kai's PR, which you linked, attempted to address this by having it be an enum stated in various negative terms ("last-resort", "never") but this goes against some of the API guidelines I've heard from the TAG in the past (which is that option defaults should be falsey), makes it difficult for developers who specifically want a software adapter to get what they're looking for, and still doesn't completely resolve the ambiguity of what's returned. The plural adapter approach was intended to resolve this by letting the developer set their own criteria for adapter selections rather than trying to find away to align our specced preferences with their intended ones.

An alternate approach that we could take to resolving those concerns is reframing allowSoftware as forceSoftware (a boolean, default false) and adding an isSoftware boolean attribute to the adapter itself. This would achieve a few things:

  • Software adapters can always be returned. Developers that wish to opt out need to check the attribute and terminate their initialization path if it's true. This ensures it's intentional and not easy to accidentally inherit from code snippet copying.
  • It allows developers who want to explicitly avoid a known bad adapter (due to driver bugs, etc) to unambiguously state that they only want the (presumably more reliable) software adapter, which was one of the use cases that we were aware of as a group.
  • forceSoftware is unambiguous, even when mixed with other arguments. If you set it to true you always get either a software adapter or nothing (if software isn't supported.) Projecting this forward with a potential forceComputeOnly options you can see how only adapters that match all of the specified force* options may be returned. If you specify forceSoftware: true, forceComputeOnly: true it's easy to guess that you should only ever get back a software adapter that does nothing but compute or null.

@litherum
Copy link
Contributor

litherum commented May 17, 2021

developers who specifically want a software adapter

I was under the impression the WebGPU CG was proceeding under the assumption that this set of developers was so small that we shouldn't be designing an API around them. If developers don't want to use a GPU, writing code that uses the WebGPU API seems counterintuitive.

Edit: Oh, I think I see the distinction. The set of developers who primarily want a software adapter to the exclusion of all hardware adapters is the small set. However, requesting a software adapter after you've discovered that the hardware adapter cannot be used for your purposes for some reason is more reasonable.

reframing allowSoftware as forceSoftware

This sounds fine to us.

Software adapters can always be returned.

I don't think this is a dealbreaker, but I want to be clear that the Mac and iOS ports of WebKit don't plan on implementing software adapters at all. So we would just return null.

@litherum
Copy link
Contributor

litherum commented May 17, 2021

For what it's worth, the CSS Fonts spec has a similar conundrum when looking up a desired font from a family. The request can say something like font-weight: bold; font-style: italic; but the set of faces present in the family is an arbitrary sparse set - you might have bold and italic but not bold italic.

The way the CSS Fonts spec solves this is that there is a priority order for properties within the request: First, you look for the font with the closest font-stretch, and if there is only one, you return it immediately. If multiple fonts share the same font-stretch value, then you look for the font with the closest font-style, then font-weight, etc.

https://drafts.csswg.org/css-fonts-4/#font-style-matching

@toji
Copy link
Member Author

toji commented May 17, 2021

Closed in favor of #1734 after today's discussion.

@kainino0x kainino0x deleted the software-adapters branch July 14, 2022 20:18
ben-clayton pushed a commit to ben-clayton/gpuweb that referenced this pull request Sep 6, 2022
…ogether (gpuweb#1660)

The `resolveTarget,usage` test has only tested an invalid case
(COPY_SRC | COPY_DST). So this PR adds more cases to check if
the resolveTarget usage is valid.

Issue: gpuweb#1618
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants