Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Direct2D Depth Buffer

I need to draw a list of shapes and I am using Direct2D. I get the list of shapes from a file. The list is sorted and the order of the elements inside the file represents the order these shapes will be drawn. So, if for example the file specifies two rectangle in the same position and with the same sizes, only the second one will be visible (since the first will be overwritten).

Given my list of shapes I proceede to its drawing in the following way:

list<Shape> shapes;

for (const auto& shape : shapes)
   shape.draw();

It is straightforward to see that if I have two shapes I cannot invert the order of the drawing operations, and this means that I must be sure that shape2 will be always drawn after shape1 and so on. Follows that I can not use multiple threads to draw my shapes, and this is a huge disadvantage in terms of performances.

I read that Direct3D supports the depth buffer (or z-buffer), which specifies for each pixel its z-coordinate, such that only the "visible" pixels (the onces closer to the viewer) will be drawn, regardless of the order in which the shapes are drawn. And I have the depth information of each shape when I read the file.

Is there a way to use the depth buffer in Direct2D, or a similar technique which allows me the use of multiple threads to draw my shapes?

like image 486
Nick Avatar asked May 06 '16 14:05

Nick


1 Answers

Is there a way to use the depth buffer in Direct2D, or a similar technique which allows me the use of multiple threads to draw my shapes?

The answer here is no. Althought the Direct2D library is built on top of Direct3D, it doesn't provide the user such feature through the API, since the primitives you can draw are only described by two-dimensional coordinates. The last primitive you draw to the render target is ensured to be visible, so no depth testing is taking place. Also, the depth buffer in Direct3D doesn't have much to do with multi-threading on the CPU side.

Also note that even if you are issuing drawing commands using multiple threads they will be serialized by the Direct3D driver and performed sequentially. Some newer graphical APIs like Direct3D 12 and Vulkan does provide multithreaded drivers which allows you to effectively draw different content from different threads, but they come with higher complexity.

So eventually if you stick to Direct2D you are left with the option of drawing each shape sequentially using a single thread.

But what can be done is that you can eliminate the effectively occluded shapes by testing for occlusion each shape against all others. So the occluded shapes can be discarded from the list and never rendered at all. The trick here is that some of the shapes does not fill their bounds rect entirely, due to transparent regions (like text) or if the shape is a complex polygon. Such shapes can not be easily tested or will need more complex algorithms.

So you have to iterate thourgh all shapes and if the current shape is a rectangle only then perform occlusion testing with all previous shapes' bounds rects.

The following code should be considered pseudo-code, it is intended just to demonstrates the idea.

#define RECTANGLE 0
#define TEXT      1
#define TRIANGLE  2
//etc

typedef struct {
    int type; //We have a type field
    Rect bounds_rect; //Bounds rect
    Rect coordinates; //Coordinates, which count vary according to shape type
    //Probably you have many other fields here
} Shape;

//We have all shapes in a vector
std::vector<Shape> shapes;

Iterate all shapes.

for (int i=1; i<shapes.size; i++) {
  if(shape[i].type != RECTANGLE) {
    //We will not perform testing if the current shape is not rectangle.
    continue; 
  }

  for(int j=0; j<i; j++) {
    if(isOccluded(&shape[j], &shape[i])) {
      //shape[j] is totally invisible, so remove it from 'shapes' list
    }
  }
}

Occlusion testing is something like this

bool isOccluded(Shape *a, Shape *b) {
  return (a.bounds_rect.left > b.coordinates.left && a.bounds_rect.right < b.coordinates.right &&
          a.bounds_rect.top > b.coordinates.to && a.bounds_rect.bottom < b.coordinates.bottom);
}

And you don't have to iterate all shapes with a single thread, you can create multiple threads to perform tests for different parts of the shape list. Of course you will need some locking technique like mutex when deleting shapes from the list, but that is another topic.

like image 184
Anton Angelov Avatar answered Oct 12 '22 08:10

Anton Angelov