Java-Gaming.org    
Featured games (91)
games approved by the League of Dukes
Games in Showcase (581)
games submitted by our members
Games in WIP (500)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
   Home   Help   Search   Login   Register   
  Show Posts
Pages: [1] 2 3 ... 16
1  Java Game APIs & Engines / Java 2D / Re: Awesome Speeds With VolatileImage on: 2012-10-07 21:24:55
Wrong. BufferedImages *are* accelerated now. Look into "managed images".

Managed BIs are accelerated when used as source, when used as destination ("drawing on bufferedimages"), they are strictly CPU only.
I did the new xrender java2d pipeline thats in jdk7, I know its the same for the opengl pipeline and thinking about the java2d-internals I really doubt the d3d pipeline accelerates rendering TO BIs.

So the code posted compares software-only rendering with hw-accelerated rendering, but by using a BufferedImage as buffer (=destination) it doesn't give "image management" even a chance.

Basically Kerai summed it up quite nicely:
Quote
Java2D tries to cache BufferedImage as texture whenever possible, so drawing it anywhere is as fast as drawing texture.
problem is.. drawing ON BufferedImage is performed in software, while drawing ON VolatileImage is (if possible) on GPU.
so its only faster to use VolatileImage if you want to draw ON IT, otherwise its better to use compatible BufferedImage.
2  Java Game APIs & Engines / Java 2D / Re: Awesome Speeds With VolatileImage on: 2012-10-07 13:20:09
What you say was true in 2004 / Java 5 (as the links imply) indeed. Drawing ON a BufferedImage is often hardware accelerated now.

Nope, drawing ON BufferedImages is never accelerated - its just modern CPUs got quite fast Wink

orogamo:
Try to only make the Buffer a VolatieImage, and use BufferedImage for your sprites.
It should give similar or even better throughput.
3  Java Game APIs & Engines / Java 2D / Re: Linux + OpenGL pipeline + windowed = no display until resize on: 2009-04-08 16:47:21
Quote
Just make sure everything drawing-related you do (including opening the window) is done on the AWT thread. This might be difficult for your active rendering, but you could create an updater implementing Runnable and call SwingUtilities.invokeAndWait(updater), from your main loop.
Graphics2D is thread-safe, and especially with the current STR implementations it doesn't make _any_ difference which thread paints to your OGL surface. Its mutexed anyway, executed by a single (non-EDT) thread later.

The original OpenGL pipeline worked quite good, but it turns out, that they used an implementation path becoming notoriously unstable after driver updates.
The problem is drivers are borken, period.

- Clemens
4  Java Game APIs & Engines / Java 2D / Re: Anti aliased drawPolygon on: 2008-11-18 17:45:22
well, there's still -Dsun.java2d.opengl=True Wink
5  Java Game APIs & Engines / Java 2D / Re: Anti aliased drawPolygon on: 2008-11-18 00:25:52
Have you tried -Dsun.java2d.pmoffscreen=false ?
It should help a lot in your case.

- Clemens
6  Game Development / Performance Tuning / Re: BufferedImage backed by a ByteBuffer on: 2008-05-22 19:58:18
Quote
Why am I stressing this? If any code in the JVM creates a HeapByteBuffer using ByteBuffer.allocate(...), the performance of the direct buffers can lose performance by factor 10, seriously - it has to unoptimize from extremely fast pointer-access to a jumptable with subclasses, now that there is more than 1 subclass of ByteBuffer.
If it would be a "normal" method call, it would go from monomorphic to a bimorphic call which is something like if/else + uncommon trap handling.

However this stuff is totally instrified (at least in the server-vm), so I guess it will not be treated like a normal method.

lg Clemens
7  Java Game APIs & Engines / Java 2D / Re: Fastest way to drawImage()? on: 2008-05-22 19:55:32
What do you mean by this:
Quote
but are loaded into BufferedImages where the pixel ARGB integer values are read sequentially to render the image...

Could you show us some code?

