As some of you may know, I occasionally blog about various software-related things.
This time I've written something about how the 'new generation' has a tendency to overdesign/overengineer, without bothering to look at the technical details first...
Thought I might share it here, some of you may be interested.
http://scalibq.wordpress.com/2011/07/20/premature-design-is-the-root-of-all-evil/
Posted on 2011-07-20 06:38:43 by Scali
Added to my feeds - will skim through it during lunches :)
Posted on 2011-07-20 11:03:43 by JimmyClif
Reminiscent of the idea of an emergent architecture? Nice blog post.
Posted on 2011-07-20 11:21:14 by michaelg
Nicely written blog post, Scali.  I think a good summation might be: "experience is the best teacher".
Posted on 2011-07-20 11:43:15 by p1ranha

Nicely written blog post, Scali.  I think a good summation might be: "experience is the best teacher".


Thanks... personally I'd lean more towards 'history' rather than 'experience'.
I mean, yes I have learnt a lot of things through experience... but then I found out that smart people have learnt these things long before I did, and wrote about it.
I quote two articles, one from 1974, and one from 1986. So what I'm saying is not new in any way. It's just that this knowledge seems to be 'in limbo', so I try to bring it under the attention of the younger generation, so they don't HAVE to learn the hard way, through many years of experience.

I haven't really put a conclusion/summation at the end of the blog yet... I think I will add one that says something like this.
Posted on 2011-07-20 12:10:30 by Scali

I always build a small prototype application first, which concentrates only on getting such core functionality working, and finding out what’s what.


Most of my finished programs are 'prototypes' which just happen to work  :D
Posted on 2011-07-20 12:15:01 by JimmyClif

I haven't really put a conclusion/summation at the end of the blog yet... I think I will add one that says something like this.


I've added an extra conclusion at the end now, trying to stress that point.
Posted on 2011-07-20 15:34:19 by Scali


I always build a small prototype application first, which concentrates only on getting such core functionality working, and finding out what’s what.


Most of my finished programs are 'prototypes' which just happen to work  :D


Well, that's good, isn't it? The program works, and it's finished :)
Posted on 2011-07-20 15:35:20 by Scali


Most of my finished programs are 'prototypes' which just happen to work  :D


Well, that's good, isn't it? The program works, and it's finished :)


Wait just one minute!  Are you telling me there are actually finished programs?  :shock:
Posted on 2011-07-20 16:31:40 by p1ranha

Wait just one minute!  Are you telling me there are actually finished programs?  :shock:


Finished, abandoned... glass is half-full or half-empty... :P
Posted on 2011-07-20 17:15:36 by Scali
Tom-eih-toe :: Tom-aah-toe - START: My programs work fine until they crash, then I'll fix them and GOTO START

Thank goodness I'm not paid for programming - for the payee or the payer :)

Anyway, just wanted to let you know that I haven't had such a great time reading a blog in a very long time. The Jed Smith discussion and related posts are pure gold. :thumbsup:
Posted on 2011-07-22 07:22:59 by JimmyClif

Anyway, just wanted to let you know that I haven't had such a great time reading a blog in a very long time. The Jed Smith discussion and related posts are pure gold. :thumbsup:


Yea, I think I let him get off easy there... perhaps I should do another blog follow-up on him soon :)
The irony is that I don't exactly consider myself an expert on security, it's not even a subject I'm interested in at all, I mostly just learnt about security as and when encountered during whatever programming it is that I was doing at the time. For Windows-programmers it's pretty obvious: SECURITY_ATTRIBUTES are all over the Win32API (so why doesn't he know about them?).

I'm also waiting for AMD to finally release that Bulldozer of theirs, because I still need to do a follow-up on this post:
http://scalibq.wordpress.com/2010/08/31/john-fruehe-amd%e2%80%99s-latest-and-greatest-liar/
Posted on 2011-07-22 11:11:31 by Scali

I'm also waiting for AMD to finally release that Bulldozer of theirs, because I still need to do a follow-up on this post:
http://scalibq.wordpress.com/2010/08/31/john-fruehe-amd%e2%80%99s-latest-and-greatest-liar/


Heh, what a coincidence, just saw this: http://www.brightsideofnews.com/news/2011/7/22/amd-lowers-performance-projections-for-bulldozer-opteron.aspx
I guess AMD agrees: John Fruehe was talking smack.
This site also points to a Fruehe-blog stating these lies. Told you so :)
Posted on 2011-07-23 08:08:30 by Scali
Hey Scali, good read!

