Henri,

Thanks for the quick reply.

So I got rid of the maze of structures and instead called the helper functions
directly for each test. The tests are now much easier to understand.
Is there anything
else that should be incorporated into these tests? Any other ideas or
suggestions?

Travis Athougies

On Thu, Jul 22, 2010 at 3:16 PM, Henri Verbeet <[email protected]> wrote:
> On 21 July 2010 23:46, Travis Athougies <[email protected]> wrote:
>> an HLSL test suite might be a good place to start, so here's a draft
>> of a test framework and a few tests that use it.  It's not complete, I
>> know (for one, I need to write tests for vertex shaders), but before I
>> go any farther, I'd like to know get peoples' opinion on the design.
>> Is this the way a wine HLSL test suite should look? Is there something
>> else I should incorporate into these tests? I'm absolutely open to
>> suggestions.
> Leaving the (fairly significant) specific issues with the
> implementation aside, the basic idea will work, but it seems to me
> you're making it more complicated than it needs to be. This really
> doesn't need to be much more complex than the tests in
> dlls/d3d9/tests/visual.c, though a bit more generic would be nice.
>
>> http://wiki.winehq.org/HLSLTestSuite
> That page is pretty awful.
>



--
Travis Athougies



-- 
Travis Athougies
diff --git a/dlls/d3dx9_36/tests/Makefile.in b/dlls/d3dx9_36/tests/Makefile.in
index b9ad40d..ed7857a 100644
--- a/dlls/d3dx9_36/tests/Makefile.in
+++ b/dlls/d3dx9_36/tests/Makefile.in
@@ -13,7 +13,8 @@ C_SRCS = \
 	math.c \
 	mesh.c \
 	shader.c \
-	surface.c
+	surface.c \
+	hlsl.c 
 
 RC_SRCS = rsrc.rc
 
