Platform-agnostic renderer

Started by
50 comments, last by ATC 11 years, 7 months ago

True, but I think the design idea still stands. If you have a parent reference in every class, you can always navigate up the hierarchy if your platform requires something to carry out it's functionality.

I.e your ResourceLoader class might need to obtain a pointer to the X11 Display struct in order for it to create or load an image (i.e XCreatePixmap())

[see: Single Responsibility Principle, Separation of Concerns]

If your ResourceLoader can't fully encapsulate the process of loading resources, then you need to rethink your design. Of course it may need a reference to the underlying device, but it should never have to go searching for it - you should pass a reference to the device into the constructor.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Advertisement
+1 for ignoring any design decision Ogre3D did.

As for the discussion "should a mesh draw itself, i.e. should I have a function Mesh::Draw()?" the answer is strongly no. In a typical 3D scene, you never do anything just once. You never just draw a single mesh, you never just update the physics of a single rigid body, and so on. The most important design decision for a performant 3D engine is to allow batch, batch, batch! This means that you will need a centralized renderer that can control the render process, query the renderable objects, sort by state changes, and submit render calls as optimally as possible. The information required to do that is scene-wide, and inside a single Mesh::Draw() function you can't (or shouldn't) make these state optimization decisions. It's the renderer's role to know how to draw a mesh, and it's also the renderer's role to know how to do that as efficiently as possible with respect to all other content in the scene.
Hmm,

I think the Unreal Engine approach is descent.
The User can adjust at runtime in a Config file wich specific *.DLL (win32/win64) / *.SO (Linux/Unix/Mac) Renderer Driver should be used.
You can interface with an abstract Renderdriver proxy class and let the Renderdriver do its thing. Simple but effective and ellegant (just my 2 cents).
Here's how I'm currently doing things, thanks to some of the brilliant suggestions I've recieved here from our community's most brilliant and senior members... :)

Pseudo-code:

[source lang="csharp"] public class RenderOp
{
public string[] CmdString { get; set; }
public MeshData Mesh { get; set; }
public Material Material { get; set; }
};

public class RenderOpBatch
{
// Pseudo-implementation not shown to save space
};

public abstract class Renderer
{
RenderOpBatch currentBatch;
Queue<RenderOpBatch> RenderBatches;

public virtual void StartRenderBatch() {
currentBatch = new RenderOpBatch();
RenderBatches.Enqueue(currentBatch);
}

public virtual void QueueOp(RenderOp op) {
currentBatch.Add(op);
}

public abstract void FlushAllBatches();

// blah, blah, blah... you get the idea :)
};[/source]

That's not truly how I've implemented it, but just a pseudo-code expression of the idea. For instance, I don't really use Queue<T>, I use a custom collection type that allows me to choose LIFO, FIFO or custom sorting of batches and all sorts of stuff. Any comments/criticisms/suggestions concerning this concept?
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________
Ok... here's another thing I'm trying to work out: vertex types and input layouts (can't remember what the OpenGL counterpart of an input layout is called... usage hint, maybe?)...

I need to design a sub-system through which new vertex structures can be implemented beyond the common ones the engine will already offer, and the ones I do offer need to adhere to a clean, consistent format. It needs to be written so that the same data can be used to create an D3D "input layout" or an OpenGL "usage hint" (or whatever it's called) on-the-fly as the vertex data is pushed to the renderer. It's hard for me to decide on things sometimes because I'm not sure what parts/features of D3D and OpenGL are so seldomly used that they can just be cut out, and which people are going to be pissed if I don't let them have... Anyway, some of the ideas I have are:

The first thing to consider is how we designate what fields of a vertex are for (and how big they are). We could, like DirectX, just use a string (e.g., "POSITION"). Or we could use some type of enumeration, like this:

[source lang="csharp"] public enum VertexElements
{
NULL = 0x0000,
POSITION = 0x0001,
COLOR = 0x0002,
TEXCOORD = 0x0004,
NORMAL = 0x0008,
BINORMAL = 0x0016,
TANGENT = 0x0032
};[/source]

What might the pros/cons of each method be? And what would be a good way to represent the size of vertex fields without using a platform-specific enumeration like SlimDX's "Format" enum? Or is there yet another unthought-of way of doing this that would be superior to both?

Next, what would be the best way to implement a cohesive vertex typing system that can be broken-down and understood by virtually any type of renderer? I have some thoughts already, and I'll show you what ideas I'm toying with:

1) A common interface all vertex structures inherit from. For example:

[source lang="csharp"] [StructLayout(LayoutKind.Sequential)]
public interface IVertex
{
int SizeInBytes { get; }

VertexElements[] Elements { get; }

byte[] ToBytes();
};[/source]

All vertex types would implement that interface if such a method was used, and they would have to return a static value which is not part of the memory of an actual vertex instance on the stack (as that would throw things off).

2) Create a new struct/class (e.g., "VertexDescription") that houses a nice description of a vertex-type and tells you what's in its guts. The essence of it might look like this (incomplete example):