lg Clemens
8  Game Development / Performance Tuning / Re: ... performance on: 2008-04-17 16:58:27
Yes, unfourtunatly the varargs create an Object[] for now - maybe future runtime optimizations can decrease the cost of this operation.
If you write something _very_ sensitive to performance like a raytracer where a vararg-method is called millions of times, I would suggest not using it.

lg Clemens
9  Java Game APIs & Engines / Java 2D / Re: java volatile img on: 2008-04-17 16:55:07
Rendering BufferedImages to a VolatileImage is also accalerated if possible, so replacing BufferedImages with VIs won't speed your stuff up.
Its quite simply: Rendering to an VI is accaletared, but rendering from an image is accalerated with both, BufferedImages and VolatileImages.

lg Clemens
10  Game Development / Performance Tuning / Re: Tuning VolatileImage for software-accelerated graphics on: 2008-03-13 19:26:59
Quote
you don't even know what you're talking about....
Sorry but I know what I am talking about. If you're not modyfing your VolatielImages your work is completly useless - BufferedImages provide excatly the same functionality without the risk of loosing their content.

Quote
This is why I gave up on Java2D about... hm... seven years ago now.
Because BufferedImages are accalerated Wink ?

lg Clemens
11  Game Development / Performance Tuning / Re: Tuning VolatileImage for software-accelerated graphics on: 2008-03-13 00:16:38
But they're not as fast as VolatileImage instances because each render cycle has to fetch pixels data from a RAM allocated buffer, unlike Volatile buffer which are known to solely use the VRAM allocation.

Sorry but thats not true. BufferedImages are, if accalerated, copied to VRAM and when blitted to another GPU resource the vram-copy is used.

lg Clemens
12  Game Development / Performance Tuning / Re: Tuning VolatileImage for software-accelerated graphics on: 2008-03-12 10:01:25
I wonder why you don't simply use BufferedImages for your sprites, if they are read-only?
This is what BufferedImages have been designed for (since 1.5), e.g. I know about my old GeForce488Go which has quite a high per-primitive overhead for each rendered VI, whereas thousands of BIs are no problem at all. (I guess the pbuffer state changes are killing it)

lg Clemens
13  Game Development / Performance Tuning / Re: Escape Analysis? on: 2008-03-09 23:33:27
I think I can even think up situations where they decrease performance.
When used right they would be definitivly a way to improve performance, the other way round to opposite happens.
Look at C++ where you have almost all possibilities when it comes down to tell the compiler what you *really* want it to generate - it requires a lot of knowledge to keep the complexity manageable. Worst is, when available they will be used, so even the 98% that won't benefit of value types would have a hard time learning things they don't even seriously need.

lg Clemens
14  Game Development / Performance Tuning / Re: Escape Analysis? on: 2008-03-09 20:38:51
I reckon value types would add vast amounts of benefit to lots of developers. The real problem is inertia in the massive JRE platform and the VM etc.

Most likely for game developers, not for me, nor for the server guys Wink
15  Game Development / Performance Tuning / Re: Escape Analysis? on: 2008-03-09 12:12:52
Quote
But it's getting closer, and Flash just seems like it's being developed in a much more flexible manner than Java.  The past two versions of Actionscript have each seriously broken code from the previous versions, to the point that you're essentially learning a new language.
In my opinion this just shows how bad the initial design of flash was - and where it comes from.
First it was just a thing for dynamic vector graohics, them some poor scripting was added and somehow married ith the video-design they stll had in mind.
The developers found that because flash is so widely deployed that they could make webapps with it, so again some pieces were added some where broken.
So from my point of view flash only focuses what developers are currently crying for, no matter how it would influence the "whole picture" - it may sound dynamic but in my opionion its a tradeof to produce something useable (any hypeable) for now no matter what will be tomorrow.
Very much like MS blowing up C# wih all feature developers can imagine, just to ride on the "hype-wave".
It also to some degree shows that flash is not realy used for very serious stuff, unlike java, because enterprises really don't like stuff to be broken - code has to work for decades instead of just a few versions. Take some Java-1.1 code and you stll can perfectly extend and enhance it with the current JDK/IDEs.