diff --git a/dlls/d3dx9_36/tests/hlsl.c b/dlls/d3dx9_36/tests/hlsl.c
new file mode 100644
index 0000000..9c91294
--- /dev/null
+++ b/dlls/d3dx9_36/tests/hlsl.c
@@ -0,0 +1,703 @@
+/*
+ * Copyright (C) 2010 - Travis Athougies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#define CINTERFACE
+#define COBJMACROS
+#include "wine/test.h"
+
+#include <d3d9.h>
+#include <d3dx9.h>
+#include <d3dx9shader.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "hlsl.h"
+
+/* if GEN_EXPECTED is #define'd, then we will dump the values generated by the
+   PIXEL_SHADER_TEST_LINEAR's tests. You can use this to generate the expected
+   values tables.
+*/
+
+/* Global variables, etc */
+static LPDIRECT3DVERTEXBUFFER9 geometry = 0;
+static LPDIRECT3DVERTEXSHADER9 vshader = 0;
+static LPDIRECT3DVERTEXDECLARATION9 vdeclaration = 0;
+static LPDIRECT3DDEVICE9 device = 0;
+
+#ifdef GEN_EXPECTED
+static HANDLE output = 0;
+#endif
+
+const struct vertex
+{
+    float x, y, z;
+} geometry_vertices[4] = {
+    {-1, -1, 0},
+    {-1, 1, 0},
+    {1, -1, 0},
+    {1, 1, 0}
+};
+
+const D3DVERTEXELEMENT9 vdeclelements[] = {
+    {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
+    D3DDECL_END()
+};
+
+/* For ps_3_0 shaders, we need a vertex shader */
+const char* vshader_hlsl =
+    "float4 vshader(float4 pos: POSITION): POSITION \
+    { \
+      return pos; \
+    } \
+    ";
+
+/* Generic functions to create windows, initialize Direct3D, etc. */
+static HWND create_window(void)
+{
+    WNDCLASS wc = {0};
+    wc.lpfnWndProc = DefWindowProc;
+    wc.lpszClassName = "d3d9_test_wc";
+    RegisterClass(&wc);
+
+    return CreateWindow("d3d9_test_wc", "d3d9_test",
+                        0, 0, 0, 0, 0, 0, 0, 0, 0);
+}
+
+static IDirect3DDevice9 *init_d3d9(HMODULE d3d9_handle)
+{
+    IDirect3D9 * (__stdcall * d3d9_create)(UINT SDKVersion) = 0;
+    IDirect3D9 *d3d9_ptr = 0;
+    IDirect3DDevice9 *device_ptr = 0;
+    D3DPRESENT_PARAMETERS present_parameters;
+    D3DXMATRIX projection_matrix;
+
+    void* temp_geometry_vertices;
+
+    LPD3DXBUFFER compiled = NULL;
+    LPD3DXBUFFER errors;
+
+    HRESULT hres;
+
+    d3d9_create = (void *)GetProcAddress(d3d9_handle, "Direct3DCreate9");
+    ok(d3d9_create != NULL, "Failed to get address of Direct3DCreate9\n");
+    if (!d3d9_create) return NULL;
+
+    d3d9_ptr = d3d9_create(D3D_SDK_VERSION);
+    if (!d3d9_ptr)
+    {
+        skip("could not create D3D9\n");
+        return NULL;
+    }
+
+    ZeroMemory(&present_parameters, sizeof(present_parameters));
+    present_parameters.Windowed = TRUE;
+    present_parameters.hDeviceWindow = create_window();
+    present_parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
+
+    hres = IDirect3D9_CreateDevice(d3d9_ptr, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &present_parameters, &device_ptr);
+    ok(SUCCEEDED(hres), "Could not create device, IDirect3D9_CreateDevice returned: %08x", hres);
+
+    /* Create the geometry on which our pixel shader will run */
+    hres = IDirect3DDevice9_CreateVertexBuffer(device_ptr, 4 * sizeof(struct vertex), D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &geometry, NULL);
+    ok(SUCCEEDED(hres), "Could not create vertex buffer, IDirect3DDevice9_CreateVertexBuffer returned: %08x\n", hres);
+
+    hres = IDirect3DVertexBuffer9_Lock(geometry, 0, sizeof(geometry_vertices), (void**)&temp_geometry_vertices, 0);
+    ok(SUCCEEDED(hres), "IDirect3DVertexBuffer9_Lock returned: %08x\n", hres);
+    memcpy(temp_geometry_vertices, geometry_vertices, sizeof(geometry_vertices));
+    IDirect3DVertexBuffer9_Unlock(geometry);
+
+    hres = IDirect3DDevice9_CreateVertexDeclaration(device_ptr, vdeclelements, &vdeclaration);
+    ok(SUCCEEDED(hres), "Could not create vertex declaration: IDirect3DDevice9_CreateVertexDeclaration returned: %08x\n", hres);
+
+    /* Set up projection */
+    D3DXMatrixOrthoLH(&projection_matrix, 2.0, 2.0, 0, 1.0);
+    IDirect3DDevice9_SetTransform(device_ptr, D3DTS_PROJECTION, &projection_matrix);
+
+    hres = D3DXCompileShader(vshader_hlsl, strlen(vshader_hlsl),
+                             NULL, NULL, "vshader", "vs_1_1", 0,
+                             &compiled, &errors, 0);
+    if(!compiled)
+    {
+        vshader = NULL;
+        skip("Not compiling vertex shader due to lacking wine HLSL support!\n");
+        return device_ptr;
+    }
+    if(errors)
+      ok(SUCCEEDED(hres), "Vertex shader compilation failed: %s\n", (char*) errors->lpVtbl->GetBufferPointer(errors));
+
+    hres = IDirect3DDevice9_CreateVertexShader(device_ptr, (DWORD*) compiled->lpVtbl->GetBufferPointer(compiled), &vshader);
+    ok(SUCCEEDED(hres), "IDirect3DDevice9_CreateVertexxShader returned: %08x\n", hres);
+    IUnknown_Release(compiled);
+    IUnknown_Release(errors);
+
+    return device_ptr;
+}
+
+static void set_constant(LPD3DXCONSTANTTABLE constants, struct constant_table* constant)
+{
+    switch(constant->type) {
+    case CONSTANT_TYPE_INT:
+        constants->lpVtbl->SetInt(constants, device, constant->name, (int)constant->value[0]);
+        break;
+    case CONSTANT_TYPE_FLOAT:
+        constants->lpVtbl->SetFloat(constants, device, constant->name, constant->value[0]);
+        break;
+    case CONSTANT_TYPE_FLOAT4:
+    {
+        D3DXVECTOR4 vector;
+        vector.x = constant->value[0];
+        vector.y = constant->value[1];
+        vector.z = constant->value[2];
+        vector.w = constant->value[3];
+        constants->lpVtbl->SetVector(constants, device, constant->name, &vector);
+        break;
+    }
+    case CONSTANT_TYPE_FLOAT4_ARRAY:
+    {
+        constants->lpVtbl->SetVectorArray(constants, device, constant->name, (D3DXVECTOR4*) constant->array, constant->size);
+        break;
+    }
+    }
+}
+
+#ifdef GEN_EXPECTED
+static int fmt_results_linear(void* actual, struct pixel_shader_test_linear* ps)
+{
+    const int per_line = 4;
+    static char buf[2 * 1024 * 1024]; /* Give us 2 megs to work with */
+    int result = 0;
+    int i = 0, j = 0;
+    int index = 0, written = 0;
+    float* _actual = (float*) actual;
+    index += sprintf(&buf[index], "float %s_expected[] = {\n", ps->head.name);
+    for(i = 0;i < ps->samples;i += per_line) {
+        for(j = 0;j < per_line && (i + j) < ps->samples;j++)
+            index += sprintf(&buf[index], " %.20ff,", _actual[i + j]);
+        index += sprintf(&buf[index], "\n");
+    }
+    index += sprintf(&buf[index], "};\n\n");
+    WriteFile(output, (void*)buf, index,  &written, NULL);
+    return result;
+}
+#endif
+
+/* For an explanation of why this works, please see
+   www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */
+int ULPS(float A, float B)
+{
+    int bInt;
+    int aInt = *(int*)&A;
+    if (aInt < 0)
+        aInt = 0x80000000 - aInt;
+    bInt = *(int*)&B;
+    if (bInt < 0)
+        bInt = 0x80000000 - bInt;
+    return abs(aInt - bInt);
+}
+
+/* Compare floating point arrays using units in the last place (ULPS)
+   keeping a count of how many erroneous values we have found */
+static int cmp_results_linear(struct pixel_shader_test_linear* ps, float* actual)
+{
+    int result = 0;
+    int i = 0;
+    for(i = 0;i < ps->samples;i++)
+        if(ULPS(ps->expected[i], actual[i]) >= ps->max_ulps)
+            result ++;
+    return result;
+}
+
+/* Compile our pixel shader and get back the compiled version and a constant
+   table */
+LPDIRECT3DPIXELSHADER9 compile_pixel_shader(struct pixel_shader_test* ps, LPD3DXCONSTANTTABLE* constants)
+{
+    LPD3DXBUFFER compiled = NULL;
+    LPD3DXBUFFER errors;
+    LPDIRECT3DPIXELSHADER9 pshader;
+    HRESULT hr;
+
+    /* Compile the pixel shader for this particular test and create the pixel
+       shader object */
+    hr = D3DXCompileShader(ps->shader, strlen(ps->shader),
+                           NULL, NULL, "test", ps->version,
+                           0, &compiled, &errors, constants);
+    if(errors)
+        ok (hr == D3D_OK, "Pixel shader %s compilation failed: %s\n", ps->name, (char*) errors->lpVtbl->GetBufferPointer(errors));
+    if(FAILED(hr)) return NULL;
+
+    hr = IDirect3DDevice9_CreatePixelShader(device, (DWORD*) compiled->lpVtbl->GetBufferPointer(compiled), &pshader);
+    ok(SUCCEEDED(hr), "IDirect3DDevice9_CreatePixelShader returned: %08x\n", hr);
+    IUnknown_Release(compiled);
+    return pshader;
+}
+
+/* Draw the geometry using our shader. This method is used for both linear and probe type shaders */
+LPDIRECT3DSURFACE9 compute_shader_linear(struct pixel_shader_test* ps, int samples, D3DFORMAT format)
+{
+    LPDIRECT3DPIXELSHADER9 pshader;
+    LPDIRECT3DSURFACE9 render_target;
+    LPDIRECT3DSURFACE9 readback;
+    LPD3DXCONSTANTTABLE constants;
+
+    HRESULT hr;
+
+    pshader = compile_pixel_shader(ps, &constants);
+    if(pshader == NULL) return NULL;
+
+    /* Create the render target surface*/
+    hr = IDirect3DDevice9_CreateRenderTarget(device, samples, 1, format,
+                                             D3DMULTISAMPLE_NONE, 0, FALSE, &render_target, NULL);
+    ok(hr == D3D_OK, "IDirect3DDevice9_CreateRenderTarget returned: %08x\n", hr);
+
+    /* The Direct3D 9 docs state that we cannot lock a render target surface,
+       instead we must copy the render target onto this surface to lock it */
+    hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, samples, 1, format,
+                                                      D3DPOOL_SYSTEMMEM, &readback, NULL);
+    ok(hr == D3D_OK, "IDirect3DDevice9_CreateOffscreenPlainSurface returned: %08x\n", hr);
+
+    hr = IDirect3DDevice9_SetRenderTarget(device, 0, render_target);
+    ok(hr == D3D_OK, "IDirect3DDevice9_SetRenderTarget returned: %08x\n", hr);
+
+    /* Clear the backbuffer */
+    hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
+    ok(hr == D3D_OK, "IDirect3DDevice9_Clear returned: %08x\n", hr);
+
+    /* Start drawing the geometry which will fill the entire screen */
+    hr = IDirect3DDevice9_BeginScene(device);
+    ok(hr == D3D_OK, "IDirect3DDevice9_BeginScene returned: %08x\n", hr);
+
+    /* This vertex shader is required, because pixel shaders 3.0 must have either
+       a vertex shader or pretransformed vertices */
+    hr = IDirect3DDevice9_SetVertexShader(device, vshader);
+    ok(hr == D3D_OK, "IDirect3DDevice9_SetVertexShader returned: %08x\n", hr);
+    hr = IDirect3DDevice9_SetPixelShader(device, pshader);
+    ok(hr == D3D_OK, "IDirect3DDevice9_SetPixelShader returned: %08x\n", hr);
+    /* The VPOS semantic gives us whole numbered values in the range of our screen
+       coordinates (e.g. on a 800x600 screen, VPOS would range from (0,0) to
+       (800.0, 600.0) in (1.0, 1.0) increments. However, our shader tests
+       operate on values in the range [0, 1]. By setting this constant here,
+       we let our shaders convert between VPOS and the clamped values.
+    */
+    constants->lpVtbl->SetFloat(constants, device, "samples", (float)samples);
+    if(ps->constants) {
+        int i = 0;
+        for(;ps->constants[i].name;i++)
+            set_constant(constants, &ps->constants[i]);
+    }
+    hr = IDirect3DDevice9_SetVertexDeclaration(device, vdeclaration);
+    ok(hr == D3D_OK, "IDirect3DDevice9_SetVertexDeclaration returned: %08x\n", hr);
+    IDirect3DDevice9_SetStreamSource(device, 0, geometry, 0, sizeof(struct vertex));
+    ok(hr == D3D_OK, "IDirect3DDevice9_SetStreamSource returned: %08x\n", hr);
+    IDirect3DDevice9_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, 0, 2);
+    ok(hr == D3D_OK, "IDirect3DDevice9_DrawPrimitive returned: %08x\n", hr);
+
+    hr = IDirect3DDevice9_EndScene(device);
+    ok(hr == D3D_OK, "IDirect3DDevice9_EndScene returned: %08x\n", hr);
+
+    IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL);
+
+    IDirect3DDevice9_GetRenderTargetData(device, render_target, readback);
+
+    /* release everything */
+    IUnknown_Release(pshader);
+    IUnknown_Release(render_target);
+    IUnknown_Release(constants);
+
+    return readback;
+}
+
+/* draw a struct pixel_shader_test, depending on its type */
+LPDIRECT3DSURFACE9 compute_shader(struct pixel_shader_test* ps)
+{
+    switch(ps->type) {
+        /* Even though samples occupies the same space in both structs in practice,
+           in theory, ANSI C makes no such guarantee, so we have this switch statement */
+    case PIXEL_SHADER_TEST_LINEAR:
+        return compute_shader_linear(ps, ((struct pixel_shader_test_linear*) ps)->samples, D3DFMT_R32F);
+    case PIXEL_SHADER_TEST_PROBE:
+        return compute_shader_linear(ps, ((struct pixel_shader_test_probe*) ps)->samples, D3DFMT_A8R8G8B8);
+    default:
+        trace("Unknown pixel shader type: %08x\n", ps->type);
+    }
+    return 0;
+}
+
+/* The pixel shader tests are designed to run using pixel shader version
+   ps_3_0's VPOS semantic. This semantic was chosen, because texture coordinate
+   calculations do not have to be the same on every platform, as per the
+   Direct3D 9 specification. The VPOS semantic however, holds screen position,
+   and since screen pixels are specified in whole numbers, the VPOS semantic
+   should the same accross all hardware configurations. In this function, all we
+   have to do is ensure that our geometry fills the entire screen and that our
+   pixel shader is set. */
+static void test_ps_linear(void* data, struct pixel_shader_test_linear* ps)
+{
+    int error_cnt;
+
+#ifdef GEN_EXPECTED
+    fmt_results_linear((float*) data, ps);
+#else
+    error_cnt =  cmp_results_linear(ps, (float*) data);
+    ok(error_cnt == 0, "Results for %s not expected: %d errors\n", ps->head.name, error_cnt);
+    if(error_cnt == 0) printf("SUCCESSS!!!\nSUCCESS!!!\nSUCCESS!\n");
+#endif
+}
+
+/* Run each probe and make sure that each value is what we expected it to be */
+static void test_ps_probe(void* _data, struct pixel_shader_test_probe* ps)
+{
+    int i = 0;
+    DWORD* data = (DWORD*) _data;
+
+    for(;i < ps->probe_cnt;i++) {
+        ok((data[ps->probes[i].sample] & ps->probes[i].mask) == (ps->probes[i].color & ps->probes[i].mask), "%s: Probing sample %d turned up the wrong color %08x (should have been %08x)\n", ps->head.name, ps->probes[i].sample, data[ps->probes[i].sample], ps->probes[i].color);
+    }
+
+}
+
+/* Run each test in the pixel_shader_tests array */
+static void run_ps_test(IDirect3DDevice9* device, struct pixel_shader_test* test)
+{
+    HRESULT hr;
+    LPDIRECT3DSURFACE9 readback;
+    D3DLOCKED_RECT lr;
+
+    trace("Runnning test %s\n", test->name);
+    readback = compute_shader(test);
+    if(!readback)
+    {
+        skip("Skipping pixel shader test %s, due to lacking HLSL support\n", test->name);
+        return;
+    }
+    hr = IDirect3DSurface9_LockRect(readback, &lr, NULL, D3DLOCK_READONLY);
+    ok(hr == D3D_OK, "IDirect3DSurface9_LockRect returned: %08x\n", hr);
+
+    switch((test)->type) {
+    case PIXEL_SHADER_TEST_LINEAR:
+        test_ps_linear(lr.pBits, (struct pixel_shader_test_linear*) test);
+        break;
+    case PIXEL_SHADER_TEST_PROBE:
+        test_ps_probe(lr.pBits, (struct pixel_shader_test_probe*) test);
+        break;
+    default:
+        trace("Unknown type: %08x\n", test->type);
+    }
+    hr = IDirect3DSurface9_UnlockRect(readback);
+    ok(hr == D3D_OK, "IDirect3DSurface9_UnlockRect returned: %08x\n", hr);
+
+    IUnknown_Release(readback);
+}
+
+/* Now the actual test functions */
+
+static void test_trig(IDirect3DDevice9* device)
+{
+    /* These values were chosen after comparing the results of both NVIDIA and
+       ATI graphics cards on Windows and the results of an NVIDIA card on Linux,
+       using the provided gen-expected.c. It was found that the ULPS between
+       most values were fairly low, but for a certain number of values (with
+       seemingly no correlation), the ULPs error was very, very high (as high
+       as 65536). It also seems that the incidence of the higher numbers was
+       much lower than that of the lower numbers. Since Direct3D doesn't require
+       every renderer to produce exactly the same results, these values were
+       chosen to be loose enough so that the test will succeed on a proper
+       implementation but strict enough so that an improper implementation will
+       cause the test to fail */
+    static float sin_expected[] = {
+        0.50000000000000000000f, 0.59754514694213867000f, 0.69134163856506348000f, 0.77778506278991699000f,
+        0.85355341434478760000f, 0.91573476791381836000f, 0.96193975210189819000f, 0.99039268493652344000f,
+        1.00000000000000000000f, 0.99039268493652344000f, 0.96193975210189819000f, 0.91573476791381836000f,
+        0.85355335474014282000f, 0.77778506278991699000f, 0.69134163856506348000f, 0.59754508733749390000f,
+        0.50000000000000000000f, 0.40245491266250610000f, 0.30865836143493652000f, 0.22221493721008301000f,
+        0.14644664525985718000f, 0.08426526188850402800f, 0.03806024789810180700f, 0.00960734486579895020f,
+        0.00000000000000000000f, 0.00960734486579895020f, 0.03806024789810180700f, 0.08426523208618164100f,
+        0.14644661545753479000f, 0.22221490740776062000f, 0.30865836143493652000f, 0.40245485305786133000f,
+    };
+
+    static struct pixel_shader_test_linear sin_test =
+        {
+            {PIXEL_SHADER_TEST_LINEAR, "sin", /* Shader test type and name */
+             "uniform float samples;\n"
+             "float4 test(float2 pos: VPOS): COLOR\n"
+             "{\n"
+             "  const float pi2 = 6.2831853071795862;\n"
+             "  float calcd = (sin((pos.x/samples) * pi2) + 1)/2;"
+             "  return calcd;\n"
+             "}\n",
+             "ps_3_0", 0, NULL}, /* Shader profile, Flags (unused for now), and constant table */
+            32, 64, sin_expected /* Samples, Max ULPS, and expected values */
+        };
+
+    static float cos_expected[] = {
+        1.00000000000000000000f, 0.99039268493652344000f, 0.96193975210189819000f, 0.91573476791381836000f,
+        0.85355335474014282000f, 0.77778506278991699000f, 0.69134169816970825000f, 0.59754514694213867000f,
+        0.50000000000000000000f, 0.40245485305786133000f, 0.30865830183029175000f, 0.22221493721008301000f,
+        0.14644661545753479000f, 0.08426526188850402800f, 0.03806024789810180700f, 0.00960737466812133790f,
+        0.00000000000000000000f, 0.00960737466812133790f, 0.03806024789810180700f, 0.08426526188850402800f,
+        0.14644661545753479000f, 0.22221493721008301000f, 0.30865830183029175000f, 0.40245485305786133000f,
+        0.50000000000000000000f, 0.59754514694213867000f, 0.69134169816970825000f, 0.77778506278991699000f,
+        0.85355335474014282000f, 0.91573476791381836000f, 0.96193975210189819000f, 0.99039268493652344000f,
+    };
+
+    static struct pixel_shader_test_linear cos_test =
+        {
+            {PIXEL_SHADER_TEST_LINEAR, "cos", /* Shader test type and name */
+             "uniform float samples;\n"
+             "float4 test(float2 pos: VPOS): COLOR\n"
+             "{\n"
+             "  const float pi2 = 6.2831853071795862;\n"
+             "  float calcd = (cos((pos.x/samples) * pi2) + 1)/2;"
+             "  return calcd;\n"
+             "}\n",
+             "ps_3_0", 0, NULL}, /* Version, flags (unused), and constant table */
+            32, 64, cos_expected /* Samples, max ULPs, and expected values */
+        };
+
+    run_ps_test(device, (struct pixel_shader_test*) &sin_test);
+    run_ps_test(device, (struct pixel_shader_test*) &cos_test);
+}
+
+static void test_swizzle(IDirect3DDevice9* device)
+{
+    static struct sample_probe swizzle_probes[] = {
+        {SAMPLE_PROBE_RGB, 0, D3DCOLOR_XRGB(0, 255, 255)}
+    };
+
+    CONSTANT_TABLE_BEGIN(constants)
+    FLOAT4("color", 1, 0, 0, 1)
+    CONSTANT_TABLE_END()
+
+    static struct pixel_shader_test_probe swizzle_test  =
+        {
+            {PIXEL_SHADER_TEST_PROBE, "swizzle_test", /* Test type and name */
+             "uniform float4 color;\n" /* Once again this is to prevent compiler optimization. */
+             "float4 test(): COLOR\n"
+             "{\n"
+             "  float4 ret = color;\n"
+             "  ret.gb = ret.ra;\n"
+             "  ret.ra = float2(0, 0);\n"
+             "  return ret;\n"
+             "}\n",
+             "ps_3_0", 0, constants /* Version, flags (unused), and constant table */
+            },
+            1, 1, swizzle_probes /* Samples to take, Number of probes, and probe table */
+        };
+
+    run_ps_test(device, (struct pixel_shader_test*) &swizzle_test);
+}
+
+static void test_conditionals(IDirect3DDevice9* device)
+{
+    static struct sample_probe if_greater_probes[] = {
+        {SAMPLE_PROBE_RGB, 15, D3DCOLOR_XRGB(0, 255, 0)},
+        {SAMPLE_PROBE_RGB, 25, D3DCOLOR_XRGB(0, 0, 255)},
+    };
+
+    static struct pixel_shader_test_probe if_greater_test =
+        {
+            {PIXEL_SHADER_TEST_PROBE, "if_greater", /* Type and name */
+             "uniform float samples;\n"
+             "float4 test(float2 pos: VPOS): COLOR\n"
+             "{\n"
+             "  if(pos.x > 20.0)\n"
+             "     return float4(0, 0, 1, 0);\n"
+             "  else\n"
+             "     return float4(0, 1, 0, 0);\n"
+             "}\n",
+             "ps_3_0", /* Shader profile */
+             0, /* Flags currently unused */
+             NULL /* This shader needs no constants */
+            },
+            32, /* Number of pixels to draw */
+            2, if_greater_probes /* Number of probes and probe table */
+        };
+
+    run_ps_test(device, (struct pixel_shader_test*) &if_greater_test);
+}
+
+static void test_float(IDirect3DDevice9* device)
+{
+    static struct sample_probe probes1[] = {
+        {SAMPLE_PROBE_RGBA, 10, D3DCOLOR_ARGB(255, 0, 255, 0)},
+        {SAMPLE_PROBE_RGBA, 16, D3DCOLOR_ARGB(255, 0, 255, 0)},
+        {SAMPLE_PROBE_RGBA, 22, D3DCOLOR_ARGB(255, 0, 255, 0)},
+    };
+
+    static struct pixel_shader_test_probe vec4_indexing_test1 =
+        {
+            {PIXEL_SHADER_TEST_PROBE, "vec4_indexing_test1", /* shader type and name */
+             "float4 test(): COLOR\n" /* Shader code */
+             "{\n"
+             "  float4 color;\n"
+             "  color[0] = 0.0;\n"
+             "  color[1] = 1.0;\n"
+             "  color[2] = 0.0;\n"
+             "  color[3] = 1.0;\n"
+             "  return color;\n"
+             "}\n",
+             "ps_3_0", /* Shader profile */
+             0, /* Flags (currently unused ) */
+             NULL /* This shader needs no constants */
+            },
+            32, /* Number of pixels to draw (remember this is all in the x direction) */
+            3, probes1 /* Number of probes and probe table */
+        };
+
+    static struct sample_probe probes2[] = {
+        {SAMPLE_PROBE_RGBA, 10, D3DCOLOR_ARGB(255, 0, 255, 0)},
+        {SAMPLE_PROBE_RGBA, 16, D3DCOLOR_ARGB(255, 0, 255, 0)},
+        {SAMPLE_PROBE_RGBA, 22, D3DCOLOR_ARGB(255, 0, 255, 0)}
+    };
+
+    CONSTANT_TABLE_BEGIN(table2)
+    INT("i", 2)
+    CONSTANT_TABLE_END()
+
+    static struct pixel_shader_test_probe vec4_indexing_test2 =
+        {
+            {PIXEL_SHADER_TEST_PROBE, "vec4_indexing_test2", /* Type and name */
+             "uniform int i;\n" /* We have this uniform here so the compiler can't optimize */
+             "float4 test(): COLOR\n"
+             "{\n"
+             "  float4 color = float4(0, 0, 1, 1);\n"
+             "  color.g = color[i];\n"
+             "  color.b = 0;\n"
+             "  return color;\n"
+             "}\n",
+             "ps_3_0", /* Shader profile we want to use */
+             0, /* Flags (currently unused) */
+             table2}, /* Constant table */
+            32, /* Number of samples to draw */
+            3, probes2 /* Number of probes and probe table */
+        };
+
+    run_ps_test(device, (struct pixel_shader_test*) &vec4_indexing_test1);
+    run_ps_test(device, (struct pixel_shader_test*) &vec4_indexing_test2);
+}
+
+static void test_constant_tables(IDirect3DDevice9* device)
+{
+    static struct sample_probe vector_array_staticindex_probes[] = {
+        {SAMPLE_PROBE_RGBA, 0, D3DCOLOR_ARGB(255, 255, 255, 255)}
+    };
+
+    static D3DXVECTOR4 vasit_colors[] = {
+        {1, 0, 0, 0},
+        {0, 0, 1, 0},
+        {0, 0, 0, 1},
+        {0, 1, 0, 0}
+    };
+
+    CONSTANT_TABLE_BEGIN(vector_array_staticindex_constants)
+    FLOAT4_ARRAY("colors", 4, vasit_colors)
+    CONSTANT_TABLE_END()
+
+    static struct pixel_shader_test_probe vector_array_staticindex_test =
+        {
+            {PIXEL_SHADER_TEST_PROBE, "vector_array_test",
+             "uniform float4 colors[4];"
+             "float4 test(float2 wpos: VPOS):COLOR\n"
+             "{\n"
+             "  return colors[0] + colors[1] + colors[2] + colors[3];\n"
+             "}\n",
+             "ps_3_0",
+             0,
+             vector_array_staticindex_constants},
+            1,
+            1, vector_array_staticindex_probes
+        };
+
+    static struct sample_probe integer_constant_probes[] = {
+        {SAMPLE_PROBE_RGBA, 0, D3DCOLOR_ARGB(255, 255, 255, 255)},
+        {SAMPLE_PROBE_RGBA, 6, D3DCOLOR_ARGB(0, 255, 0, 255)}
+    };
+
+    CONSTANT_TABLE_BEGIN(integer_constant_constants)
+    INT("brk", 5)
+    CONSTANT_TABLE_END()
+
+    static struct pixel_shader_test_probe integer_constant_test =
+        {
+            {PIXEL_SHADER_TEST_PROBE, "integer_constant_test",
+             "uniform int brk;"
+             "float4 test(float2 wpos: VPOS):COLOR\n"
+             "{\n"
+             "  if(wpos.x > brk)\n"
+             "    return float4(1, 0, 1, 0);\n"
+             "  else\n"
+             "    return float4(1, 1, 1, 1);\n"
+             "}\n",
+             "ps_3_0",
+             0,
+             integer_constant_constants},
+            8,
+            2, integer_constant_probes
+        };
+             
+        
+    run_ps_test(device, (struct pixel_shader_test*) &vector_array_staticindex_test);
+    run_ps_test(device, (struct pixel_shader_test*) &integer_constant_test);
+}
+
+/* The heart of our little testing framework */
+START_TEST(hlsl)
+{
+    HMODULE d3d9_handle;
+    D3DCAPS9 caps;
+    ULONG refcount;
+
+    d3d9_handle = LoadLibraryA("d3d9.dll");
+    if (!d3d9_handle) {
+        skip("Could not read d3d9.dll\n");
+        return;
+    }
+
+    device = init_d3d9(d3d9_handle);
+    if (!device) return;
+
+#ifdef GEN_EXPECTED
+    output = CreateFile("expected.c", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
+
+    /* Make sure we support pixel shaders, before trying to compile them! */
+    IDirect3DDevice9_GetDeviceCaps(device, &caps);
+    if(caps.PixelShaderVersion & 0xffff) {
+        todo_wine {
+            test_constant_tables(device);
+            test_conditionals(device);
+            test_float(device);
+            test_trig(device);
+            test_swizzle(device);
+        }
+    } else skip("No pixel shader support\n");
+
+    /* Reference counting sanity checks */
+    if(vshader) {
+        refcount = IDirect3DVertexShader9_Release(vshader);
+        ok(!refcount, "Vertex shader has %u references left\n", refcount);
+    }
+
+    refcount = IDirect3DVertexBuffer9_Release(geometry);
+    ok(!refcount, "Vertex buffer has %u references left\n", refcount);
+
+    refcount = IDirect3DVertexDeclaration9_Release(vdeclaration);
+    ok(!refcount, "Vertex declaration has %u references left\n", refcount);
+
+    refcount = IDirect3DDevice9_Release(device);
+    ok(!refcount, "Device has %u references left\n", refcount);
+    return;
+}
diff --git a/dlls/d3dx9_36/tests/hlsl.h b/dlls/d3dx9_36/tests/hlsl.h
new file mode 100644
index 0000000..ae67ace
--- /dev/null
+++ b/dlls/d3dx9_36/tests/hlsl.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 - Travis Athougies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#ifndef __hlsl_H__
+#define __hlsl_H__
+
+#define PIXEL_SHADER_TEST_LINEAR       1
+#define PIXEL_SHADER_TEST_PROBE        2
+
+#define SAMPLE_PROBE_RGB      0x00FFFFFF
+#define SAMPLE_PROBE_RGBA     0xFFFFFFFF
+
+#define CONSTANT_TYPE_FLOAT            1
+#define CONSTANT_TYPE_INT              2
+#define CONSTANT_TYPE_FLOAT4           3
+#define CONSTANT_TYPE_FLOAT4_ARRAY     4
+
+#define CONSTANT_TABLE_BEGIN(name) static struct constant_table name[] = {
+#define INT(name, value)   {name, CONSTANT_TYPE_INT, {value, 0, 0, 0}, NULL, 0},
+#define FLOAT(name, value) {name, CONSTANT_TYPE_FLOAT, {value, 0, 0, 0}, NULL, 0},
+#define FLOAT4(name, v1, v2, v3, v4) {name, CONSTANT_TYPE_FLOAT4, {v1,v2,v3,v4}, NULL, 0},
+#define FLOAT4_ARRAY(name, size, array) {name, CONSTANT_TYPE_FLOAT4_ARRAY, {0, 0, 0, 0}, array, size},
+#define CONSTANT_TABLE_END() {NULL, 0, {0, 0, 0, 0}} };
+
+struct constant_table
+{
+  const char* name;
+  int type;
+  float value[4];
+  void* array;
+  int size;
+};
+
+/* Probes tell the testing framework to examine the color output of certain pixels,
+   and raise an error if they're not what we expected */
+struct sample_probe
+{
+  /* Lets us specify a mask so we can specify which bits we want to check
+	 You'd normally want to use SAMPLE_PROBE_RGB and SAMPLE_PROBE_RGBA here*/
+  DWORD mask;
+  int sample;
+  DWORD color;
+};
+
+/* This is the base structure for writing a pixel shader test.
+   This structure is never used plain. Instead, use the specific
+   structure for the type of test you're writing. */
+struct pixel_shader_test
+{
+  UINT type; /* The type of this test. So the engine knows how to treat the struct */
+  const char* name; /* The name of the test */
+  const char* shader; /* The shader code to run */
+  LPCSTR version; /* The pixel shader profile to run under */
+  UINT flags; /* Any flags for the test. Currently unused */
+  /* A table to an array of struct constant_table's. Using this
+	 you can set constants for the shader. If you leave this as
+	 NULL, no constants will be set. Construct the constant table
+	 using the appropriate macros. For example.
+	 CONSTANT_TABLE_BEGIN(name)
+	 INT(name, 8)
+	 CONTANT_TABLE_END(); */
+  struct constant_table* constants;
+};
+
+/* A pixel shader test that makes sure that the output of our
+   shader matches an array of expected values. The pixel
+   shader is drawn in a samples x 1 area and should use
+   the VPOS semantic's x component to determine its position.
+
+   Note: The use of VPOS was decided upon because texture coordinates
+   seemed to give very different results on different platforms.
+   Screen position was perfectly consistent.
+*/
+struct pixel_shader_test_linear
+{
+  struct pixel_shader_test head; /* The parent class */
+  int samples; /* How many samples to take */
+  /* How many Units in the Last Place errors we should tolerate.
+	 You should determine this after examining the output of the
+	 shader on a few other platforms */
+  int max_ulps;
+  /* An array of floating point numbers of sample length that represent our expected values */
+  float* expected; 
+};
+
+/* Specifies a test where we want to examine the value of only some
+   pixels and ensure that their colors match up exactly to what
+   we expected.
+
+   Once again use the VPOS semantic.
+*/
+struct pixel_shader_test_probe
+{
+  struct pixel_shader_test head; /* see struct pixel_shader_test_linear */
+  int samples; /* See struct pixel_shader_test_linear */
+  int probe_cnt; /* The number of probes to do */
+  struct sample_probe* probes; /* A pointer to an array of struct sample_probe's of length probe_cnt */
+};
+
+#endif
+
diff --git a/include/d3dx9shader.h b/include/d3dx9shader.h
index c8380bc..c54d27c 100644
--- a/include/d3dx9shader.h
+++ b/include/d3dx9shader.h
@@ -296,6 +296,17 @@ HRESULT WINAPI D3DXAssembleShader(LPCSTR data,
                                   LPD3DXBUFFER* shader,
                                   LPD3DXBUFFER* error_messages);
 
+HRESULT WINAPI D3DXCompileShader(LPCSTR src_data,
+                                 UINT data_len,
+                                 const D3DXMACRO* defines,
+                                 LPD3DXINCLUDE include,
+                                 LPCSTR function_name,
+                                 LPCSTR profile,
+                                 DWORD flags,
+                                 LPD3DXBUFFER* shader,
+                                 LPD3DXBUFFER* error_messages,
+                                 LPD3DXCONSTANTTABLE* constant_table);
+
 HRESULT WINAPI D3DXGetShaderConstantTableEx(CONST DWORD* byte_code,
                                             DWORD flags,
                                             LPD3DXCONSTANTTABLE* constant_table);


Reply via email to