[v3 1/3] d3dx9: Don't apply unmodified states in CommitChanges.

Paul Gofman gofmanp at gmail.com
Fri Mar 17 06:00:41 CDT 2017


Signed-off-by: Paul Gofman <gofmanp at gmail.com>
---
v3:
    - changed patch title;
    - removed redundant walk_parameter_tree() in clear_dirty_params();
    - used select_param variable instead of reusing 'param' in d3dx9_get_param_value_ptr();
    - changed ERR to WARN on out of bounds selector access in d3dx9_get_param_value_ptr();
    - return E_FAIL instead of D3DERR_INVALIDCALL on out of bounds selector access in d3dx9_get_param_value_ptr();
    - changed comment in d3dx9_apply_state();
    - return D3D_OK only on E_FAIL from d3dx9_get_param_value_ptr() and error otherwise in d3dx9_apply_state();
    - changed d3dx9_get_param_value_ptr() to return E_FAIL for missing preshader for FXLC paramaters, otherwise
        BeginPass() totally fails if any fxlc parameter had unsupported preshader, which breaks existing tests and
        is not the designed behaviour;
    - keep TRACE only for D3D_OK on E_FAIL return case in d3dx9_apply_state();
    - don't set all preshader input constants when not required in d3dx_param_eval_set_shader_constants();
    - don't set all preshader input constants when not required in d3dx_evaluate_parameter().
---
 dlls/d3dx9_36/d3dx9_private.h |  10 ++-
 dlls/d3dx9_36/effect.c        | 141 +++++++++++++++++++++++++++---------------
 dlls/d3dx9_36/preshader.c     |  42 ++++++++++---
 3 files changed, 133 insertions(+), 60 deletions(-)

diff --git a/dlls/d3dx9_36/d3dx9_private.h b/dlls/d3dx9_36/d3dx9_private.h
index 27b91a7..9b81db7 100644
--- a/dlls/d3dx9_36/d3dx9_private.h
+++ b/dlls/d3dx9_36/d3dx9_private.h
@@ -222,6 +222,11 @@ struct d3dx_parameter
     DWORD *dirty_flag_ptr;
 };
 
+static inline BOOL is_param_dirty(struct d3dx_parameter *param)
+{
+    return (*param->dirty_flag_ptr & PARAMETER_FLAG_DIRTY);
+}
+
 struct d3dx9_base_effect;
 
 struct d3dx_parameter *get_parameter_by_name(struct d3dx9_base_effect *base,
@@ -231,8 +236,9 @@ void d3dx_create_param_eval(struct d3dx9_base_effect *base_effect, void *byte_co
         unsigned int byte_code_size, D3DXPARAMETER_TYPE type, struct d3dx_param_eval **peval) DECLSPEC_HIDDEN;
 void d3dx_free_param_eval(struct d3dx_param_eval *peval) DECLSPEC_HIDDEN;
 HRESULT d3dx_evaluate_parameter(struct d3dx_param_eval *peval,
-        const struct d3dx_parameter *param, void *param_value) DECLSPEC_HIDDEN;
+        const struct d3dx_parameter *param, void *param_value, BOOL update_all) DECLSPEC_HIDDEN;
 HRESULT d3dx_param_eval_set_shader_constants(struct IDirect3DDevice9 *device,
-        struct d3dx_param_eval *peval) DECLSPEC_HIDDEN;
+        struct d3dx_param_eval *peval, BOOL update_all) DECLSPEC_HIDDEN;
+BOOL is_param_eval_input_dirty(struct d3dx_param_eval *peval) DECLSPEC_HIDDEN;
 
 #endif /* __WINE_D3DX9_PRIVATE_H */
diff --git a/dlls/d3dx9_36/effect.c b/dlls/d3dx9_36/effect.c
index 849e832..92a54b7 100644
--- a/dlls/d3dx9_36/effect.c
+++ b/dlls/d3dx9_36/effect.c
@@ -172,6 +172,9 @@ struct ID3DXEffectImpl
     struct d3dx_pass *active_pass;
     BOOL started;
     DWORD flags;