I can't say I *totally* agree with you though. I think the design methods taught in schools now is really good, but the true evil is that as soon as these students get out of the class room, they "just wing it".

Our school used the Three-Tier programming model. If you're not familiar with it, the idea is to create reusable solutions to small problems. This is done by breaking the interface with external actors (users, other computers, or even other parts of the program) and the persistent data storage routines (SQL, CVS, Binary Formats, etc.) completely apart using  a business tier which provides the actual solution to the interface.


Presentation Tier <-> Business Tier <-> Persistence Tier


What's more, this programming model is complemented by a series of design documents which controls the flow of development. For example, the use-case document provides the step by step instructions for how the interface (Persistence Tier) will be accessed by the external actors. From the use-case an event/function planning document can be created to prototype the "how" of the solution (Business Tier), this document comes in the form of a large table describing how each signal/event and other interface actions are handled -- usually through a final column describing a bit of English psuedo-code for the solution. This document in turn gives way for a requirements document which describes what storage/network services will be needed to solve the problem at hand (for the Persistence Tier).

Now, after that very modular design strategy has been taught, what's the first thing novice programmers do?


I don't want to have to write a bunch of documents, I just want to code!


Yep, that's right. They throw documentation out of the window to "improve productivity". But what are they really throwing away? The use-case gives you modularity, the event/procedure planning document gives you a great place to simplify and optimize your algorithms before ever writing any code, and the requirements document builds a reusable standard for data access. And to add insult to injury, they stop designing small solutions and try to improve productivity yet again by creating an overall solution which is neither modular or optimal. Then, when the company wants said application to be updated or features added, the novice is stuck with doing some hacked fixes and trying to extend a system rather than plugging in new components through a small, modular design.

Regards,
Bryant Keller
Posted on 2011-07-23 09:04:37 by Synfire

Hey Scali, good read!


Thanks.


I can't say I *totally* agree with you though. I think the design methods taught in schools now is really good, but the true evil is that as soon as these students get out of the class room, they "just wing it".


I suppose it can go either way, yes. Some will just go the 'textbook'-way too much, where others become too 'creative' with the design methodologies.

My point is mostly about non-trivial code. Thing with textbook-examples is that you already know the solution beforehand. The three-tier model is nice, but when you have to start from scratch, you may not know exactly what goes where, and how the three tiers need to communicate exactly, in all areas.
So in such cases, you'll want to build a prototype to just experiment with the technology a bit, and figure out what makes sense.

I mean, if you take my 3d engine for example... some things just HAVE to go someplace, since it will not work otherwise. For example, I *have* to build an input layout object at the moment I compile a vertex shader, since it is not available at any other point in time. That is just a limitation of the shader compilation model in D3D10+. I had to find that out by building a prototype first... I just built a simple app that did nothing much other than rendering a simple cube on screen. But it taught me all I needed to know about the D3D10+ pipeline.

From then on, I could take my D3D9 design, and figure out where and how to modify it to incorporate D3D10+ as well. I did something quite similar for OpenGL, with my BHM 3D sample. I started by just building a simple prototype OpenGL app. This taught me enough about OpenGL to figure out how to build a nice object-oriented wrapper for it. It's quite similar to my D3D-engine, but some things just work differently.
If I had just blindly taken my 'textbook' D3D9 design and stuck to that, it would have been very hard to incorporate D3D10+ or OpenGL in there, as the APIs just work quite differently in some ways. You'd paint yourself into a corner, but you wouldn't know until it actually happened, as the D3D9-design looks good on paper... it *is* a good design, for D3D9, just not for other APIs.
Posted on 2011-07-23 09:31:25 by Scali
Well, that's sorta what I'm talking about though. The model they actually taught in school was based around small solutions. You wouldn't directly implement a solution for "Game Engine", rather you would start by creating small solutions for various components of a game engine. This means that as you implement each small part, you can rapidly create test apps which make use of the solution. If as you move along you decide to swap to OpenGL from DirectX, it becomes a matter of updating the Business Tier's for each solution rather than worrying about how they are connected (the Presentation Tier would make sure that they still connected just fine).