[source lang="csharp"] public class VertexLayout
{
int sizeInBytes;
VertexElements[] elements;
};[/source]

In addition to this structure, perhabs it might be an idea to implement a new enumeration type which replaces platform-specific enumerations like SlimDX's "Format" but offers the same data in a new way; potential even giving the size in bytes of an element as its own numerical value!?

[source lang="csharp"] public enum ElementFormat
{
byteX1 = 1,
byteX2 = 2,
byteX3 = 3,
byteX4 = 4,
shortX1 = 2,
shortX2 = 4,

// ...and so on...
};[/source]

Anyway, I hope the wisdom of the community can once again offer me some excellent ideas! smile.png

EDIT: The idea of assigning the enum values of "ElementFormat" the size on the element in bytes actually wont work because C# treats enums as numeric values and would not be able to distinguish between them. My bad, didn't think about that. Please disregard that erroneous idea.
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________
Hi ATC,


Here's how I'm currently doing things, thanks to some of the brilliant suggestions I've recieved here from our community's most brilliant and senior members... smile.png

Yeah, there are some amazing peoples out there smile.png



Pseudo-code:

[source lang="csharp"] public class RenderOp
{
public string[] CmdString { get; set; }
public MeshData Mesh { get; set; }
public Material Material { get; set; }
};

public class RenderOpBatch
{
// Pseudo-implementation not shown to save space
};

public abstract class Renderer
{
RenderOpBatch currentBatch;
Queue<RenderOpBatch> RenderBatches;

public virtual void StartRenderBatch() {
currentBatch = new RenderOpBatch();
RenderBatches.Enqueue(currentBatch);
}

public virtual void QueueOp(RenderOp op) {
currentBatch.Add(op);
}

public abstract void FlushAllBatches();

// blah, blah, blah... you get the idea smile.png
};[/source]
[/quote]

I think this pretty straith forward, But i think you can change your rederque with task based sheduling like (i recommended
the free OpenSource Version of Intel Thread building blocks). Then your Renderque can spread over multiple cores.
In TBB you have Namespaces and OOP Classes instead of dealing with native Posix or Winthreads so it is much
more easier - and - it is crossplatform and works with the Intel/Visual C/C++ and GNU GCC Compiler.

Peter

I think this pretty straith forward, But i think you can change your rederque with task based sheduling like (i recommended
the free OpenSource Version of Intel Thread building blocks). Then your Renderque can spread over multiple cores.
In TBB you have Namespaces and OOP Classes instead of dealing with native Posix or Winthreads so it is much
more easier - and - it is crossplatform and works with the Intel/Visual C/C++ and GNU GCC Compiler.

Peter


To be implemented in the D3D11-specific renderer implementation and its OpenGL counterparts. :-)

The engine already contains a robust and "battle-proven" sub-library I call the "HAI" (Hardware Abstraction Interface). It can pull all of the important information about a machine's graphics hardware from DXGI or OpenGL, and it also finds (among other things) the amount of CPU cores, total physical memory (RAM), HDD space, logical drives, etc. My D3D11 renderer implementation will of course use that to allocate rendering tasks to dynamically-generated threads, the number of which is selected to be optimal for the CPU speed and the amount of cores.
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________


To be implemented in the D3D11-specific renderer implementation and its OpenGL counterparts. :-)

The engine already contains a robust and "battle-proven" sub-library I call the "HAI" (Hardware Abstraction Interface). It can pull all of the important information about a machine's graphics hardware from DXGI or OpenGL, and it also finds (among other things) the amount of CPU cores, total physical memory (RAM), HDD space, logical drives, etc. My D3D11 renderer implementation will of course use that to allocate rendering tasks to dynamically-generated threads, the number of which is selected to be optimal for the CPU speed and the amount of cores.


Wow, i think this was a lot of work and many ifdefs. (-;

I thnik allways the solution is what fit for your needs and if you have selfmade code you understand you get even more productive
enstead of learning lots of diffrent tools and Version changes.

Peter

Wow, i think this was a lot of work and many ifdefs. (-;

I thnik allways the solution is what fit for your needs and if you have selfmade code you understand you get even more productive
enstead of learning lots of diffrent tools and Version changes.

Peter


It might surprise you, then, to hear there are actually very few ifdefs in the entire code-base. Furthermore, the engine can switch between DirectX versions, OpenGL versions and entire rendering APIs (e.g., DirectX to OpenGL) while it runs. :-)

Most of the #ifs and #elses have only to do with Debug vs Release builds and handling exceptions; often in resource disposal code (no sense in throwing an exception in a release build if the application can continue or is shutting down, for example).
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________
Well, I think I got the vertex type paradox solved in a way that works great and seems perfect. Things are looking good thanks to you guys' input and brilliant advice.
_______________________________________________________________________________
CEO & Lead Developer at ATCWARE™
"Project X-1"; a 100% managed, platform-agnostic game & simulation engine

Please visit our new forums and help us test them and break the ice!
___________________________________________________________________________________

This topic is closed to new replies.

Advertisement