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);