Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"pixel-density" CSS property #74

Open
Awendel opened this issue Oct 19, 2022 · 1 comment
Open

"pixel-density" CSS property #74

Awendel opened this issue Oct 19, 2022 · 1 comment

Comments

@Awendel
Copy link

Awendel commented Oct 19, 2022

css-pixel-density

Proposal for a new CSS property "pixel-density".

Link hosted repository: https://github.com/Awendel/css-pixel-density
Link to CSS Drafts issue: w3c/csswg-drafts#7848

Abstract

Offer a way for a given layer to adjust its internal resolution by a multiplying factor. The default should be "1" or "100%", which is the resolution that is currently chosen.

If I set the value to 0.5 or 50% it should half the internal resolution of the layer (and hence put less pressure on the rasterizer hardware and gpu memory etc).

.layer{
  pixel-density : 0.5; /* halfs the resolution */
}

.zoomIn{
  pixel-density : 200%; /* doubles the resolution in preparation for zoom in */
  will-change : transform;
}

Background

Every year devices become higher and higher resolution (especially smartphones, most have very high dpi displays).
At the same time the scope and scale of what web apps can render (think Figma, data visualisation, ...) becomes larger and larger.
Moreover, the amount web users increases every year with largest growth coming from developing countries where sadly the majority have access to only low end hardware.

Utility

  • make graphics intensive animation run smooth (60fps) even on low end devices, by temporarily lowering the resolution for the duration of the animation
  • offer way for low end devices to use graphics intensive application with useable framerates
    ( similar to how common it is to lower the resolution in gaming, especially on 4K displays etc)
  • with 6k and 8k displays slowly entering the mainstream (and thereby further quadrupling the resolution compared to 4k), this will become all but neccessary to display graphics intense content at useable framerates

Example File

( see Live Demo )

Problematic File - this file is a great example of where the property could be applied to improve user and developer experience.
This renders some procedural graphics intensive content and starts a "zooming out" animation.
It uses transform scale and will-change:transform in order to rasterise the content and "make it smooth"

Chrome and Firefox both handle it in slightly different, but both insufficient ways:

Chrome respects the will-change:transform property which leads to the animation running smooth.
Yet, it locks the resolution at the start resolution which leads to a GPU memory explosion and henceforth it creates visual artifacts and doesn't render a lot of the content when zoomed out (since it runs out of GPU memory very quick).

Firefox seems to ignore the will-change:transform property. This leads to it not having a GPU overflow, it shows all content but because it re-rasterises on every frame, it has a very low framerate (15fps or worse).

This "zooming" out effect would be a very common scenario in something like Figma / Design tool, Maps or Whiteboarding tool. The only way to achieve running this smooth in a 4k display, would be to lower the resolution during the "zooming" out. Ideally also in conjunction with locking the raster resolution (using will-change: transform)

Compatibility & Easy of Integration

The proposed property would integrate very well with the following existing properties:

  • transform, esp. scale
  • will-change, especially when set to "transform". Will-change "locks" the internal resolution of a layer already, but it doesn't provide a way to lower it, especialling when "zooming" out. It is currently not enough to ensure every animation runs smooth. I will attach some test cases to prove this and show the only way to run it smooth would be to lower the resolution.
    Moreover, it would be very simple to integrate into the existing render logic of browsers. Since there already is a mechanism to calculate the layer resolution (multiplying screen resolution by devicePixelRation and transform:scale), it would be just another multiplier in that computation.
  • new Houdini Painting API. This API allows developers to define custom "fills" of surfaces, to emulate something like a shader. The API is very similar to the Canvas2d API. But unfortunately, in contrast to the actual canvas API one is not able to set the internal resolution of the "canvas" to something other than the devicePixelRation. "pixel-density" henceforth would be great way to offer that ability to control the resolution.

Lack of alternatives

The only way to achieve this currently is by pushing all render logic to canvas, where one can control the resolution.

Sadly this has led to a situation where all graphics intensive application use a custom WebGL render stack (Figma, Google Maps etc), even though all rendering could be done with plain HTML.

Plain HTML is also by nature more accessible (via ARIA labels, screen readers etc, which are not available in WebGL).