+
+    D3DLIGHT9 current_light[8];
+    D3DMATERIAL9 current_material;
 };
 
 struct ID3DXEffectCompilerImpl
@@ -1339,6 +1342,14 @@ static void set_dirty(struct d3dx_parameter *param)
     *param->dirty_flag_ptr |= PARAMETER_FLAG_DIRTY;
 }
 
+static void clear_dirty_params(struct d3dx9_base_effect *base)
+{
+    unsigned int i;
+
+    for (i = 0; i < base->parameter_count; ++i)
+        *base->parameters[i].dirty_flag_ptr &= ~PARAMETER_FLAG_DIRTY;
+}
+
 static HRESULT d3dx9_base_effect_set_value(struct d3dx9_base_effect *base,
         D3DXHANDLE parameter, const void *data, UINT bytes)
 {
@@ -2537,17 +2548,20 @@ static HRESULT d3dx9_base_effect_set_array_range(struct d3dx9_base_effect *base,
 }
 
 static HRESULT d3dx9_get_param_value_ptr(struct ID3DXEffectImpl *effect, struct d3dx_pass *pass,
-        struct d3dx_state *state, void **param_value, struct d3dx_parameter **out_param)
+        struct d3dx_state *state, void **param_value, struct d3dx_parameter **out_param,
+        BOOL update_all, BOOL *param_dirty)
 {
     struct d3dx_parameter *param = &state->parameter;
 
     *param_value = NULL;
     *out_param = NULL;
+    *param_dirty = FALSE;
 
     switch (state->type)
     {
         case ST_PARAMETER:
             param = param->referenced_param;
+            *param_dirty = is_param_dirty(param);
             /* fallthrough */
         case ST_CONSTANT:
             *out_param = param;
@@ -2559,26 +2573,38 @@ static HRESULT d3dx9_get_param_value_ptr(struct ID3DXEffectImpl *effect, struct
             static const struct d3dx_parameter array_idx_param =
                 {NULL, NULL, NULL, D3DXPC_SCALAR, D3DXPT_INT, 1, 1, 0, 0, 0, 0, sizeof(array_idx)};
             HRESULT hr;
+            struct d3dx_parameter *ref_param, *selected_param;
 
             if (!param->param_eval)
             {
                 FIXME("Preshader structure is null.\n");
                 return D3DERR_INVALIDCALL;
             }
-            if (FAILED(hr = d3dx_evaluate_parameter(param->param_eval, &array_idx_param, &array_idx)))
-                return hr;
+            if (update_all || is_param_eval_input_dirty(param->param_eval))
+            {
+                if (FAILED(hr = d3dx_evaluate_parameter(param->param_eval, &array_idx_param,
+                        &array_idx, update_all)))
+                    return hr;
+            }
+            else
+            {
+                array_idx = state->index;
+            }
+            ref_param = param->referenced_param;
+            TRACE("Array index %u, stored array index %u, element_count %u.\n", array_idx, state->index,
+                    ref_param->element_count);
 
-            param = param->referenced_param;
-            TRACE("Array index %u.\n", array_idx);
-            if (array_idx >= param->element_count)
+            if (array_idx >= ref_param->element_count)
             {
-                ERR("Computed array index %u is out of bound %u.\n", array_idx, param->element_count);
-                return D3DERR_INVALIDCALL;
+                WARN("Computed array index %u is out of bound %u.\n", array_idx, ref_param->element_count);
+                return E_FAIL;
             }
-            param = &param->members[array_idx];
+            selected_param = &ref_param->members[array_idx];
+            *param_dirty = state->index != array_idx || is_param_dirty(selected_param);
+            state->index = array_idx;
 
-            *param_value = param->data;
-            *out_param = param;
+            *param_value = selected_param->data;
+            *out_param = selected_param;
             return D3D_OK;
         }
         case ST_FXLC:
@@ -2586,12 +2612,18 @@ static HRESULT d3dx9_get_param_value_ptr(struct ID3DXEffectImpl *effect, struct
             {
                 *out_param = param;
                 *param_value = param->data;
-                return d3dx_evaluate_parameter(param->param_eval, param, *param_value);
+                if (update_all || is_param_eval_input_dirty(param->param_eval))
+                {
+                    *param_dirty = TRUE;
+                    return d3dx_evaluate_parameter(param->param_eval, param, *param_value, update_all);
+                }
+                else
+                    return D3D_OK;
             }
             else
             {
                 FIXME("No preshader for FXLC parameter.\n");
-                return D3DERR_INVALIDCALL;
+                return E_FAIL;
             }
     }
     return E_NOTIMPL;
@@ -2764,10 +2796,10 @@ static HRESULT d3dx_set_shader_const_state(IDirect3DDevice9 *device, enum SHADER
 }
 
 static HRESULT d3dx9_apply_state(struct ID3DXEffectImpl *effect, struct d3dx_pass *pass,
-        struct d3dx_state *state, unsigned int parent_index);
+        struct d3dx_state *state, unsigned int parent_index, BOOL update_all);
 
 static HRESULT d3dx_set_shader_constants(struct ID3DXEffectImpl *effect, struct d3dx_pass *pass,
-        struct d3dx_parameter *param, BOOL vs)
+        struct d3dx_parameter *param, BOOL vs, BOOL update_all)
 {
     IDirect3DDevice9 *device = effect->device;
     HRESULT hr, ret;
@@ -2781,7 +2813,7 @@ static HRESULT d3dx_set_shader_constants(struct ID3DXEffectImpl *effect, struct
         FIXME("param_eval structure is null.\n");
         return D3DERR_INVALIDCALL;
     }
-    if (FAILED(hr = d3dx_param_eval_set_shader_constants(device, param->param_eval)))
+    if (FAILED(hr = d3dx_param_eval_set_shader_constants(device, param->param_eval, update_all)))
         return hr;
     params = param->param_eval->shader_inputs.inputs_param;
     cdesc = param->param_eval->shader_inputs.inputs;
@@ -2799,7 +2831,7 @@ static HRESULT d3dx_set_shader_constants(struct ID3DXEffectImpl *effect, struct
             for (j = 0; j < sampler->state_count; ++j)
             {
                 if (FAILED(hr = d3dx9_apply_state(effect, pass, &sampler->states[j],
-                        cdesc[i].RegisterIndex + (vs ? D3DVERTEXTEXTURESAMPLER0 : 0))))
+                        cdesc[i].RegisterIndex + (vs ? D3DVERTEXTEXTURESAMPLER0 : 0), update_all)))
                     ret = hr;
             }
         }