But like I said, you get these novices that think they can save time by trying to tackle the "Game Engine" directly, or maybe breaking it up into a few simple objects which describe the game engine. That's not really what most people are taught but it seems that everyone now-a-days does it.

It is actually very similar to what you're talking about with making prototype apps to learn from first. With the 3-tier model, you simply apply a modular design to each part that you are learning so that when you're finished playing with your prototype, it can immediately go into production as long as you consistently follow the 3-tier design throughout the scope of your project.

What I like about the 3-tier model is how it's completely changed the manner in which I design my applications. If I'm going to be working on the Presentation Tier for a module, I'll work mostly on pen & paper drawing flowcharts and researching possible frameworks which might make my job easier. If I'm working within the Persistence Tier, I'll spend most of my time working out the data structures or SQL Queries. And when I'm working within the Business Tier, I spend a lot more time on a dry erase board simplifying any algebraic or boolean logic functions. Because of the separation of each tier, I know immediately how I need to approach each part of a small problem. And usually, by the time I actually begin writing my first draft of the module and it's test app, it's already fairly close to production quality and rarely needs any additional optimizations.

Why wouldn't it need additional optimizations? Because I've taken my time to truly focus and understand what it is I'm doing. I build the full application up in vary small steps and validate those steps as I grow the modular design into an actual application.

I'm not really big into game or graphics design, but to explain it in a more relative manner... Say you wanted to create a software based 3D graphics rendering library (let's just ignore the fact that pretty much every modern graphics card can handle this stuff for us), with the three tier design method, you would implement a modular pixelOut routine. the presentation tier would be the actual procedure call, ie pixelOut(SCREEN scr, int x, int y). The business tier would be taking that x and y and applying it to the scr bitmap. The persistence tier would be defined as the actual scr bitmap, scr might not be a bitmap, you might decide you want it to be a jpg. But that wouldn't matter to pixelOut's presentation or business tiers. Then we write a test app to "prove" the pixelOut works. When it works, we have our first solution. Next we tackle calculating 3d points from 2d points.. this may be done by creating a pair of procedures calcX3d(int x, int z) and calcY3d(int y, int z). You'd follow the standard three tier method with those as well. As you work on each small solution, validating them is easy because you work individually with each solution, and solutions work together because the interfaces are pluggable; ie. pixelOut(calcX3d(iX, 10), calcY3d(iY, 10)); Once you get through that, you might think about implementing some kind of rotation method using precomputed cos/sin tables. Each step just fits, each part will work once plugged in because you design the interface to be reusable. Do you know or care if it's DirectX or OpenGL behind pixelOut? would it make any difference to calcX3d? Of course not! So from there on, if ever need to change from software rendering to hardware rendering, you just create abstract wrappers for the features of your 3d model that is handled by hardware and nothing else needs updating. This gets thrown out the window by those pesky newbs and rather than solving small problems, they try to solve the problem of pixelOut3DandRotate(....); :P
Posted on 2011-07-23 10:54:38 by Synfire
Well, the thing about graphics is that you indeed *do* care about what API is behind it, at least to a certain extent. What that extent is exactly, that's something you can only determine once you have some hands-on experience with the API.
The APIs don't map 1:1. Which means that indeed you have to worry about how they are connected. For example, I used to have the vertex format declaration stored with each mesh. Makes sense, no? A mesh is where you store vertices, so that's where you store the vertex format as well.

Well, no, apparently not. In D3D10+, there's some extra validation implemented in the API. As a result, you need to create an Input Descriptor (vertex declarations no longer exist, this is the closest object, semantically), from your compiled vertex shader. After all, the vertex shader code contains the vertex format as well.
So something that originally 'belonged to' meshes, now had to be moved to the vertex shaders.
Posted on 2011-07-23 12:10:05 by Scali

For example, I used to have the vertex format declaration stored with each mesh. Makes sense, no? A mesh is where you store vertices, so that's where you store the vertex format as well.


