Hi,

First of all, let me introduce myself. I'm a young software developer
entering college this fall. I've always been interested in helping out
the WINE project, especially in the areas of DirectX and Direct3D. Dan
Kegel suggested in http://wiki.winehq.org/HLSLTestSuite that writing
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.

And lastly, I'd like to thank the wine developers for producing such a
wonderful piece of software. We really appreciate you all!

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