If you look at Java, which has been initially designed for applets and web-devices, it later evolved on the server.
It was ported to mobile devics and lately to blu-ray players ... its extremly flexible.
And on the server it did not feel like a misfit, java was perfectly capable handling the server stuff - as well as the stuff on the client (which unfourtunatly was a  bit out of SUN's focus at that time). Could you ever imagine flash would be used outside the area it was developed for?

Quote
For instance, this whole stack allocation thing has one extremely simple solution.  Forget escape analysis, just allow classes or structs with value semantics - this has been suggested a thousand times for a thousand reasons.  If you can never pass a pointer or a reference, then BAM!, the thing can never escape, so you can safely allocate it to the stack.  Easy to implement, proven to work (built-in types prove this).  But no.  As Sun sees it, any possible means of declaring and manipulating a value type is far too complex for the rubes that program Java, so the idea gets flushed every time it's brought up.
This has always been a problem point - and if you look almost no languages add that.
Value types add some kind of complexity to the system which affects _all_ developers, whereas only relativly few would really benefit a lot from them.
So ... if flash is so cool why does actionscript not have value types Wink

lg Clemens

By the way my style is a bit of .. well ... agressive so don't take any offense please
16  Game Development / Performance Tuning / Re: Escape Analysis? on: 2008-03-08 15:59:58
They can be sceptical all they like - it's already implemented in Excelsior JET and it works.
Sure, the question just seems who will implement it in Java.
I see ongoing bashing of Sun for not implementing stack-allocation - but on the other side, nobody else takes the work to do it now that Java is open-source. It seems its just not important enough Wink

By the way the team that implemented initial EA and stack-allocation is located in Linz/Austria (40km away from my home) ... it seems they already did implement a prototype: http://www.usenix.org/events/vee05/full_papers/p111-kotzmann.pdf

lg Clemens
17  Game Development / Performance Tuning / Re: Escape Analysis? on: 2008-03-08 15:55:02
Quote
Maybe I'm being a bit cynical and overly harsh (definitely am, sorry!), but looking at the relative performance increases between the Flash player and the JVM over the past few years, I'm starting to cross my fingers and hope that the Flash VM does in fact start supporting Java code as was rumored on another thread, if only because it's a platform where the desktop consumer is considered worthy of optimizing the VM for.  I'm getting ahead of myself, for sure, because the Flash VM is still quite a bit slower than the Java one (and it definitely doesn't have escape analysis), though the gap is closing (when I get around to running them I'll post some current test results in another thread).

Well I don't know how serious I should take this post.
Have you ever benchmarked flash9's VM?
Sure there have been GREAT improvements compared to older versions of flash - but only because older versions of flash did not have a JIT at all.
So in fact Flash9 adds to Flash what Java has since about ... well I guess it was 1.1.7 when Sun shipped it with the Symantec Just-in-Time-compiler.

If you would have seriously benchmarked the flash-vm, you would see that it cannot compete not even with the client-vm, not speaking about the server-jvm at all.
The client-jvm does not do too fency optimizations, but has undergone major tuning over the past years - and also benefits from other runtime improvements that were done to the rest of the runtime. And the server-compiler itself generates code better than the .NET JIT without any question.

I don't know wether the tired compilers will be default anytime soon, but I think this direction is right.
A focus could be to make the client-compiler compile even faster (at the expense of the quality of the generated code), because the server-compiler will be there anyway to optimize the really hard stuff.

lg Clemens
18  Game Development / Performance Tuning / Re: Escape Analysis? on: 2008-03-06 20:34:54
Hi again,

I'll try to answer some questions to my best knowledge ... but if I am wrong, I am wrong Wink

Quote
Does anyone know much about the internals of EA and why it might be more difficult or expensive to implement on the client VM?
The idea of the client-compiler is to do cheap optimizations so that code can be compiled fast - it wouldn't make much sence to add an expensive (and optimistic) optimization like EA. The server-compiler is the place where it belongs to and with the tired compilers you should be already be able to benefit from it with the JDK7-ea builds.


Quote
'm a bit confused.  From what I remember, escape analysis was supposed to automagically solve many of our temporary small object woes
.............
It seems even sun VM devs are not up to date with the -XX: flags
EA != Stack allocation. EA is a step to gather information which can be used to du stack allocation and several other optimizations.
So EA is implemented and already some simpler optimizations which use the information gathered by EA, but stack allocation is not done till now.

Currently there seems to be some work on using EA for doing scalar replacement, something which should help to remove some of the memory preasure 64-bit systems suffer from (also a win for 32-bit systems) Smiley

lg Clemens
19  Java Game APIs & Engines / Java 2D / Re: Java2D & Native Buffers on: 2008-03-06 12:44:37
There was some discussion about pinning in the VM: As far as I can say nobody should expect pinning to be available anymore.
Hotspot itself does not support pinning, as it would destroy the bump-the-pointer allocation strategy - and because allocation for typical java programs is much more important than java->C data transport I don't think things will change.

The get*Critical Methods themself block the GC, therefor allow to access the data without copying it.

By the way, my old Athlon-Thunderbird-1Ghz (PC100 ram) copied arround with about 800mb/s - so nothing to really worry about anymore Smiley

lg Clemens
20  Java Game APIs & Engines / Java 2D / Re: drawImage perfomance on Leopard on: 2008-02-13 19:27:44
Java-1.5.0 on Leopard switched from their quarzt-java2d-backend to a software-only java2d pipeline.
There is some switch which loads the "old" java2d pipeline, I dont know its exact name but I am sure you'll be able to find it on the net.

Best would be if you could write a small testcase and post a description to: java-dev@lists.apple.com

lg Clemens
21  Java Game APIs & Engines / Java 2D / Re: Using less Java heap with BufferedImage on: 2008-02-08 15:40:05
Quote
I'm sure you misinterpreted that one, it should be the other way around. VolatileImage's reside in vram, if you wanted to actually change the image contents you'd have to stream to the new contents to vram over the agp/pci-e bus, which is relatively slow.
No, drawing _to_ a VolatileImage is accalerated (and also drawing a VI->VI or VI->Screen), so you don't stream the new contents over the bus but (in the optimal case) only drawing commands, the GPU then modifies the image. Thats why chet recommends VI for drawing _to_ it.
If you alter a BI and blit it again the _contents_ are transferred over the bus which is slow.

A VI also can have higher overhead when drawing a VI to screen compared to a BI, at least with the OpenGL pipeline.

Quote
Also, operations like scaling, rotating etc can be performed on the videocard for VolatileImage's, making use of the operations exposed by D3D or OpenGL.
Well, for an accalerated BufferedImage this is also true.

The best in your case would probably to allow the JVM to allocate more heap .

lg Clemens

The easiest way to understand whats goin on is to look at how VolatileImages and BufferedImages are implemented.
A VI is a pbuffer (or this new, great pbuffer replacement ^^), a BI is a texture.

lg Clemens
22  Game Development / Performance Tuning / Re: how to detect occasional stuttering on: 2008-01-19 16:02:25
Is this really worth a flame?

I agree that pools have some advantages, and sometimes "new" has some advantages ... well and thats it Wink
Each technique has its use-cases and advantages or disadvantages.

My hope is that someday soon stack-allocation will be implemented in hotspot, but as some Sun engineers statet this means many changes to the hotspot-jvm on various places so ... well ... I still hope Wink

lg Clemens
23  Game Development / Performance Tuning / Re: how to detect occasional stuttering on: 2008-01-17 12:05:01
Hi again,

Well the first loop runs interpreted first and the is replaced by OSR compiled code. Thats why my code does warmup for both code-paths, so that I don't see compilation side effects.

However I thought a bit more about the fact why in real-world apps allocation may not be that cheap as its in the benchmark here:
- The benchmark is a best-case for a GC. Almost all objects die in the young generation, which is exactly what the GC was tuned for. Also keep in mind that there are no complex object graphs.
- In real-world applications , if young generation is sized too small, a lot of stuff is propagated to the old generation. An gc in the old-gen is expensive anyway...

Quote
It's trial-and-error all over again!
Well, I just can say hotspot is great. However I have to admit that some knowledge of how hotspot does its things can help to understand whats going on...

lg Clemens
24  Game Development / Performance Tuning / Re: how to detect occasional stuttering on: 2008-01-16 23:42:10
static methods are faster, because you save some fetching and at least one branch (the branch which takes care about de-optimization).

Your benchmark does many different things, it could be quiet possible that some cache-issues come up.
I just can state that in mine allocation is faster, while in yours the pool performs better Wink

lg Clemens
25  Game Development / Performance Tuning / Re: how to detect occasional stuttering on: 2008-01-16 22:45:46
well i guess the reason is that you are not using ArrayList - furthermore all your methods are static.
Here begins the fight between readability and performance Wink

I modified the benchmark and this way there is no way out for the JIT:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
import java.util.*;

public class AllocTester
{
    public static void main(String[] args)
    {
        Vec3f v2 = new Vec3f();
        /*warmup*/
        Pooler p = new Pooler();
   
        for(int i=0; i < 1000000; i++)
        {
            Vec3f v1 = new Vec3f();
            v1.setValues(i);
            v2.add(v1);
        }

        for(int i=0; i < 1000000; i++)
        {
           Vec3f v1 = p.getObject();
            v1.setValues(i);
            v2.add(v1);
            p.releaseObject(v1);
        }

    /*measure*/
        long start = System.currentTimeMillis();
        for(int i=0; i < 100000000; i++)
        {
            Vec3f v1 = new Vec3f();
            v1.setValues(i);
            v2.add(v1);
        }
        long end = System.currentTimeMillis();
        System.out.println("Allocation took: "+(end-start));

        start = System.currentTimeMillis();
        for(int i=0; i < 100000000; i++)
        {
           Vec3f v1 = p.getObject();
            v1.setValues(i);
            v2.add(v1);
            p.releaseObject(v1);
        }
        end = System.currentTimeMillis();
        System.out.println("Pool took: "+(end-start));

         System.out.println(v2.x+v2.y+v2.z);
    }
}

class Vec3f
{
   public float x,y,z;

    public void setValues(float i)
    {
        x = i;
        y = i;
        z = i;
     }
   
    public void add(Vec3f v)
    {
        x += v.x;
        y += v.y;
        z += v.z;
    }
}

class Pooler
{
    ArrayList pool = new ArrayList();
   
    public Vec3f getObject()
    {
        if(pool.size() > 0)
        {
            return (Vec3f) pool.remove(pool.size() - 1);
        }else  
        {
           return new Vec3f();
        }
    }

    public void releaseObject(Vec3f v)
    {
        pool.add(v);
    }
}


So if even ArrayList access is slower, which is heavily inlined and optimized by the JIT (removing and adding the last element are more or less no-ops), I wonder wether this is really worth all the troubles.
Furthermore allocation was not optimized away, as I was able to watch the GC ... which was quite busy.

lg Clemens
26  Game Development / Performance Tuning / Re: how to detect occasional stuttering on: 2008-01-16 20:33:38
Sorry, it's simply not true. The overhead is in the allocation, not in the destructors.
I tested this by incresing the heap, adding -verbose:gc, to monitor the GC, and it was *NOT* run during the benchmark. Still the performance was poor, compared to pooling the Vec3's.

Well there are several factors to consider here:
1.) how large was your data-set? If you only generate 2000 objects in the pool you end up having your whole data-set in the L2 cache, whereas allocation in the heap has to move bytes arround. However this is not likely to be a real-world result.
2.) Do you set your floats to "0" before you hand out the object of the pool - Java's allocation code does this

