There is nothing stopping the console developer from making an OpenCL implementation available for their respective platform as its just a standard waiting for implementation. No different that DX or its variant being used on Microsoft console.
My point, though, is that you really only need OpenCL for the abstraction, and you don't need abstraction on consoles, even when your game is cross-platform (becasue each console will likely sell 100million units in its lifetime, and when you have those kinds of numbers, they demolish even the most popular, long-lived PC GPUs) -- yes, there could a a very specific version of OpenCL that's highly tuned for one or both consoles, but even still its unlikely it would produce as good results as a hand-tuned command stream. If you're on a console, and you have something that's so critical to be worth offloading at all, its equally worth hand-tuning it to the platform in all likelihood, and if you're pushing that kind of envelope its likely you have that expertise on your staff.
I argue that you won't get that top performance out of OpenCL even with extensions, a custom run-time, and a custom compiler -- but even if you did, that would be a huge software effort, and even still would not be vanilla OpenCL, but a custom-extended OpenCL -- so what would you gain? The extensions make it non-portable again, so we can't take our OpenCL source code and simply recompile it for another platform.
That doesn't mean one vendor's solution doesn't look like OpenCL maybe, or DirectCompute, or C++ AMP -- in fact, one could imagine easily that some subset of these are true -- it just doesn't matter. In the end, on the console, you're sufficiently specialized such that your source code in any form is effectively non-portable.
I mean, even look at the GPGPU ecosystem today -- if you need to run on CPUs and GPUs, or GPUs from anyone but nVidia, then you use OpenCL; and if you know your code will always run on nVidia hardware, then you use CUDA and you'd be a fool not to.
Tertiary would be the proper word in that succession.
But I agree that if this third status is the default research level unless they're activated at a higher level (secondary, primary), then it doesn't make much sense to make the player choose the tertiary status. In that case, what I might suggest is to label this status as something like "Research schedule", and then have the secondary status be "active" and the primary status be "aggressive" -- that would communicate to me that everything else advances at a natural ambient pace, as it normally would do absent additional funding or pressure.
I might also propose an alternative UI entirely, wherein (based on the 5 categories you have in the screenshot above) you have a 5-pointed polygon (a.k.a. a pentagon) -- with two inner rings (actually, kinda like the pentagon building, in fact) where in the center you have how many research points you have to spend, and a point on the pentagons corresponds to each research category -- so the outer-most point on the Construction category means Primary/Aggressive, the middle point means Secondary/Active, and the inner-most means Tertiary/Ambient/Minimum.
An ECS doesn't necessarily map well to cards, but a compositional approach certainly works very well.
ECS is good when you have a lot of entities that all do a different subset of some known quantity of components/behaviors, and when one particular entity might vary over time, or be different from other entities in broadly the same category of entities.
Some of that is a good fit for a game like magic, but others are not. Data-driven composition gets you what you need, IMO, without quite all the overhead of a full-blown ECS.
If you think it through 2D is not really easier conceptually different than 3D.
- You use vectors with 2, not 3 dimensions, which is basically the same difficulty. Or you dont use vectors and calculate everything twice for x and y.
- A few calculations get easier, but not much.
- You use a slightly simpler projection transform, which wont safe you much time as you basically create this once.
- You possibly waste much time emulating features like depth testing or pseudo-3D-perspectives, which you could get for free when using real 3D in the simplest possible way, without always thinking about which sprite to draw last so its above/in foreground or creating an isometric tile engine or similar things.
- You only use quads as meshes, sparing you from creating any real, more complicated meshes, but wasting way more time creating much more complicated textures, which you cant reuse as easily, making you need to create a multitude of them from different perspectives.
Fixed it for you
You do 2D in much the same way as 3D at a conceptual level, but when you get down to details the extra dimension introduces quite a lot more of them, and so quite a lot more difficulty. So, like a said before, if you plan to write things yourself (that is -- to get into the details) then 3D is significantly more difficult, and perhaps best avoided as a first-attempt; but if you plan to use existing libraries/middleware/engines (that is -- to work at a more conceptual level), then 3D is not much more difficult to wield (though even still, somewhat more difficult).
2D really simplifies everything, but not to the point of transforming it entirely -- in short, working in 2D is a great playground for learning to work in 3D later. Take physics, for example, 2D basically means you have only 3 degrees of freedom (translation along x, along y, and rotation in the X-Y plane) to deal with, rather than 6 (X, Y, Z, Roll, Pitch, Yaw) -- everything is basically the same, its just less of it and the simplifying assumptions you can make.
You probably are better off starting with 2D to get your bearings if you're new. One of the most difficult parts of making your first complex game is figuring out how all the parts fit together and interact -- and its almost entirely the same whether 2D or 3D, but you won't have the 3D details holding you up if you start with 2D.
But I guess that really only goes if you want to write those systems yourself. If you're going to use Unreal Engine or Unity or other engines/middleware, you'd probably be fine to choose either if you're very confident that your math skills and reasoning skills are up to the task. Just keep in mind that basically everything is twice as difficult or more in 3D than in 2D.
It's SIMD 4 register friendly, but not very cache friendly. a tuple of 4 vectors is 24byte in size, thus sometimes the x y z components are crossing cache line borders, and in that case you could just as good use pure SoA.
That you cross a cache line border doesn't really matter as long as you're accessing the data in a linear fashion. The memory that maps to the next cache line doesn't increase contention on the cache line before it. You may stall mid-operation waiting to read in that next cache line, but you were going to have to read it anyways, and after a few dozen or hundred sequentially-read cache lines, the pre-fecther is going to grab it for you ahead of time anyways -- all it might cost you is that the final cache line goes partially unused.
It all really comes down to this -- cache-friendly means having all the data you need when you need it, with as little other data in the way as possible. If you have a struct containing a 3D position and a 52bytes of other data, then your position-updating code can only update one position per cache line read in. If you don't also do work on those other 52 bytes, but you need to later, then you have to load that cache line again, and you pay that high cost twice. If you can't use that other cache line data right away, then it makes sense to restructure your data such that positions are in their own contiguous array, where you can process 5.3 positions per cache-line (and in the same amount of time as before to boot, given that cache-line reads are the bottleneck).
It can't be overstated how important cache utilization is. Take your system memory bandwidth and divide it by 64 bytes -- that's maximum number of cache lines you can load per second. Not one more. A typical system bandwidth is 25.6 GB/s, which translates to just 400 million cache lines per second. If you read a pathological worst-case of just one bye from each cache line, that's just 400 megabytes per second -- even if you read 12 bytes of position from each cache line, that's just 4.8 GB/s out of 25.6, a mere shadow of the machine's potential. This resource of cache-line reads per second is *way* more critical than how many operations your CPU can do per second, because it gates everything that doesn't operate entirely within the caches -- CPU cycles and even in-cache read/writes are almost free in comparison, being on the order of 100x and 10x faster, respectively.
And that's all best case, right, because like everything else that runs on a clock in your computer, those unused resources are expiring by the instant -- If you don't read any memory for 12 microseconds, you don't get to save 12 microseconds worth of cache line reads and roll them over to when they're more useful to you, they simply vanish into the ether of time. You use it or you lose it. A program at peak performance would load one new cache line at every opportunity, never load the same line twice, and be entirely done with each cache line by the time that new data needs start forcing things to be ejected (which depends on a number of factors, but assume the window is the size of your L3 cache, so anywhere between 1-6 megabytes in mainstream processors, 12-20 in enthusiast processors like the 6-8 core i7s, or larger in server CPUs)
This has not much to do with the question, but I'm reminded of playing "Black Bass" on the NES when I was young. I had borrowed it from a friend, had no instructions, and had never seen it played. I played for awhile with some small silhouettes of fish swimming beneath my lure, but they would never, ever strike at it. After probably hours, I happened upon a larger fish -- at least 4x as big as the silhouettes, and that fish on occasion would strike but I never could get him hooked. More hours pass, concentrating on trying to catch this one big fish -- and finally I got him hooked! I mashed the button as quickly as I could to reel him into the boat, excited to see how big my catch was...
And then as the fish reached the edge of the boat, and ENORMOUS hand reached down and plucked the tiny minnow I had just spent hours catching from the water.
And that, kids, is why I never played Black Bass again.
There is a lesson to be learned about scale here, however, and that is that scale means nothing absent a well-known reference. Is that fish under the serene blue surface of the lake tiny or enormous? NOT A CLUE! Its bigger than the other fish, what now? STILL NO CLUE! MAYBE THE OTHER FISH ARE JUST SUPER TINY! Look, its at least as big as that lilly-pad, its gotta be big, right? HELL NO! SURE, THEY WANT YOU TO THINK ITS A NORMAL-SIZED LILLY PAD, BUT MAYBE ITS TINY TOO! OR ENORMOUS! NO ONE KNOWS BECAUSE THERE'S NO HANDS, COKE CANS, OR BANANAS IN THE FRAME!
Personally, I feel that it doesn't matter. If you start programming procedurally, you will write bad procedural code. If you start programming in an OO fashion, you will write bad OO code (and probably bad procedural code too ). Same with functional.
When you start programming, you will suck. Same as when you start anything. and the only way to get better is to practice.
That's an important point -- there's no language in which you're going to write good code in right from the start. In turn, this means your goals matter -- if your goal is to be a reasonably compenent programmer who can whip up scripts and little programs for yourself, that's a different goal than if you want to write operating systems or engines for AAA-games. Even still, either goal is pretty far removed from the absolute beginner, so it might be better to divorce the decision of which language or paradigm is best for early learning, from the decision of which language or paradigm that you want to grow into -- although the choice of the latter might influence the choice of the former.
I write the documentation for Graphics Diagnostics, and it isn't really tuned for compute shaders -- it can do it under some circumstances in the current version, but IIRC, only if your compute shader is playing a role in your rendering (e.g. computing local lighting influences as in Dice engine).
If you can deal with being tied to one of the vendors, both provide good, free graphics debuggers. The bigger selling point of Graphics Diagnostics is that its vendor-neutral. Its good and getting better but its not an optimal experience form compute shaders right now.
I've been playing with Erik's code a bit, and another candidate is the almost-never overloaded comma operator. I didn't post the code since I'm unfamiliar with troubles that might arise. I suspect its safe, as comma has lowest precedence, and this use is similar to other uses in C++ DSL implementations, or as its used to collect terms in parts of boost, I'm just unfamiliar with potential hazards that might be found.
Its probably something of an oversight that there's no version of for_each that simply takes a container and a function to apply to all its elements -- it isn't much more inconvenient to simply use the iterator-pair version, or a short range-for form, like "for (auto l in listeners) l->do_stuff(1,2,3); but a version of for_each that doesn't use an iterator pair would eliminate unnecessary verbosity of the most-common use case:
Luckily, with non-member begin() and non-member end(), I think (that is to say **disclaimer** no warranty expressed or implied) you can implement this easily in those terms and in the terms of the existing iterator-pair version of for_each. This is not tested, but it would look something like this:
But I'll leave implementation as an exercise to the reader. (I think probably you could use std::function, but its not lightweight -- I wonder, though, if its exactly as heavy as an encapsulated member function anyways)
[EDIT] ... and Erik's C++11 variadic-template-fu is both stronger and quicker than my outdated C++06 template-fu
So do you claim that singleton patterns are bad habit? I've seen them used many times while viewing engines' source codes.
Other's have already addressed it, and I don't want this thread to descend into yet another long discussion of Singleton's pitfalls like so many others have. But since you asked me, I'll defend my position briefly.
The single worst thing that the Singleton pattern does, IMO, is that it allows you to feel good about outright ignoring important and often multitudinous dependencies that really ought to be thought about carefully, or at the very least ought to be obvious. The globalness and alwaysness of Singleton allows this to happen, and its facade of OOP-ness fools you into thinking that this state of affairs is A-Okay. Furthermore, the singleness of Singleton allows you to make the dangerous assumption that there will only ever be one, and so your typical singleton object never stops to consider how it would have to be in order for more than one of them to cohabitate the same program; worse, many people who do not understand this actually favor this trait as a way to allow them to "simplify" their task -- and as a result, they implement singletons when they "only want one" right now, and come to regret it later when they find out they actually need two or more -- Singleton as an up-front design pattern should only be considered (end even then, not always accepted) when two instances *cannot* coexist, ever, by reasons or requirements that are beyond your control. And the kicker is that a simple global can be cajoled into providing all the essential properties of a singleton design for essentially zero implementation cost -- With the kind of Singleton people write to feel like good OOP citizens, you actually have to write a bunch of code to participate in all their pitfalls and downsides. Sheesh!
I've probably over-recommended it in the past for things that other approaches would be better suited, but I think what you want here is a RESTful API, at least as long as other clients don't need to see changes instantaneously (that is, if your design calls for one player being able to upload a map, and for another player to be able to download it sometime soon, but not instantaneously) -- there are things you can do to hasten the turn-around time, but at its core RESTful interfaces support multi-layer caching -- its one of the reasons the internet scales so well, but also introduces propagation delays where you might see a recently-cached version of the content that's older than what might have just changed on the server. When you request a restful resource (via a URL) the request can be intercepted by, say, a chache your ISP runs, and they might respond with something they have stored for that URL, rather than hitting up the actual server for it again. For content that's static, or infrequently changed, this is good -- otherwise Netflix would have ground the internet to a halt long ago.
There are lots of good resources on RESTful interfaces, but the basics of the API are that each URL represents a unique resource (a map, in your case), and there is a standard set of built-in http API methods that you can use to get, create (post), update, and delete these resources. Usually, the primary resource (URL) for the thing you're dealing with isn't really the thing itself, but might respond with a list of sub-resources that make up the thing in the form of URLs that represent each sub-resource, or things you can do to/with the sub-resources (you can think of these as members of a class). So, you would request the primary URL, and in the response you would find another URL to update the map data itself, for instance. I forget the official document that defines The acronym HATEOAS describes how to best-use this approach, and its the basis of Atom/RSS feeds, so its a well-proven concept. Also, I may not be saying it clearly, but REST is an architecture, its not a protocol -- as such there are some differences of opinion regarding details of implementation.
Azure and other similar cloud providers are one solution that provides a great deal of integration and value -- it has everything you need out-of-the-box. Having never done so before, I wrote a simple RESTful API on azure in less than a day, including all the setup and familiarizing myself with the basic features of azure. Another approach would be a using a host like Digital Ocean or Linode to host a collection of Docker containers that make up sub-services in your server (one for the database, one for blob-storage, one for the REST API, one for a load balancer, etc), with that, you can fairly easily spin up additional resources as you need them. There are third-party solutions for that, but its not all integrated like Azure is. Anyways, the buzzword today is "Micro-services" so you might want to google that.
Seems reasonable to me. You can probably even draw all your opaque objects before rendering in your background, which should give you back most of your hierarchical-Z I would think. You'll need to do transparent objects afterwards, though, if you have any.