And eases the barrier to entry for new developers (since currently only heavily VC funded / large companies can afford to build their own custom WebGL rendering pipeline, where one would have to reimplement the majority of browser features anyway).

Reasons why it should be added to CSS standard

  • inline with new Houdini approach of exposing low level primitives to web developers which can be combined to greatly customise render experience
  • simplicity + intuitiveness of API
  • simplicity of integration into existing browser rendering pipelines (devicePixelRatio etc)
  • addresses 2 main concerns for web developers who do animations:
    (1) animations not running smooth (fix via lowering / downSampling resolution)
    (2) animation not being sharp (fix via increasing / upSampling resolution)
  • making web more accessible by allowing lower end devices to render graphics intensive content
  • making web more accessible because now plain HTML can be used without having to implement this in custom WebGL / Canvas rendering stack
  • has potential to reduce loading time of HTML pages when a lot of content is rendered, by lowering the resolution before content is rendered, hence taking pressure of UI thread, it will appear faster on the screen, then one could increase resolution async in order to sharpen it again but not blocking anything

Edge Cases

Combination with transform : scale

pixel-density is not an absolute property. Instead it scales relative to what the browser determines to be the correct rasterisation scale for a given layer.
Hence values won't have to be manually calculated when using transform:scale (e.g. multiplying), but instead can still be used relatively.

Combination with will-change : transform

will-change transform is meant to "lock in" a given rasterisation scale based on when the property was set.

This is currently not respected by all browsers. Only Chrome does so reliably. Ideally we'd get all browser vendors to treat will-change: transform as a "rasterization-lock". When adding pixel-density we're able to modify that behaviour in 2 different ways:

  • scale the raster resolution when setting will-change:transform, either increasing it (e..g zoom in expected) or decreasing it (zoom out expected)
  • change the rasterization scale during an ongoing will-change transform animation. By changing pixel-density we force a recalculation of the raster resolution, even if it has been previously "locked in" via will-change transform. This might be neccessary, when zooming out by a lot, which could lead to gpu memory overflow, if the resolution is not adjusted in time

Nested pixel-density

There is 2 possible approaches to this:

  • multiply nested values, similar how it's done with transform:scale
  • treat each value as independent of its possible parent values. This is the preferrable approach, since it leads to less ambiguity and fewer calculations. It is rare that a nested value would be neccessary, but they should be treated relatively to what the browser would have painted them, regardless of if a parent has a different value applied to it.

Examples

Decrease page load time

const renderTarget = document.querySelector("#page")

// substantially lower resolution to decrease GPU memory pressure and speed up shaders
renderTarget.style.pixelDensity = '0.25'

// perform expensive initial rendering, especially in a SPA

window.addEventListener("load", function(ev){

  // reset resolution to native on of screen so content becomes sharp
  // this should be quite fast and not add much block to the UI thread
  // since layout calculations have already been performed and don't need to be redone
  
  renderTarget.style.pixelDensity = '1'  

})


Developer / End-User Feedback

Strongly positive. Feedback is currently all over the place, but I will compile it here over the next few days.
There have been similar proposals in the past that led to nothing, because their proposed API was too complicated and had too many edge cases.
In general, this property would appeal more to "power-users" (e.g. Google Maps, Figma, Data visualisation developers), but would have large end-user impact because many millions of people use these "power tools" daily.

@Awendel
Copy link
Author

Awendel commented Oct 20, 2022

Developer Feedback (continuation)

see Discussion in Chromium Issue
which led to this (abandoned) proposal
Another relevant discussion of the need in another abandoned proposal at CSS Drafts
Relevant Chromium Bug that is unlikely to be solveable until this property becomes available.

The need for this property was discussed in many places and led to the development of several proposals, which all stalled, since their proposals were always too complicated and led to many (unanswered) edge cases /lack of consensus.

The only property that emerged from these discussions was will-change, which allows developers to lock in the rasterization scale. For the aforementioned reasons, this property alone is not enough to deal with all the performance related challenges, especially in a world of high DPI devices.

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

No branches or pull requests

1 participant