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