I created a really simple and stupid micro-benchmark and although I did not expect it - java allocation won without any tuning:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
import java.util.*;

public class AllocTester
{
    public static void main(String[] args)
    {
       
        /*warmup*/
        Pooler p = new Pooler();
   
        for(int i=0; i < 1000000; i++)
        {
            new Vec3f();
        }

        for(int i=0; i < 1000000; i++)
        {
           Vec3f v = p.getObject();
            p.releaseObject(v);
        }

    /*measure*/
        long start = System.currentTimeMillis();
        for(int i=0; i < 100000000; i++)
        {
            new Vec3f();
        }
        long end = System.currentTimeMillis();
        System.out.println("Allocation took: "+(end-start));

        start = System.currentTimeMillis();
        for(int i=0; i < 100000000; i++)
        {
           Vec3f v = p.getObject();
            p.releaseObject(v);
        }
        end = System.currentTimeMillis();
        System.out.println("Pool took: "+(end-start));
    }
}

class Vec3f
{
   public float x,y,z;
}

class Pooler
{
    ArrayList pool = new ArrayList();
   
    public Vec3f getObject()
    {
        if(pool.size() > 0)
        {
            return (Vec3f) pool.remove(pool.size() - 1);
        }else  
        {
           return new Vec3f();
        }
    }

