I'm rendering a somewhat complex WebGL scene here. When I view the scene on my MacBook Pro's built-in display in full screen it renders smoothly at 60 frames per second. When I view the scene on my external monitor in full screen, the frame rate drops considerably and becomes very choppy.
The native resolution of my display is 4K and it's running at 60Hz. My iGPU is an Iris Plus 640. I'm using DPI scaling and Chrome detects a resolution of 2560x1440 with pixel ratio 2. I'm setting the canvas size to 1280x720 and this is static irrespective of its presentation size. I'm using CSS to scale it up to full screen with a 16:9 aspect ratio using these rules:
canvas {
width: 100vw;
height: 56.25vw;
max-width: 177.78vh;
max-height: 100vh;
margin: auto;
position: absolute;
top: 0; bottom: 0;
left: 0; right: 0;
-ms-interpolation-mode: nearest-neighbor;
image-rendering: crisp-edges;
image-rendering: pixelated;
}
I'm setting image-rendering: pixelated because I noticed a small performance and visual improvement. My understanding is that the final step of rendering is to copy the frame buffer to the visible canvas. If the canvas is larger then it interpolates in-between pixels and this has a cost. Setting 'pixelated' is a bit like using NEAREST filtering and is therefore faster.
When I create the WebGL context I'm disabling things I don't need:
canvas.getContext('webgl', {
alpha: false,
depth: false,
stencil: false,
antialias: false,
powerPreference: 'high-performance',
});
I've followed most of the advice here. I'm using instancing with a texture atlas, I've tried to minimise state changes. Attribute 0 is enabled, etc.
I don't think the problem is in my shader since I'm rendering at a fixed size of 1280x720. Therefore, if I had too much overdraw, for example, shouldn't that also affect the performance on a smaller canvas size (like on my MacBook Pro's display)?
I'm really not sure what else to try. I've scoured the web and tried many things like using CSS transforms to scale the canvas instead of width / height. I've tried setting the zoom property. I've also spent a while looking through the performance tab in Chrome and (tried) to go through about:tracing. I've seen a few bug reports specifically about Chrome but I see the same problem in both Firefox and Safari. Have I just reached the limit of how large a WebGL canvas can be (for my GPU)?
I'm sure I've seen other WebGL demos that upscale fine to full screen so I'm really confused. What's going on?
Related
Is there any way (with CSS) to make the image to cover a div element, but only if it can be done without overriding the original dimensions on upscaling?
That to prevent pixelated image. Object-fit 'cover' upscales even too small images. And 'contain' don't push the image to cover the div.
So, is there a way to accomplish what I want?
Edit:
Seems that even 'contain' works that bad way. It upscaling the image to the parent element's edges even if that makes the image bigger than the original. That's crazy...Isn't it?
And I'm trying to see if it's possible to at least add image sizes in Wordpress by just the aspect ratio, not an exact size. I just want to change, crop to a given ratio for the image. That could at least help a bit. But no, there's no function for that. Just the image editor you can use after the upload...
Where is the responsive thinking? Isn't aspect ratio the future, not pixels?
I did a bit of research and can offer you this solution:
Image needs to be a background-image
Set a fixed width for the background-size + auto
(optional) you can center the image for aesthetics
In this example the .img-container is also flexible (20vh) and crops the image if it gets smaller then 100px. The fixed background-size value prevents the image from scaling up aswell.
created a jsFiddle to check the flexible container
.img-container {
width: 60vh;
height: 60vh;
border: 2px solid #111;
background-repeat: no-repeat;
background-position: center;
background-size: 100px auto;
background-image: url("https://mdn.mozillademos.org/files/6457/mdn_logo_only_color.png");
}
<div class="img-container"></div>
I'm currently drawing a line chart on a html5 canvas (In plan vanilla JavaScript) with a width of 1px and moving along as I draw on the x axis 2px spacing per data point. Currently my canvas size is 1000px by 300px.
My data is most of the time much larger than my canvas. I need some idea of a smart approach to zooming (or to make it seem like you are zooming) as I would like to be able to zoom and drag the view-able area around without loosing the crispness of the 1px line.
A note: The canvas could be a drawing of cat for all it matters, for the sake of a clear question if it was a cat then the cat would be much larger than the canvas and you might, as a user, be interested at looking closely at its foot and scrolling around or zooming out to see the whole cat. The real problem I see is the fact that it is a line drawing of 1px thickness.
Would it be more practical to change (increase/decrease) the x spacing and the magnitude of the the y movements when drawing? So that this way If zoomed out far, I would be drawing still with 1px thickness and still drawing on the same size canvas but moving much more fin-eight distances. This way I would have to repaint I think every time I navigate the area and if altering zoom. Also the canvas would not need to be zoomed with css.
Or would It be better to Increase the size of the canvas to a much much larger one and change the thickness of the line with each zoom? So this way when you are zoomed out the line thickness would be greater than if you were zoomed in but the distance and spacing between movements would always be the same no matter what level the zoom was. Also this way I assume I would have to repaint only when zooming the canvas element with css to change line width whereas scrolling the drawing would be fine as the whole drawing would all-ways fit into the large canvas.
I have heard that there are limitations on size and rendering on different browsers for a start and I would like to know If anyone has had any experience in dealing with large canvas drawings.
For further detail: My data points are around 70,000 long I will be increasing to 100,000 data points so the canvas would be quite big, hence my concern (it is a static chart so no worries about stalling the browser with such a large task).
What would be the most 'do-able' way and would there be a more logical approach to this task?
Please no library's.
You can keep your crisp fine lines when zooming by setting the canvas CSS size much smaller than the canvas element size.
Example:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvas1=document.getElementById("canvas1");
var ctx1=canvas1.getContext("2d");
// draw on standard canvas
ctx1.beginPath();
ctx1.moveTo(50/4,50/4);
ctx1.lineTo(250/4,250/4);
ctx1.stroke();
// draw on resolution enhanced canvas
ctx.lineWidth=4;
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(250,250);
ctx.stroke();
#canvas{border:1px solid red; width:100px; height:100px;}
#canvas1{border:1px solid blue; width:100px; height:100px;}
<h4>Left: Standard canvas, Right: Resolution enhanced</h4>
<h4>Zoom your browser to notice the difference (eg 200%)</h4>
<canvas id="canvas1" width=100 height=100></canvas>
<canvas id="canvas" width=400 height=400></canvas>
I have two divs with width of 30% and 70% and fixed height as height:100vh; Because the project needs to have the slider to be always the height of the screen you are looking at.
But I cant seem to figure out how to fix the aspect ratio of the images? As you can see in the test link that the images are narrow?
here is the link : [broken link removed]
note that this is the prototype that I am building so its still ugly as **** :)
and sorry im not too good at coding (still learning)
Your image is stretching because you've set both the width and height to 100%, so the browser is making the image width fit the div width (which is thinner than the aspect ratio of your image).
The quick fix is to amend your CSS as follows:
.cycle-slideshow img {
width: auto;
height: 100vh;
}
This tells the browser to set the height to 100% and then resize the width accordingly to keep the image the correct aspect ratio.
Although, you may want some fall back for if the browser window is much wider than it is tall, as then you'll see the edge of the image.
I have a simple canvas with a mouseover event. As the user moves the mouse, I want to draw a single pixel at the event's x,y coords (in the future it will be more complex than a single pixel). Essentially it's like a custom cursor.
The logic is extremely simple as shown below. Strangely, although I'm cleaning up the old pixel location, there're tiny remnants of the pixel left behind (since I posted this question, I have discovered it's related to the Retina display). I've been able to work around this. Instead of saving 1px and restore 1px, I save 3px and restore 3px. But I don't understand why I need to do this, and in the future when I'm drawing a more complicated cursor, I want the dirty pixel handling to be precise.
Here's a runnable JSFiddle example: http://jsfiddle.net/sbCq3/2/
// cleanup previously drawn pixel
ctx.putImageData(lastImageData, lastImageX, lastImageY);
// save the imageData currently at x,y
lastImageData = ctx.getImageData(x, y, 1, 1);
lastImageX = x;
lastImageY = y;
// draw the dot
var dotData = ctx.createImageData(1, 1);
...
ctx.putImageData(dotData, x, y);
I'm a bit stumped. I'm wondering if it has anything to do with my Retina display. If I draw a single pixel at 5,5, it's a single pixel in the image data - but I can zoom using the DigitalColor Meter (built in zoom tool) and see that single pixel is sub-divided and anti-aliased. Whereas if I view that pixel on a Windows machine it's a nice solid pixel. I haven't tested to see if this problem appears on Windows or non-retina machines yet. (I'm not referring to the normal canvas anti-aliasing problem).
UPDATE: I just tested this on my coworkers non-retina MacBook 17" and it works perfectly fine. So this definitely appears to be related to the Retina display.
I'm not entirely sure, but I think this may have to do with the resolutions of the different screens. If you've ever looked at an html5 canvas on a smartphone, which most have a much greater resolution per sq inch than a monitor, then you'll see that the quality is much degraded because the web browser expands the 100px that you tell it to use to the same physical size, which ends up being around ~130px. I think the same is happening with your retina display because it uses a crazy good resolution. Basically, different screens have different pixel size ratios. To get around this, I added the following to my code.
var PIXEL_RATIO = (function() {
var ctx = document.createElement("canvas").getContext("2d"),
dpr = window.devicePixelRatio || 1,
bsr = ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backignStorePixelRatio || 1;
return dpr / bsr;
})();
Now, whenever you make a canvas or draw anything, multiply your constants by the variable PIXEL_RATIO.
I have different widths for borders applied to a div, and only Firefox shows thin seams when the div is rotated to any angle using CSS3 Transition Rotate. These thin seams change slightly depending on angle.
If the borders are the same width, Firefox behaves nicely.
The div is not using an image, just a colored background, but the content seems irrelevant for the border of different widths issue I'm having.
Unfortunately the area behind the border is going to be reserved so I'm not able to use another div as a wrapper.
Here's a jsFiddle of an example to be seen in Firefox that has this issue. There are no issues in Chrome.
Status Update: Updated jsFiddle to show border-style prior to border-color per CSS rule but no change.
I wonder if this issue is because border-image property, which I am not using, allows up to eight images, one for each border slice. That said, if there were border-corner-color properties then that would solve the issue when using Rotate.
I have made a solution using :before in CSS: jsFiddle example.
I added this code:
#thinLinesInFirefox:before {
content: '';
display: block;
width: 201px;
height: 201px;
position: absolute;
top: -105px;
left: -120px;
border-top: 104px;
border-right: 110px;
border-bottom: 115px;
border-left: 119px;
/* Define border-style before border-class per CSS rule. */
border-style: solid;
/* Define boder-color */
border-color: black;
z-index: -1;
}
Basically, it overlays the same square using :before, except I have decreased the border-top and border-left by 1 pixel, and then increased the width and height by 1 pixel so that the 'real' div underneath appears to be the same size.
Because of the different borders, the seams are in slightly different positions, so what is underneath doesn't show.
Those look like antialiasing artifacts from painting the border in several separate pieces. Each piece is being rotated, so its edges get antialiased, with the result that some pixels at the join are partially transparent (because they're the result of painting two partially-transparent pixels on top of each other).
There is no problem on this testcase in Chrome because at corners it paints the borders under each other. Of course that causes non-opaque borders to be totally broken in Chrome; see http://snook.ca/archives/html_and_css/safari-transparent-borders
And if you were to make the border colors slightly different, you'd get seams in WebKit too. See http://jsfiddle.net/YVCeX/ (it shows seams in the div's background color, whereas Firefox optimizes away background painting under opaque borders, which is why you're seeing red seams, not blue ones.
There's really no good way to handle this, in general, without turning off antialiasing for border edges and having jaggy borders when rotated.