Actually no. Although a mesh is just a matrix of vertices, a mesh (in a 3 tier design) should be implemented in an abstract manner for support of any plotting method. Example being a Vert4f<> where the 4th value is time so you're not only modeling the location of the mesh but it's position on the time-line (or maybe wear imposed on the mesh). In 3 tier design; custom data types and structures should become globally visible to any part of the overall project. These data types can be extended if needed, but the basic "layout" as specified by your class or struct definition should be in a header that doesn't contain any module dependent references.

In your scenario, you implemented a vertex as though it "belonged to" a mesh. What I would have done (using the "Solve Small Problems" rule) is create a module called vertex which just defines the vertex and maybe some routines to convert/access various vertex related information. The mesh would then be implemented as a new module which uses the vertex module. Then when your D3D10+ code comes along, you simply implement the vertex shader by extending the vertex module itself.
Posted on 2011-07-23 19:06:49 by Synfire

Actually no. Although a mesh is just a matrix of vertices, a mesh (in a 3 tier design) should be implemented in an abstract manner for support of any plotting method.


But you can't do that, because you need to play by the rules of the API.
So you create a class Mesh, which contains the required data for the API to render a mesh. It's not really a storage class. Storage is done in lower-level classes, such as vertex buffers and index buffers, primitive descriptors etc (A mesh most certainly is NOT a matrix of vertices, at least not literally... it is an abstract representation of that).
A Mesh just groups all the storage classes together so you have everything you need in order to draw that mesh on the given API.
Technically my Mesh is abstract.. you can just draw a mesh with one call... It's just that the implementation of this draw call (and partly the mesh itself, let alone the objects it groups together) will be massively different from one API to another.

In your scenario, you implemented a vertex as though it "belonged to" a mesh. What I would have done (using the "Solve Small Problems" rule) is create a module called vertex which just defines the vertex and maybe some routines to convert/access various vertex related information.


No, I didn't. A vertex is just a struct.
The vertex format descriptor indeed *was* a separate object (one dictated by D3D9). The thing is just that you have to store that vertex format *somewhere*, in order to describe what kind of data is in your mesh.
So, you store a reference of the vertex format descriptor in your mesh. After all, the mesh contains the vertex buffers. And in D3D9, the format descriptor was most closely related to the vertex buffers anyway (you could render without shaders, and then it STILL needed a format descriptor, obviously... it also had the legacy FVF flags still, which you used when creating a vertexbuffer).
But D3D10+ changes the rules there. Firstly, the vertexbuffer is now more abstract, just a buffer. Secondly, there's no fixed function anymore, so you HAVE to have shaders at all times. Thirdly, there's more validation built into the API at initialization time, rather than at runtime.

So in D3D9 the API would want a vertex buffer and a vertex descriptor, before a draw call... and if you then optionally used a shader, it would automatically verify if the shader was compatible with the vertex descriptor.
In D3D9 you could create this vertex descriptor at any time you wanted, you just had to pass a definition, not dependent on anything.

In D3D10+ however, you cannot create a vertex descriptor yourself. When you compile a vertex shader, you need to use the compiled bytecode (but NOT the compiled vertex shader object, not the same thing... after the shader is compiled and the shader object is created, you can discard the compiled bytecode) to create what is called an Input Layout.
So in the new API, you can ONLY create the input layout during shader compilation, which means the rules change significantly. It now actually 'belongs to' the shader.

The mesh would then be implemented as a new module which uses the vertex module. Then when your D3D10+ code comes along, you simply implement the vertex shader by extending the vertex module itself.


Except that D3D9 also had vertex shaders... they just worked differently from the ones in D3D10+.

No offense, but I really don't think you understand the situation here. My point is that when using a complex API such as D3D or OpenGL, the API dictates for a large part how your application should be designed. The API tells you how your data should be formatted, and where, when and how this data should be stored, passed on etc.
A wrong design may significantly impact performance or memory overhead, or may even make certain things impossible to implement at all.

Before you are able to make an engine design that can abstract away the (sometimes huge) differences between D3D9, D3D10+ and OpenGL, you first need to be quite at home with all of them, so you know WHAT to abstract. Concepts in one API may be entirely absent in another, or they may work in such a different way that it is almost impossible to abstract away the differences.

