st/nine: Implement dummy vbo behaviour when vs is missing inputs
authorTiziano Bacocco <tizbac2@gmail.com>
Fri, 30 Jan 2015 12:51:44 +0000 (13:51 +0100)
committerAxel Davy <axel.davy@ens.fr>
Thu, 5 Feb 2015 23:07:20 +0000 (00:07 +0100)
Use a dummy vertex buffer object when vs inputs have no corresponding
entries in the vertex declaration. This dummy buffer will give to the
shader float4(0,0,0,0).

This fixes several artifacts on some games.

Signed-off-by: Axel Davy <axel.davy@ens.fr>
Signed-off-by: Tiziano Bacocco <tizbac2@gmail.com>
src/gallium/state_trackers/nine/device9.c
src/gallium/state_trackers/nine/device9.h
src/gallium/state_trackers/nine/nine_state.c
src/gallium/state_trackers/nine/nine_state.h

index e0524644f8ade6c9d14afe3358a3dbc9a81c51d2..78e148bca772baeddb5c91b3012f51eb9b98d025 100644 (file)
@@ -221,6 +221,42 @@ NineDevice9_ctor( struct NineDevice9 *This,
         NineUnknown_ConvertRefToBind(NineUnknown(This->state.rt[i]));
     }
 
+    /* Initialize a dummy VBO to be used when a a vertex declaration does not
+     * specify all the inputs needed by vertex shader, on win default behavior
+     * is to pass 0,0,0,0 to the shader */
+    {
+        struct pipe_transfer *transfer;
+        struct pipe_resource tmpl;
+        struct pipe_box box;
+        unsigned char *data;
+
+        tmpl.target = PIPE_BUFFER;
+        tmpl.format = PIPE_FORMAT_R8_UNORM;
+        tmpl.width0 = 16; /* 4 floats */
+        tmpl.height0 = 1;
+        tmpl.depth0 = 1;
+        tmpl.array_size = 1;
+        tmpl.last_level = 0;
+        tmpl.nr_samples = 0;
+        tmpl.usage = PIPE_USAGE_DEFAULT;
+        tmpl.bind = PIPE_BIND_VERTEX_BUFFER | PIPE_BIND_TRANSFER_WRITE;
+        tmpl.flags = 0;
+        This->dummy_vbo = pScreen->resource_create(pScreen, &tmpl);
+
+        if (!This->dummy_vbo)
+            return D3DERR_OUTOFVIDEOMEMORY;
+
+        u_box_1d(0, 16, &box);
+        data = This->pipe->transfer_map(This->pipe, This->dummy_vbo, 0,
+                                        PIPE_TRANSFER_WRITE |
+                                        PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE,
+                                        &box, &transfer);
+        assert(data);
+        assert(transfer);
+        memset(data, 0, 16);
+        This->pipe->transfer_unmap(This->pipe, transfer);
+    }
+
     This->cursor.software = FALSE;
     This->cursor.hotspot.x = -1;
     This->cursor.hotspot.y = -1;
@@ -387,6 +423,7 @@ NineDevice9_dtor( struct NineDevice9 *This )
     pipe_resource_reference(&This->dummy_texture, NULL);
     pipe_resource_reference(&This->constbuf_vs, NULL);
     pipe_resource_reference(&This->constbuf_ps, NULL);
+    pipe_resource_reference(&This->dummy_vbo, NULL);
     FREE(This->state.vs_const_f);
     FREE(This->state.ps_const_f);
     FREE(This->state.vs_lconstf_temp);
index 54da9e3a6246eb8ad8207c341c955e2039f3acd5..f412088ca08bfba642a4fff86ff563937df1457b 100644 (file)
@@ -123,6 +123,10 @@ struct NineDevice9
     struct nine_range_pool range_pool;
 
     struct hud_context *hud; /* NULL if hud is disabled */
+
+    /* dummy vbo (containing 0 0 0 0) to bind if vertex shader input
+     * is not bound to anything by the vertex declaration */
+    struct pipe_resource *dummy_vbo;
 };
 static INLINE struct NineDevice9 *
 NineDevice9( void *data )
index ba758176f494f6155af76b02e7876fff27addb9f..495cc862b2a8eae2c0b8e51d7eb68597d73c1c90 100644 (file)
@@ -191,26 +191,55 @@ update_vertex_elements(struct NineDevice9 *device)
     const struct NineVertexShader9 *vs;
     unsigned n, b, i;
     int index;
+    char vdecl_index_map[16]; /* vs->num_inputs <= 16 */
+    char used_streams[device->caps.MaxStreams];
+    int dummy_vbo_stream = -1;
+    BOOL need_dummy_vbo = FALSE;
     struct pipe_vertex_element ve[PIPE_MAX_ATTRIBS];
 
     state->stream_usage_mask = 0;
-
+    memset(vdecl_index_map, -1, 16);
+    memset(used_streams, 0, device->caps.MaxStreams);
     vs = device->state.vs ? device->state.vs : device->ff.vs;
 
     if (!vdecl) /* no inputs */
         return;