@@ -2808,17 +2840,34 @@ static HRESULT d3dx_set_shader_constants(struct ID3DXEffectImpl *effect, struct
 }
 
 static HRESULT d3dx9_apply_state(struct ID3DXEffectImpl *effect, struct d3dx_pass *pass,
-        struct d3dx_state *state, unsigned int parent_index)
+        struct d3dx_state *state, unsigned int parent_index, BOOL update_all)
 {
     IDirect3DDevice9 *device = effect->device;
     struct d3dx_parameter *param;
     void *param_value;
+    BOOL param_dirty;
     HRESULT hr;
 
     TRACE("operation %u, index %u, type %u.\n", state->operation, state->index, state->type);
-    if (FAILED(hr = d3dx9_get_param_value_ptr(effect, pass, state, &param_value, &param)))
-        /* Native d3dx returns D3D_OK from BeginPass or Commit involving out of bounds array
-         * access and does not touch affected state. */
+    if (FAILED(hr = d3dx9_get_param_value_ptr(effect, pass, state, &param_value, &param,
+            update_all, &param_dirty)))
+    {
+        if (hr == E_FAIL)
+        {
+            /* Native d3dx returns D3D_OK from BeginPass or Commit involving out of bounds array
+             * access and does not touch affected state, except for BeginPass when the out of bounds
+             * array index depends on dirty parameters. The latter case is supposed to return E_FAIL
+             * but is currently TODO. */
+            TRACE("returning D3D_OK on E_FAIL got from d3dx9_get_param_value_ptr().\n");
+            return D3D_OK;
+        }
+        return hr;
+    }
+
+    if (!(update_all || param_dirty
+            || state_table[state->operation].class == SC_VERTEXSHADER
+            || state_table[state->operation].class == SC_PIXELSHADER
+            || state_table[state->operation].class == SC_SETSAMPLER))
         return D3D_OK;
 
     switch (state_table[state->operation].class)
@@ -2855,7 +2904,7 @@ static HRESULT d3dx9_apply_state(struct ID3DXEffectImpl *effect, struct d3dx_pas
             ret = D3D_OK;
             for (i = 0; i < sampler->state_count; i++)
             {
-                if (FAILED(hr = d3dx9_apply_state(effect, pass, &sampler->states[i], state->index)))
+                if (FAILED(hr = d3dx9_apply_state(effect, pass, &sampler->states[i], state->index, update_all)))
                     ret = hr;
             }
             return ret;
@@ -2871,17 +2920,19 @@ static HRESULT d3dx9_apply_state(struct ID3DXEffectImpl *effect, struct d3dx_pas
         }
         case SC_VERTEXSHADER:
             TRACE("%s, shader %p.\n", state_table[state->operation].name, *(IDirect3DVertexShader9 **)param_value);
-            if (FAILED(hr = IDirect3DDevice9_SetVertexShader(device, *(IDirect3DVertexShader9 **)param_value)))
+            if ((update_all || param_dirty)
+                    && FAILED(hr = IDirect3DDevice9_SetVertexShader(device, *(IDirect3DVertexShader9 **)param_value)))
                 ERR("Could not set vertex shader, hr %#x.\n", hr);
             else if (*(IDirect3DVertexShader9 **)param_value)
-                hr = d3dx_set_shader_constants(effect, pass, param, TRUE);
+                hr = d3dx_set_shader_constants(effect, pass, param, TRUE, update_all || param_dirty);
             return hr;
         case SC_PIXELSHADER:
             TRACE("%s, shader %p.\n", state_table[state->operation].name, *(IDirect3DPixelShader9 **)param_value);
-            if (FAILED(hr = IDirect3DDevice9_SetPixelShader(device, *(IDirect3DPixelShader9 **)param_value)))
+            if ((update_all || param_dirty)
+                    && FAILED(hr = IDirect3DDevice9_SetPixelShader(device, *(IDirect3DPixelShader9 **)param_value)))
                 ERR("Could not set pixel shader, hr %#x.\n", hr);
             else if (*(IDirect3DPixelShader9 **)param_value)
-                hr = d3dx_set_shader_constants(effect, pass, param, FALSE);
+                hr = d3dx_set_shader_constants(effect, pass, param, FALSE, update_all || param_dirty);
             return hr;
         case SC_TRANSFORM:
             TRACE("%s, state %u.\n", state_table[state->operation].name, state->index);
@@ -2892,31 +2943,19 @@ static HRESULT d3dx9_apply_state(struct ID3DXEffectImpl *effect, struct d3dx_pas
             return IDirect3DDevice9_LightEnable(device, state->index, *(BOOL *)param_value);
         case SC_LIGHT:
         {
-            D3DLIGHT9 light;
-
             TRACE("%s, index %u, op %u.\n", state_table[state->operation].name, state->index,
                     state_table[state->operation].op);
-            if (FAILED(hr = IDirect3DDevice9_GetLight(device, state->index, &light)))
-            {
-                WARN("Could not get light, hr %#x.\n", hr);
-                memset(&light, 0, sizeof(light));
-            }
-            d3dx9_set_light_parameter(state_table[state->operation].op, &light, param_value);
-            return IDirect3DDevice9_SetLight(device, state->index, &light);
+            d3dx9_set_light_parameter(state_table[state->operation].op,
+                    &effect->current_light[state->index], param_value);
+            return IDirect3DDevice9_SetLight(device, state->index, &effect->current_light[state->index]);
         }
         case SC_MATERIAL:
         {
-            D3DMATERIAL9 material;
-
             TRACE("%s, index %u, op %u.\n", state_table[state->operation].name, state->index,
                     state_table[state->operation].op);
-            if (FAILED(hr = IDirect3DDevice9_GetMaterial(device, &material)))
-            {
-                WARN("Could not get material, hr %#x.\n", hr);
-                memset(&material, 0, sizeof(material));
-            }
-            d3dx9_set_material_parameter(state_table[state->operation].op, &material, param_value);
-            return IDirect3DDevice9_SetMaterial(device, &material);
+            d3dx9_set_material_parameter(state_table[state->operation].op,
+                    &effect->current_material, param_value);
+            return IDirect3DDevice9_SetMaterial(device, &effect->current_material);
         }
         case SC_NPATCHMODE:
             TRACE("%s, nsegments %f.\n", state_table[state->operation].name, *(float *)param_value);
@@ -2933,7 +2972,7 @@ static HRESULT d3dx9_apply_state(struct ID3DXEffectImpl *effect, struct d3dx_pas
     return D3D_OK;
 }
 
-static HRESULT d3dx9_apply_pass_states(struct ID3DXEffectImpl *effect, struct d3dx_pass *pass)
+static HRESULT d3dx9_apply_pass_states(struct ID3DXEffectImpl *effect, struct d3dx_pass *pass, BOOL update_all)
 {
     unsigned int i;
     HRESULT ret;
@@ -2945,12 +2984,13 @@ static HRESULT d3dx9_apply_pass_states(struct ID3DXEffectImpl *effect, struct d3
     {
         HRESULT hr;
 
-        if (FAILED(hr = d3dx9_apply_state(effect, pass, &pass->states[i], ~0u)))
+        if (FAILED(hr = d3dx9_apply_state(effect, pass, &pass->states[i], ~0u, update_all)))
         {
             WARN("Error applying state, hr %#x.\n", hr);
             ret = hr;
         }
     }
+    clear_dirty_params(&effect->base_effect);
     return ret;
 }
 
@@ -3736,7 +3776,7 @@ static HRESULT WINAPI ID3DXEffectImpl_Begin(ID3DXEffect *iface, UINT *passes, DW
                 if (FAILED(hr = IDirect3DDevice9_BeginStateBlock(effect->device)))
                     ERR("BeginStateBlock failed, hr %#x.\n", hr);
                 for (i = 0; i < technique->pass_count; i++)
-                    d3dx9_apply_pass_states(effect, &technique->passes[i]);
+                    d3dx9_apply_pass_states(effect, &technique->passes[i], TRUE);
                 if (FAILED(hr = IDirect3DDevice9_EndStateBlock(effect->device, &technique->saved_state)))
                     ERR("EndStateBlock failed, hr %#x.\n", hr);
             }
@@ -3766,7 +3806,9 @@ static HRESULT WINAPI ID3DXEffectImpl_BeginPass(ID3DXEffect *iface, UINT pass)
     if (technique && pass < technique->pass_count && !effect->active_pass)
     {
         effect->active_pass = &technique->passes[pass];
-        return d3dx9_apply_pass_states(effect, effect->active_pass);
+        memset(effect->current_light, 0, sizeof(effect->current_light));
+        memset(&effect->current_material, 0, sizeof(effect->current_material));
+        return d3dx9_apply_pass_states(effect, effect->active_pass, TRUE);
     }
 
     WARN("Invalid argument supplied.\n");
@@ -3785,8 +3827,7 @@ static HRESULT WINAPI ID3DXEffectImpl_CommitChanges(ID3DXEffect *iface)
         WARN("Called without an active pass.\n");
         return D3D_OK;
     }
-    /* TODO: apply only changed states */
-    return d3dx9_apply_pass_states(effect, effect->active_pass);
+    return d3dx9_apply_pass_states(effect, effect->active_pass, FALSE);
 }
 
 static HRESULT WINAPI ID3DXEffectImpl_EndPass(ID3DXEffect *iface)
diff --git a/dlls/d3dx9_36/preshader.c b/dlls/d3dx9_36/preshader.c
index 7373c41..b650f33 100644
--- a/dlls/d3dx9_36/preshader.c
+++ b/dlls/d3dx9_36/preshader.c
@@ -835,7 +835,7 @@ void d3dx_free_param_eval(struct d3dx_param_eval *peval)
     HeapFree(GetProcessHeap(), 0, peval);
 }
 
-static void set_constants(struct d3dx_regstore *rs, struct d3dx_const_tab *const_tab)
+static void set_constants(struct d3dx_regstore *rs, struct d3dx_const_tab *const_tab, BOOL update_all)
 {
     unsigned int const_idx;
 
@@ -850,6 +850,9 @@ static void set_constants(struct d3dx_regstore *rs, struct d3dx_const_tab *const
         BOOL transpose;
         unsigned int count;
 
+        if (!(update_all || is_param_dirty(param)))
+            continue;
+
         transpose = (const_set->constant_class == D3DXPC_MATRIX_COLUMNS && param->class == D3DXPC_MATRIX_ROWS)
                 || (param->class == D3DXPC_MATRIX_COLUMNS && const_set->constant_class == D3DXPC_MATRIX_ROWS);
         if (const_set->constant_class == D3DXPC_MATRIX_COLUMNS)
@@ -1148,7 +1151,26 @@ static HRESULT execute_preshader(struct d3dx_preshader *pres)
     return D3D_OK;
 }
 
-HRESULT d3dx_evaluate_parameter(struct d3dx_param_eval *peval, const struct d3dx_parameter *param, void *param_value)
+static BOOL is_const_tab_input_dirty(struct d3dx_const_tab *ctab)
+{
+    unsigned int i;
+
+    for (i = 0; i < ctab->const_set_count; ++i)
+    {
+        if (is_param_dirty(ctab->const_set[i].param))
+            return TRUE;
+    }
+    return FALSE;
+}
+
+BOOL is_param_eval_input_dirty(struct d3dx_param_eval *peval)
+{
+    return is_const_tab_input_dirty(&peval->pres.inputs)
+            || is_const_tab_input_dirty(&peval->shader_inputs);
+}
+
+HRESULT d3dx_evaluate_parameter(struct d3dx_param_eval *peval, const struct d3dx_parameter *param,
+        void *param_value, BOOL update_all)
 {
     HRESULT hr;
     unsigned int i;
@@ -1157,7 +1179,7 @@ HRESULT d3dx_evaluate_parameter(struct d3dx_param_eval *peval, const struct d3dx
 
     TRACE("peval %p, param %p, param_value %p.\n", peval, param, param_value);
 
-    set_constants(&peval->pres.regs, &peval->pres.inputs);
+    set_constants(&peval->pres.regs, &peval->pres.inputs, update_all);
 
     if (FAILED(hr = execute_preshader(&peval->pres)))
         return hr;
@@ -1246,7 +1268,8 @@ static HRESULT set_shader_constants_device(struct IDirect3DDevice9 *device, stru
     return result;
 }
 
-HRESULT d3dx_param_eval_set_shader_constants(struct IDirect3DDevice9 *device, struct d3dx_param_eval *peval)
+HRESULT d3dx_param_eval_set_shader_constants(struct IDirect3DDevice9 *device, struct d3dx_param_eval *peval,
+        BOOL update_all)
 {
     static const enum pres_reg_tables set_tables[] =
             {PRES_REGTAB_OCONST, PRES_REGTAB_OICONST, PRES_REGTAB_OBCONST};
@@ -1257,11 +1280,14 @@ HRESULT d3dx_param_eval_set_shader_constants(struct IDirect3DDevice9 *device, st
 
     TRACE("device %p, peval %p, param_type %u.\n", device, peval, peval->param_type);
 
-    set_constants(rs, &pres->inputs);
-    if (FAILED(hr = execute_preshader(pres)))
-        return hr;
+    if (update_all || is_const_tab_input_dirty(&pres->inputs))
+    {
+        set_constants(rs, &pres->inputs, update_all);
+        if (FAILED(hr = execute_preshader(pres)))
+            return hr;
+    }
 
-    set_constants(rs, &peval->shader_inputs);
+    set_constants(rs, &peval->shader_inputs, update_all);
     result = D3D_OK;
     for (i = 0; i < ARRAY_SIZE(set_tables); ++i)
     {
-- 
2.9.3




More information about the wine-patches mailing list