Edit: Perhaps I need to add this, since it doesn't seem immediately obvious by just looking at the version number...
DirectX versions are revolutionary, not evolutionary. Generally a new version introduces new COM interfaces as well. At the very least, by definition it is an entirely new API, since all typenames are different.
In some cases, the difference with the previous version is not that big, and a search-and-replace will take care of 90% of the API differences (eg D3D8->D3D9, D3D10->D3D11). In other cases, the API is pretty much completely different in every way, and there's little if any similarity with the previous version (D3D9->D3D10).
D3D10 was completely redesigned from scratch. So you can't just take a D3D9-codebase and 'update' it to D3D10. The two aren't even remotely similar. The difference is about as large as going from D3D9 to OpenGL.

Edit2: The fact that there are such differences between these APIs, which technically all run on the same hardware, already indicates that there is not just one possible design for such a system. Videocards are a moving target, the design for these APIs is partly the result of at what moment in time these APIs were designed. OpenGL was originally designed for SGI's computer systems, which were still mostly software renderers back then. It has been extended ever since, to account for new hardware acceleration features, but in general it is still the same design (on the outside).
Direct3D is updated at every major hardware architecture update. D3D9 dates from the first D3D9-videocards, the Radeon 9700 series. D3D10 is built around the first D3D10-videocards, the GeForce 8800 series. The hardware design is massively different, and the API design reflects that (even though the hardware and API are still designed to perform basically the same rendering operations, at a higher level).
Posted on 2011-07-24 05:05:51 by Scali
A Mesh just groups all the storage classes together so you have everything you need in order to draw that mesh on the given API.
Technically my Mesh is abstract.. you can just draw a mesh with one call... It's just that the implementation of this draw call (and partly the mesh itself, let alone the objects it groups together) will be massively different from one API to another.


Then in such a case, as I was trying to get across earlier, you implement each of those "massively different" parts as individual business classes which overload a generic that defines the requirements of any interface components and the outputs (if any) of the storage components.

Maybe I'm not reading you right, or maybe you're not reading me right. But that's kinda the point of the 3-Tier model. From what you just said, the business tier of your mesh varies from version to version, but as long as you use a well defined generic structure for your business tier, porting between the versions should just be a matter of rewriting the business tier and hot-plugging them as needed.

No, I didn't. A vertex is just a struct.
The vertex format descriptor indeed *was* a separate object (one dictated by D3D9). The thing is just that you have to store that vertex format *somewhere*, in order to describe what kind of data is in your mesh.
So, you store a reference of the vertex format descriptor in your mesh. After all, the mesh contains the vertex buffers. And in D3D9, the format descriptor was most closely related to the vertex buffers anyway (you could render without shaders, and then it STILL needed a format descriptor, obviously... it also had the legacy FVF flags still, which you used when creating a vertexbuffer).
But D3D10+ changes the rules there. Firstly, the vertexbuffer is now more abstract, just a buffer. Secondly, there's no fixed function anymore, so you HAVE to have shaders at all times. Thirdly, there's more validation built into the API at initialization time, rather than at runtime.


When you start talking about "stored (or transmitted) data" that's a key that you are needing a persistence tier. Say for example you wanted to write a chat server. The chat server would essentially handle the inputs the same way, it would interface with your system logger or stdout in the same way, but say that a change occurs. Say that some of your users have odd firewall configurations and they can't send certain types of packets. The business tier isn't just a "storage container" it acts as an abstract translation tier between whatever input/output medium you are using. So you could have you clients chatting over HTTP, SSH, FTP, or even custom protocols but the business tier wouldn't care because it's still receiving the same data because the persistence tier would translate as needed.

So I guess what I'm not understanding here is why, given that you create the vertex descriptor as a separate model whose presentation tier is connected to the business persistence tier of the mesh model, you can't just update the persistence tier of the mesh model to accommodate the changes made to the internal format of the vertex descriptor.

So in D3D9 the API would want a vertex buffer and a vertex descriptor, before a draw call... and if you then optionally used a shader, it would automatically verify if the shader was compatible with the vertex descriptor.
In D3D9 you could create this vertex descriptor at any time you wanted, you just had to pass a definition, not dependent on anything.