    public void releaseObject(Vec3f v)
    {
        pool.add(v);
    }
}


My results were:
Allocation took: 2113
Pool took: 4216


lg Clemens
27  Game Development / Performance Tuning / Re: Objects vs mapped objects vs floats on: 2008-01-15 13:12:24
I read a bit in recently fixed bugs/rfe's and it seems that at least for the server-compiler a lot of tuning has been done for ByteBuffer's (in JDK6u4 and JDK7).
Anybody willed to re-run the benchmarks? Wink

lg Clemens
28  Java Game APIs & Engines / Java 2D / Re: Vista+Java2D - Performance useless + Vsync doesn't work? on: 2008-01-14 01:28:23
Well polygons are rendered sending down "spans" to the graphic card - a span is a rectangular part of that polygon.
So first there's quite some CPU overhead generating those spans from the polygon supplied by the user and second there's quite a high per-primitive overhead for drawing operations with todays drivers/systems. And because a polygon can result in many spans, a lot of per-primitive overhead happens here :-/

lg Clemens
29  Java Game APIs & Engines / Java 2D / Re: RenderCapabilities? on: 2008-01-07 21:05:22
I also have to agree - such an API is overdue.

Back in 1.4/5.0 almost all Java2D backends had more or less the same capabilities / weaks, but now its really changing.
An api like this would allow developers to deviced wether eyecandy XYZ is really woth the 1/4 fps rate.

lg Clemens
30  Java Game APIs & Engines / Java 2D / Re: Sign up for Java2D bug update notifications mail list on: 2007-12-14 17:00:50
Wow cool - thanks Smiley
Pages: [1] 2 3 ... 16
 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

xsi3rr4x (57 views)
2014-04-15 18:08:23

BurntPizza (55 views)
2014-04-15 03:46:01

UprightPath (68 views)
2014-04-14 17:39:50

UprightPath (51 views)
2014-04-14 17:35:47

Porlus (68 views)
2014-04-14 15:48:38

tom_mai78101 (93 views)
2014-04-10 04:04:31

BurntPizza (153 views)
2014-04-08 23:06:04

tom_mai78101 (249 views)
2014-04-05 13:34:39

trollwarrior1 (205 views)
2014-04-04 12:06:45

CJLetsGame (213 views)
2014-04-01 02:16:10
List of Learning Resources
by SHC
2014-04-18 03:17:39

List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51

Good Examples
by matheus23
2014-04-01 18:40:34

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:22:30
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!