-    for (n = 0; n < vs->num_inputs; ++n) {
-        DBG("looking up input %u (usage %u) from vdecl(%p)\n",
-            n, vs->input_map[n].ndecl, vdecl);
 
-        index = -1;
-        for (i = 0; i < vdecl->nelems; i++) {
-            if (vdecl->usage_map[i] == vs->input_map[n].ndecl) {
-                index = i;
+    if (vdecl) {
+        for (n = 0; n < vs->num_inputs; ++n) {
+            DBG("looking up input %u (usage %u) from vdecl(%p)\n",
+                n, vs->input_map[n].ndecl, vdecl);
+
+            for (i = 0; i < vdecl->nelems; i++) {
+                if (vdecl->usage_map[i] == vs->input_map[n].ndecl) {
+                    vdecl_index_map[n] = i;
+                    used_streams[vdecl->elems[i].vertex_buffer_index] = 1;
+                    break;
+                }
+            }
+            if (vdecl_index_map[n] < 0)
+                need_dummy_vbo = TRUE;
+        }
+    } else {
+        /* No vertex declaration. Likely will never happen in practice,
+         * but we need not crash on this */
+        need_dummy_vbo = TRUE;
+    }
+
+    if (need_dummy_vbo) {
+        for (i = 0; i < device->caps.MaxStreams; i++ ) {
+            if (!used_streams[i]) {
+                dummy_vbo_stream = i;
                 break;
             }
         }
+    }
+    /* there are less vertex shader inputs than stream slots,
+     * so if we need a slot for the dummy vbo, we should have found one */
+    assert (!need_dummy_vbo || dummy_vbo_stream != -1);
 
+    for (n = 0; n < vs->num_inputs; ++n) {
+        index = vdecl_index_map[n];
         if (index >= 0) {
             ve[n] = vdecl->elems[index];
             b = ve[n].vertex_buffer_index;
@@ -219,18 +248,27 @@ update_vertex_elements(struct NineDevice9 *device)
             if (state->stream_freq[b] & D3DSTREAMSOURCE_INSTANCEDATA)
                 ve[n].instance_divisor = state->stream_freq[b] & 0x7FFFFF;
         } else {
-            /* TODO: msdn doesn't specify what should happen when the vertex
-             * declaration doesn't match the vertex shader inputs.
-             * Some websites say the code will pass but nothing will get rendered.
-             * We should check and implement the correct behaviour. */
-            /* Put PIPE_FORMAT_NONE.
-             * Some drivers (r300) are very unhappy with that */
-            ve[n].src_format = PIPE_FORMAT_NONE;
+            /* if the vertex declaration is incomplete compared to what the
+             * vertex shader needs, we bind a dummy vbo with 0 0 0 0.
+             * This is not precised by the spec, but is the behaviour
+             * tested on win */
+            ve[n].vertex_buffer_index = dummy_vbo_stream;
+            ve[n].src_format = PIPE_FORMAT_R32G32B32A32_FLOAT;
             ve[n].src_offset = 0;
             ve[n].instance_divisor = 0;
-            ve[n].vertex_buffer_index = 0;
         }
     }
+
+    if (state->dummy_vbo_bound_at != dummy_vbo_stream) {
+        if (state->dummy_vbo_bound_at >= 0)
+            state->changed.vtxbuf |= 1 << state->dummy_vbo_bound_at;
+        if (dummy_vbo_stream >= 0) {
+            state->changed.vtxbuf |= 1 << dummy_vbo_stream;
+            state->vbo_bound_done = FALSE;
+        }
+        state->dummy_vbo_bound_at = dummy_vbo_stream;
+    }
+
     cso_set_vertex_elements(device->cso, vs->num_inputs, ve);
 
     state->changed.stream_freq = 0;
@@ -566,6 +604,7 @@ update_vertex_buffers(struct NineDevice9 *device)
 {
     struct pipe_context *pipe = device->pipe;
     struct nine_state *state = &device->state;
+    struct pipe_vertex_buffer dummy_vtxbuf;
     uint32_t mask = state->changed.vtxbuf;
     unsigned i;
     unsigned start;
@@ -573,6 +612,19 @@ update_vertex_buffers(struct NineDevice9 *device)
 
     DBG("mask=%x\n", mask);
 
+    if (state->dummy_vbo_bound_at >= 0) {
+        if (!state->vbo_bound_done) {
+            dummy_vtxbuf.buffer = device->dummy_vbo;
+            dummy_vtxbuf.stride = 0;
+            dummy_vtxbuf.user_buffer = NULL;
+            dummy_vtxbuf.buffer_offset = 0;
+            pipe->set_vertex_buffers(pipe, state->dummy_vbo_bound_at,
+                                     1, &dummy_vtxbuf);
+            state->vbo_bound_done = TRUE;
+        }
+        mask &= ~(1 << state->dummy_vbo_bound_at);
+    }
+
     for (i = 0; mask; mask >>= 1, ++i) {
         if (mask & 1) {
             if (!count)
@@ -580,8 +632,8 @@ update_vertex_buffers(struct NineDevice9 *device)
             ++count;
         } else {
             if (count)
-                pipe->set_vertex_buffers(pipe,
-                                         start, count, &state->vtxbuf[start]);
+                pipe->set_vertex_buffers(pipe, start, count,
+                                         &state->vtxbuf[start]);
             count = 0;
         }
     }
@@ -1116,6 +1168,11 @@ nine_state_set_defaults(struct NineDevice9 *device, const D3DCAPS9 *caps,
 
     for (s = 0; s < Elements(state->changed.sampler); ++s)
         state->changed.sampler[s] = ~0;
+
+    if (!is_reset) {
+        state->dummy_vbo_bound_at = -1;
+        state->vbo_bound_done = FALSE;
+    }
 }
 
 void
index 25ca3dba3051eb3cd1f0f4aea2ef5a532cb4ca5e..191695953388206286cf67889bbc6cf0915c59d6 100644 (file)
@@ -178,6 +178,9 @@ struct nine_state
     uint8_t bound_samplers_mask_vs;
     uint16_t bound_samplers_mask_ps;
 
+    int dummy_vbo_bound_at; /* -1 = not bound , >= 0 = bound index */
+    boolean vbo_bound_done;
+
     struct {
         struct {
             uint32_t group;