In D3D10+ however, you cannot create a vertex descriptor yourself. When you compile a vertex shader, you need to use the compiled bytecode (but NOT the compiled vertex shader object, not the same thing... after the shader is compiled and the shader object is created, you can discard the compiled bytecode) to create what is called an Input Layout.
So in the new API, you can ONLY create the input layout during shader compilation, which means the rules change significantly. It now actually 'belongs to' the shader.


Yeah, maybe it's my lack of game development knowledge, but is there a reason that you can't just call down to the shader from the vertex model's business view when the mesh tries to read in the vertex model? This would just be a small change to the vertex model from what I'm reading.

Except that D3D9 also had vertex shaders... they just worked differently from the ones in D3D10+.


The reusable models themselves change, but the connections between them stay pretty much the same unless you find your overall model to be unoptimized. I don't really see how this would be significant unless you were breaking design and using the shader model directly from within your business tier.

No offense, but I really don't think you understand the situation here.


Nor do I.  :shock:

My point is that when using a complex API such as D3D or OpenGL, the API dictates for a large part how your application should be designed. The API tells you how your data should be formatted, and where, when and how this data should be stored, passed on etc.
A wrong design may significantly impact performance or memory overhead, or may even make certain things impossible to implement at all.


I very much understand that, however. The only game I've ever done was a very simple FPS when I was working in a group in college. This was done in the 3-tier design using OpenGL, so I know for a fact that OpenGL can be easily adapted into a 3-tier model. Adversely, the largest corporation to be supporting the 3-tier model as a replacement for MVC is, well.. Microsoft. In fact, I'd be hard pressed to believe that they didn't use their favorite 3-Tier model (COM+) in the design of DirectX itself.

Before you are able to make an engine design that can abstract away the (sometimes huge) differences between D3D9, D3D10+ and OpenGL, you first need to be quite at home with all of them, so you know WHAT to abstract. Concepts in one API may be entirely absent in another, or they may work in such a different way that it is almost impossible to abstract away the differences.


Amen to that. I'm not saying that 3-tier architectures (a collection of interconnected 3-tier models) never has to be redesigned. The idea is that they RARELY have to be changed. Changing one node in the architecture, unless an extreme case occurs, rarely has any affect on the other nodes because the other nodes are designed to use only a specific part of the other nodes and it's the responsibility of the other node to provide them as such. If you do have to change the architecture itself, it means your overall design was flawed (or poorly implemented) to begin with. That's why it's so important to continue doing all those tedious documents that the newbs think are pointless. it prevents architecture redesign.

Edit: Perhaps I need to add this, since it doesn't seem immediately obvious by just looking at the version number...
DirectX versions are revolutionary, not evolutionary. Generally a new version introduces new COM interfaces as well. At the very least, by definition it is an entirely new API, since all typenames are different.
In some cases, the difference with the previous version is not that big, and a search-and-replace will take care of 90% of the API differences (eg D3D8->D3D9, D3D10->D3D11). In other cases, the API is pretty much completely different in every way, and there's little if any similarity with the previous version (D3D9->D3D10).
D3D10 was completely redesigned from scratch. So you can't just take a D3D9-codebase and 'update' it to D3D10. The two aren't even remotely similar. The difference is about as large as going from D3D9 to OpenGL.


Ah, so it does use Microsoft's 3-tier model. It might be possible, just a shot in the dark here, that D3D9 wasn't built using the 3-tier design style which is why Microsoft did a floor up redesign.

Edit2: The fact that there are such differences between these APIs, which technically all run on the same hardware, already indicates that there is not just one possible design for such a system. Videocards are a moving target, the design for these APIs is partly the result of at what moment in time these APIs were designed. OpenGL was originally designed for SGI's computer systems, which were still mostly software renderers back then. It has been extended ever since, to account for new hardware acceleration features, but in general it is still the same design (on the outside).
Direct3D is updated at every major hardware architecture update. D3D9 dates from the first D3D9-videocards, the Radeon 9700 series. D3D10 is built around the first D3D10-videocards, the GeForce 8800 series. The hardware design is massively different, and the API design reflects that (even though the hardware and API are still designed to perform basically the same rendering operations, at a higher level).


Again, the point of the 3-tier design is to make individual solutions to each of these differences in a way that they interconnect together in a seamless fashion which doesn't disturb the other models in the architecture.
Posted on 2011-07-24 13:26:21 by Synfire