mirror of
https://gitlab.winehq.org/wine/vkd3d.git
synced 2024-11-21 16:46:41 -08:00
2349 lines
76 KiB
C
2349 lines
76 KiB
C
/*
|
|
* Copyright 2020-2024 Elizabeth Figura for CodeWeavers
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
/*
|
|
* This application contains code derived from piglit, the license for which
|
|
* follows:
|
|
*
|
|
* Copyright © 2010 Intel Corporation
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#ifdef __MINGW32__
|
|
# define _HRESULT_DEFINED
|
|
typedef int HRESULT;
|
|
#else
|
|
# define WIDL_C_INLINE_WRAPPERS
|
|
#endif
|
|
|
|
#define COBJMACROS
|
|
#define CONST_VTABLE
|
|
#include "config.h"
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "vkd3d_windows.h"
|
|
#include "vkd3d_d3dcommon.h"
|
|
#include "vkd3d_d3dcompiler.h"
|
|
#include "vkd3d_test.h"
|
|
#include "shader_runner.h"
|
|
|
|
struct test_options test_options = {0};
|
|
|
|
#ifdef VKD3D_CROSSTEST
|
|
static const char HLSL_COMPILER[] = "d3dcompiler47.dll";
|
|
#else
|
|
static const char HLSL_COMPILER[] = "vkd3d-shader";
|
|
#endif
|
|
static const char *const model_strings[] =
|
|
{
|
|
[SHADER_MODEL_2_0] = "2.0",
|
|
[SHADER_MODEL_3_0] = "3.0",
|
|
[SHADER_MODEL_4_0] = "4.0",
|
|
[SHADER_MODEL_4_1] = "4.1",
|
|
[SHADER_MODEL_5_0] = "5.0",
|
|
[SHADER_MODEL_5_1] = "5.1",
|
|
[SHADER_MODEL_6_0] = "6.0",
|
|
};
|
|
|
|
void fatal_error(const char *format, ...)
|
|
{
|
|
unsigned int i;
|
|
va_list args;
|
|
|
|
for (i = 0; i < vkd3d_test_state.context_count; ++i)
|
|
fprintf(stderr, "%s: ", vkd3d_test_state.context[i]);
|
|
|
|
va_start(args, format);
|
|
vfprintf(stderr, format, args);
|
|
va_end(args);
|
|
exit(1);
|
|
}
|
|
|
|
enum parse_state
|
|
{
|
|
STATE_NONE,
|
|
STATE_INPUT_LAYOUT,
|
|
STATE_PREPROC,
|
|
STATE_PREPROC_INVALID,
|
|
STATE_REQUIRE,
|
|
STATE_RESOURCE,
|
|
STATE_SAMPLER,
|
|
STATE_SHADER_COMPUTE,
|
|
STATE_SHADER_COMPUTE_TODO,
|
|
STATE_SHADER_PIXEL,
|
|
STATE_SHADER_PIXEL_TODO,
|
|
STATE_SHADER_VERTEX,
|
|
STATE_SHADER_VERTEX_TODO,
|
|
STATE_SHADER_EFFECT,
|
|
STATE_SHADER_EFFECT_TODO,
|
|
STATE_SHADER_HULL,
|
|
STATE_SHADER_HULL_TODO,
|
|
STATE_SHADER_DOMAIN,
|
|
STATE_SHADER_DOMAIN_TODO,
|
|
STATE_SHADER_GEOMETRY,
|
|
STATE_SHADER_GEOMETRY_TODO,
|
|
STATE_TEST,
|
|
};
|
|
|
|
static bool match_tag(struct shader_runner *runner, const char *tag)
|
|
{
|
|
for (size_t i = 0; i < runner->caps->tag_count; ++i)
|
|
{
|
|
if (!strcmp(tag, runner->caps->tags[i]))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool check_qualifier_args_conjunction(struct shader_runner *runner, const char *line, const char **const rest)
|
|
{
|
|
bool holds = true;
|
|
unsigned int i;
|
|
|
|
static const struct
|
|
{
|
|
const char *text;
|
|
enum shader_model sm_min, sm_max;
|
|
bool tag;
|
|
}
|
|
valid_args[] =
|
|
{
|
|
{"sm>=4", SHADER_MODEL_4_0, SHADER_MODEL_6_0},
|
|
{"sm>=6", SHADER_MODEL_6_0, SHADER_MODEL_6_0},
|
|
{"sm<4", SHADER_MODEL_2_0, SHADER_MODEL_4_0 - 1},
|
|
{"sm<6", SHADER_MODEL_2_0, SHADER_MODEL_6_0 - 1},
|
|
{"glsl", 0, 0, true},
|
|
{"d3d12", 0, 0, true},
|
|
{"mvk", 0, 0, true},
|
|
{"vulkan", 0, 0, true},
|
|
};
|
|
|
|
while (*line != ')' && *line != '|')
|
|
{
|
|
bool match = false;
|
|
|
|
while (isspace(*line))
|
|
++line;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(valid_args); ++i)
|
|
{
|
|
const char *option_text = valid_args[i].text;
|
|
size_t option_len = strlen(option_text);
|
|
|
|
if (strncmp(line, option_text, option_len))
|
|
continue;
|
|
|
|
match = true;
|
|
line += option_len;
|
|
if (valid_args[i].tag)
|
|
holds &= match_tag(runner, option_text);
|
|
else if (runner->minimum_shader_model < valid_args[i].sm_min
|
|
|| runner->minimum_shader_model > valid_args[i].sm_max)
|
|
holds = false;
|
|
|
|
break;
|
|
}
|
|
|
|
while (isspace(*line))
|
|
++line;
|
|
|
|
if (match && *line == '&')
|
|
{
|
|
++line;
|
|
}
|
|
else if (*line != ')' && *line != '|')
|
|
{
|
|
fatal_error("Invalid qualifier argument '%s'.\n", line);
|
|
}
|
|
}
|
|
|
|
assert(*line == ')' || *line == '|');
|
|
if (rest)
|
|
*rest = line;
|
|
|
|
return holds;
|
|
}
|
|
|
|
static bool check_qualifier_args(struct shader_runner *runner, const char *line, const char **const rest)
|
|
{
|
|
bool first = true;
|
|
bool holds = false;
|
|
|
|
assert(*line == '(');
|
|
++line;
|
|
|
|
while (*line != ')')
|
|
{
|
|
if (!first && *line == '|')
|
|
++line;
|
|
first = false;
|
|
|
|
holds = check_qualifier_args_conjunction(runner, line, &line) || holds;
|
|
}
|
|
|
|
assert(*line == ')');
|
|
if (rest)
|
|
*rest = line + 1;
|
|
|
|
return holds;
|
|
}
|
|
|
|
static bool match_string_generic(struct shader_runner *runner, const char *line, const char *token,
|
|
const char **const rest, bool exclude_bracket, bool allow_qualifier_args)
|
|
{
|
|
size_t len = strlen(token);
|
|
bool holds = true;
|
|
|
|
while (isspace(*line) || (exclude_bracket && *line == ']'))
|
|
++line;
|
|
|
|
if (strncmp(line, token, len) || !(isspace(line[len]) || line[len] == '('
|
|
|| (exclude_bracket && line[len] == ']')))
|
|
return false;
|
|
line += len;
|
|
|
|
if (allow_qualifier_args && *line == '(')
|
|
holds = check_qualifier_args(runner, line, &line);
|
|
|
|
if (rest)
|
|
{
|
|
*rest = line;
|
|
while (isspace(**rest))
|
|
++*rest;
|
|
}
|
|
return holds;
|
|
}
|
|
|
|
static bool match_string_with_args(struct shader_runner *runner,
|
|
const char *line, const char *token, const char **const rest)
|
|
{
|
|
return match_string_generic(runner, line, token, rest, false, true);
|
|
}
|
|
|
|
static bool match_string(const char *line, const char *token, const char **const rest)
|
|
{
|
|
return match_string_generic(NULL, line, token, rest, false, false);
|
|
}
|
|
|
|
static bool match_directive_substring_with_args(struct shader_runner *runner,
|
|
const char *line, const char *token, const char **const rest)
|
|
{
|
|
return match_string_generic(runner, line, token, rest, true, true);
|
|
}
|
|
|
|
static bool match_directive_substring(const char *line, const char *token, const char **const rest)
|
|
{
|
|
return match_string_generic(NULL, line, token, rest, true, false);
|
|
}
|
|
|
|
static const char *close_parentheses(const char *line)
|
|
{
|
|
while (isspace(*line))
|
|
++line;
|
|
|
|
if (*line != ')')
|
|
fatal_error("Malformed probe arguments '%s'.\n", line);
|
|
|
|
return line;
|
|
}
|
|
|
|
static DXGI_FORMAT parse_format(const char *line, enum texture_data_type *data_type, unsigned int *texel_size,
|
|
bool *is_shadow, const char **rest)
|
|
{
|
|
static const struct
|
|
{
|
|
const char *string;
|
|
enum texture_data_type data_type;
|
|
unsigned int texel_size;
|
|
DXGI_FORMAT format;
|
|
bool is_shadow;
|
|
}
|
|
formats[] =
|
|
{
|
|
{"r32g32b32a32-float", TEXTURE_DATA_FLOAT, 16, DXGI_FORMAT_R32G32B32A32_FLOAT},
|
|
{"r32g32b32a32-sint", TEXTURE_DATA_SINT, 16, DXGI_FORMAT_R32G32B32A32_SINT},
|
|
{"r32g32b32a32-uint", TEXTURE_DATA_UINT, 16, DXGI_FORMAT_R32G32B32A32_UINT},
|
|
{"r32g32-float", TEXTURE_DATA_FLOAT, 8, DXGI_FORMAT_R32G32_FLOAT},
|
|
{"r32g32-sint", TEXTURE_DATA_SINT, 8, DXGI_FORMAT_R32G32_SINT},
|
|
{"r32g32-uint", TEXTURE_DATA_UINT, 8, DXGI_FORMAT_R32G32_UINT},
|
|
{"r32-float-shadow", TEXTURE_DATA_FLOAT, 4, DXGI_FORMAT_R32_FLOAT, true},
|
|
{"r32-float", TEXTURE_DATA_FLOAT, 4, DXGI_FORMAT_R32_FLOAT},
|
|
{"r32-sint", TEXTURE_DATA_SINT, 4, DXGI_FORMAT_R32_SINT},
|
|
{"r32-uint", TEXTURE_DATA_UINT, 4, DXGI_FORMAT_R32_UINT},
|
|
{"r32-typeless", TEXTURE_DATA_UINT, 4, DXGI_FORMAT_R32_TYPELESS},
|
|
{"unknown", TEXTURE_DATA_UINT, 0, DXGI_FORMAT_UNKNOWN},
|
|
};
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(formats); ++i)
|
|
{
|
|
if (match_string(line, formats[i].string, rest))
|
|
{
|
|
if (data_type)
|
|
*data_type = formats[i].data_type;
|
|
if (texel_size)
|
|
*texel_size = formats[i].texel_size;
|
|
if (is_shadow)
|
|
*is_shadow = formats[i].is_shadow;
|
|
return formats[i].format;
|
|
}
|
|
}
|
|
|
|
fatal_error("Unknown format '%s'.\n", line);
|
|
}
|
|
|
|
static const char *const shader_cap_strings[] =
|
|
{
|
|
[SHADER_CAP_CLIP_PLANES] = "clip-planes",
|
|
[SHADER_CAP_DEPTH_BOUNDS] = "depth-bounds",
|
|
[SHADER_CAP_FLOAT64] = "float64",
|
|
[SHADER_CAP_GEOMETRY_SHADER] = "geometry-shader",
|
|
[SHADER_CAP_INT64] = "int64",
|
|
[SHADER_CAP_POINT_SIZE] = "point-size",
|
|
[SHADER_CAP_ROV] = "rov",
|
|
[SHADER_CAP_WAVE_OPS] = "wave-ops",
|
|
};
|
|
|
|
static bool match_shader_cap_string(const char *line, enum shader_cap *cap)
|
|
{
|
|
for (enum shader_cap i = 0; i < SHADER_CAP_COUNT; ++i)
|
|
{
|
|
if (match_string(line, shader_cap_strings[i], &line))
|
|
{
|
|
*cap = i;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void parse_require_directive(struct shader_runner *runner, const char *line)
|
|
{
|
|
enum shader_cap shader_cap;
|
|
bool less_than = false;
|
|
unsigned int i;
|
|
|
|
if (match_string(line, "shader model >=", &line)
|
|
|| (less_than = match_string(line, "shader model <", &line)))
|
|
{
|
|
for (i = 0; i < ARRAY_SIZE(model_strings); ++i)
|
|
{
|
|
if (match_string(line, model_strings[i], &line))
|
|
{
|
|
if (less_than)
|
|
{
|
|
if (!i)
|
|
fatal_error("Shader model < '%s' is invalid.\n", line);
|
|
runner->maximum_shader_model = min(runner->maximum_shader_model, i - 1);
|
|
}
|
|
else
|
|
{
|
|
runner->minimum_shader_model = max(runner->minimum_shader_model, i);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
fatal_error("Unknown shader model '%s'.\n", line);
|
|
}
|
|
else if (match_string(line, "options:", &line))
|
|
{
|
|
static const struct option
|
|
{
|
|
unsigned int option;
|
|
const char *name;
|
|
}
|
|
options[] =
|
|
{
|
|
{ 0, "none" },
|
|
{ D3DCOMPILE_PACK_MATRIX_ROW_MAJOR, "row-major" },
|
|
{ D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR, "column-major" },
|
|
{ D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY, "backcompat" },
|
|
{ D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES, "unbounded-descriptor-arrays" },
|
|
};
|
|
|
|
runner->compile_options = 0;
|
|
for (i = 0; i < ARRAY_SIZE(options); ++i)
|
|
{
|
|
if (match_string(line, options[i].name, &line))
|
|
runner->compile_options |= options[i].option;
|
|
}
|
|
}
|
|
else if (match_string(line, "format", &line))
|
|
{
|
|
DXGI_FORMAT format = parse_format(line, NULL, NULL, NULL, &line);
|
|
|
|
while (line[0] != '\0')
|
|
{
|
|
if (match_string(line, "uav-load", &line))
|
|
runner->require_format_caps[format] |= FORMAT_CAP_UAV_LOAD;
|
|
else
|
|
fatal_error("Unknown format cap '%s'.\n", line);
|
|
}
|
|
}
|
|
else if (match_shader_cap_string(line, &shader_cap))
|
|
{
|
|
runner->require_shader_caps[shader_cap] = true;
|
|
}
|
|
else
|
|
{
|
|
fatal_error("Unknown require directive '%s'.\n", line);
|
|
}
|
|
}
|
|
|
|
static D3D12_COMPARISON_FUNC parse_comparison_func(const char *line, const char **rest)
|
|
{
|
|
static const struct
|
|
{
|
|
const char *string;
|
|
D3D12_COMPARISON_FUNC func;
|
|
}
|
|
funcs[] =
|
|
{
|
|
{"less equal", D3D12_COMPARISON_FUNC_LESS_EQUAL},
|
|
{"not equal", D3D12_COMPARISON_FUNC_NOT_EQUAL},
|
|
{"greater equal", D3D12_COMPARISON_FUNC_GREATER_EQUAL},
|
|
{"never", D3D12_COMPARISON_FUNC_NEVER},
|
|
{"less", D3D12_COMPARISON_FUNC_LESS},
|
|
{"equal", D3D12_COMPARISON_FUNC_EQUAL},
|
|
{"greater", D3D12_COMPARISON_FUNC_GREATER},
|
|
{"always", D3D12_COMPARISON_FUNC_ALWAYS},
|
|
};
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(funcs); ++i)
|
|
{
|
|
if (match_string(line, funcs[i].string, rest))
|
|
return funcs[i].func;
|
|
}
|
|
|
|
fatal_error("Unknown comparison func '%s'.\n", line);
|
|
}
|
|
|
|
static D3D12_TEXTURE_ADDRESS_MODE parse_sampler_address_mode(const char *line, const char **rest)
|
|
{
|
|
if (match_string(line, "border", rest))
|
|
return D3D12_TEXTURE_ADDRESS_MODE_BORDER;
|
|
if (match_string(line, "clamp", rest))
|
|
return D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
|
if (match_string(line, "mirror_once", rest))
|
|
return D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE;
|
|
if (match_string(line, "mirror", rest))
|
|
return D3D12_TEXTURE_ADDRESS_MODE_MIRROR;
|
|
if (match_string(line, "wrap", rest))
|
|
return D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
|
|
|
fatal_error("Unknown sampler address mode '%s'.\n", line);
|
|
}
|
|
|
|
static void parse_sampler_directive(struct sampler *sampler, const char *line)
|
|
{
|
|
if (match_string(line, "address", &line))
|
|
{
|
|
sampler->u_address = parse_sampler_address_mode(line, &line);
|
|
sampler->v_address = parse_sampler_address_mode(line, &line);
|
|
sampler->w_address = parse_sampler_address_mode(line, &line);
|
|
}
|
|
else if (match_string(line, "filter", &line))
|
|
{
|
|
static const struct
|
|
{
|
|
const char *string;
|
|
D3D12_FILTER filter;
|
|
}
|
|
filters[] =
|
|
{
|
|
{"point point point", D3D12_FILTER_MIN_MAG_MIP_POINT},
|
|
{"point point linear", D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR},
|
|
{"point linear point", D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT},
|
|
{"point linear linear", D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR},
|
|
{"linear point point", D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT},
|
|
{"linear point linear", D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR},
|
|
{"linear linear point", D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT},
|
|
{"linear linear linear", D3D12_FILTER_MIN_MAG_MIP_LINEAR},
|
|
};
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(filters); ++i)
|
|
{
|
|
if (match_string(line, filters[i].string, &line))
|
|
{
|
|
sampler->filter = filters[i].filter;
|
|
if (sampler->func)
|
|
sampler->filter |= D3D12_FILTER_REDUCTION_TYPE_COMPARISON << D3D12_FILTER_REDUCTION_TYPE_SHIFT;
|
|
return;
|
|
}
|
|
}
|
|
|
|
fatal_error("Unknown sampler filter '%s'.\n", line);
|
|
}
|
|
else if (match_string(line, "comparison", &line))
|
|
{
|
|
sampler->filter |= D3D12_FILTER_REDUCTION_TYPE_COMPARISON << D3D12_FILTER_REDUCTION_TYPE_SHIFT;
|
|
sampler->func = parse_comparison_func(line, &line);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
fatal_error("Unknown sampler directive '%s'.\n", line);
|
|
}
|
|
}
|
|
|
|
static void parse_resource_directive(struct resource_params *resource, const char *line)
|
|
{
|
|
if (match_string(line, "format", &line))
|
|
{
|
|
resource->desc.format = parse_format(line, &resource->data_type,
|
|
&resource->desc.texel_size, &resource->is_shadow, &line);
|
|
assert_that(!resource->explicit_format, "Resource format already specified.\n");
|
|
resource->explicit_format = true;
|
|
}
|
|
else if (match_string(line, "stride", &line))
|
|
{
|
|
if (sscanf(line, "%u", &resource->stride) < 1)
|
|
fatal_error("Malformed texture stride '%s'.\n", line);
|
|
resource->desc.texel_size = resource->stride;
|
|
resource->desc.format = DXGI_FORMAT_UNKNOWN;
|
|
assert_that(!resource->explicit_format, "Resource format already specified.\n");
|
|
resource->explicit_format = true;
|
|
}
|
|
else if (match_string(line, "size", &line))
|
|
{
|
|
if (sscanf(line, "( buffer , %u ) ", &resource->desc.width) == 1)
|
|
{
|
|
resource->desc.dimension = RESOURCE_DIMENSION_BUFFER;
|
|
resource->desc.height = 1;
|
|
}
|
|
else if (sscanf(line, "( raw_buffer , %u ) ", &resource->desc.width) == 1)
|
|
{
|
|
resource->desc.dimension = RESOURCE_DIMENSION_BUFFER;
|
|
resource->desc.height = 1;
|
|
resource->is_raw = true;
|
|
}
|
|
else if (sscanf(line, "( counter_buffer , %u ) ", &resource->desc.width) == 1)
|
|
{
|
|
resource->desc.dimension = RESOURCE_DIMENSION_BUFFER;
|
|
resource->desc.height = 1;
|
|
resource->is_uav_counter = true;
|
|
resource->stride = sizeof(uint32_t);
|
|
resource->desc.texel_size = resource->stride;
|
|
resource->desc.format = DXGI_FORMAT_UNKNOWN;
|
|
assert_that(!resource->explicit_format, "Resource format already specified.\n");
|
|
resource->explicit_format = true;
|
|
}
|
|
else if (sscanf(line, "( 2d , %u , %u ) ", &resource->desc.width, &resource->desc.height) == 2)
|
|
{
|
|
resource->desc.dimension = RESOURCE_DIMENSION_2D;
|
|
}
|
|
else if (sscanf(line, "( 2dms , %u , %u , %u ) ",
|
|
&resource->desc.sample_count, &resource->desc.width, &resource->desc.height) == 3)
|
|
{
|
|
resource->desc.dimension = RESOURCE_DIMENSION_2D;
|
|
}
|
|
else
|
|
{
|
|
fatal_error("Malformed resource size '%s'.\n", line);
|
|
}
|
|
}
|
|
else if (match_string(line, "levels", &line))
|
|
{
|
|
char *rest;
|
|
|
|
resource->desc.level_count = strtoul(line, &rest, 10);
|
|
if (rest == line)
|
|
fatal_error("Malformed texture directive '%s'.\n", line);
|
|
}
|
|
else
|
|
{
|
|
union
|
|
{
|
|
float f;
|
|
int32_t i;
|
|
uint32_t u;
|
|
} u;
|
|
char *rest;
|
|
|
|
u.u = 0;
|
|
|
|
for (;;)
|
|
{
|
|
switch (resource->data_type)
|
|
{
|
|
case TEXTURE_DATA_FLOAT:
|
|
u.f = strtof(line, &rest);
|
|
break;
|
|
|
|
case TEXTURE_DATA_SINT:
|
|
u.i = strtol(line, &rest, 0);
|
|
break;
|
|
|
|
case TEXTURE_DATA_UINT:
|
|
u.u = strtoul(line, &rest, 0);
|
|
break;
|
|
}
|
|
|
|
if (rest == line)
|
|
break;
|
|
|
|
vkd3d_array_reserve((void **)&resource->data, &resource->data_capacity, resource->data_size + sizeof(u), 1);
|
|
memcpy(resource->data + resource->data_size, &u, sizeof(u));
|
|
resource->data_size += sizeof(u);
|
|
line = rest;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void parse_input_layout_directive(struct shader_runner *runner, const char *line)
|
|
{
|
|
struct input_element *element;
|
|
const char *rest;
|
|
|
|
vkd3d_array_reserve((void **)&runner->input_elements, &runner->input_element_capacity,
|
|
runner->input_element_count + 1, sizeof(*runner->input_elements));
|
|
element = &runner->input_elements[runner->input_element_count++];
|
|
|
|
element->slot = strtoul(line, (char **)&rest, 10);
|
|
if (rest == line)
|
|
fatal_error("Malformed input layout directive '%s'.\n", line);
|
|
line = rest;
|
|
|
|
element->format = parse_format(line, NULL, &element->texel_size, NULL, &line);
|
|
|
|
if (!(rest = strpbrk(line, " \n")))
|
|
rest = line + strlen(line);
|
|
element->name = malloc(rest - line + 1);
|
|
memcpy(element->name, line, rest - line);
|
|
element->name[rest - line] = 0;
|
|
line = rest;
|
|
|
|
element->index = strtoul(line, (char **)&rest, 10);
|
|
if (rest == line)
|
|
element->index = 0;
|
|
}
|
|
|
|
void init_resource(struct resource *resource, const struct resource_params *params)
|
|
{
|
|
resource->desc = params->desc;
|
|
}
|
|
|
|
struct resource *shader_runner_get_resource(struct shader_runner *runner, enum resource_type type, unsigned int slot)
|
|
{
|
|
struct resource *resource;
|
|
size_t i;
|
|
|
|
for (i = 0; i < runner->resource_count; ++i)
|
|
{
|
|
resource = runner->resources[i];
|
|
|
|
if (resource->desc.type == type && resource->desc.slot == slot)
|
|
return resource;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void set_resource(struct shader_runner *runner, const struct resource_params *params)
|
|
{
|
|
struct resource *resource;
|
|
size_t i;
|
|
|
|
if (!(resource = runner->ops->create_resource(runner, params)))
|
|
{
|
|
if (!bitmap_is_set(runner->failed_resources[params->desc.type], params->desc.slot))
|
|
{
|
|
++runner->failed_resource_count;
|
|
bitmap_set(runner->failed_resources[params->desc.type], params->desc.slot);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (bitmap_is_set(runner->failed_resources[params->desc.type], params->desc.slot))
|
|
{
|
|
assert(runner->failed_resource_count);
|
|
--runner->failed_resource_count;
|
|
bitmap_clear(runner->failed_resources[params->desc.type], params->desc.slot);
|
|
}
|
|
|
|
for (i = 0; i < runner->resource_count; ++i)
|
|
{
|
|
if (runner->resources[i]->desc.slot == resource->desc.slot
|
|
&& runner->resources[i]->desc.type == resource->desc.type)
|
|
{
|
|
runner->ops->destroy_resource(runner, runner->resources[i]);
|
|
runner->resources[i] = resource;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (runner->resource_count == MAX_RESOURCES)
|
|
fatal_error("Too many resources declared.\n");
|
|
|
|
runner->resources[runner->resource_count++] = resource;
|
|
}
|
|
|
|
static void set_default_target(struct shader_runner *runner)
|
|
{
|
|
struct resource_params params = {0};
|
|
|
|
if (shader_runner_get_resource(runner, RESOURCE_TYPE_RENDER_TARGET, 0)
|
|
|| shader_runner_get_resource(runner, RESOURCE_TYPE_DEPTH_STENCIL, 0))
|
|
return;
|
|
|
|
params.desc.slot = 0;
|
|
params.desc.type = RESOURCE_TYPE_RENDER_TARGET;
|
|
params.desc.dimension = RESOURCE_DIMENSION_2D;
|
|
params.desc.format = DXGI_FORMAT_R32G32B32A32_FLOAT;
|
|
params.data_type = TEXTURE_DATA_FLOAT;
|
|
params.desc.texel_size = 16;
|
|
params.desc.width = RENDER_TARGET_WIDTH;
|
|
params.desc.height = RENDER_TARGET_HEIGHT;
|
|
params.desc.level_count = 1;
|
|
|
|
set_resource(runner, ¶ms);
|
|
}
|
|
|
|
static void set_uniforms(struct shader_runner *runner, size_t offset, size_t count, const void *uniforms)
|
|
{
|
|
size_t initial_count = runner->uniform_count;
|
|
|
|
runner->uniform_count = align(max(runner->uniform_count, offset + count), 4);
|
|
vkd3d_array_reserve((void **)&runner->uniforms, &runner->uniform_capacity,
|
|
runner->uniform_count, sizeof(*runner->uniforms));
|
|
memset(runner->uniforms + initial_count, 127,
|
|
(runner->uniform_count - initial_count) * sizeof(*runner->uniforms));
|
|
memcpy(runner->uniforms + offset, uniforms, count * sizeof(*runner->uniforms));
|
|
}
|
|
|
|
static void read_int(const char **line, int *i, bool is_uniform)
|
|
{
|
|
char *rest;
|
|
long val;
|
|
|
|
errno = 0;
|
|
val = strtol(*line, &rest, 0);
|
|
|
|
if (errno != 0 || (is_uniform && *rest != '\0' && !isspace((unsigned char)*rest)))
|
|
fatal_error("Malformed int constant '%s'.\n", *line);
|
|
|
|
*i = val;
|
|
if (*i != val)
|
|
fatal_error("Out of range int constant '%.*s'.\n", (int)(rest - *line), *line);
|
|
|
|
*line = rest + (!is_uniform && *rest == ',');
|
|
}
|
|
|
|
static void read_uint(const char **line, unsigned int *u, bool is_uniform)
|
|
{
|
|
char *rest;
|
|
unsigned long val;
|
|
|
|
errno = 0;
|
|
val = strtoul(*line, &rest, 0);
|
|
|
|
if (errno != 0 || (is_uniform && *rest != '\0' && !isspace((unsigned char)*rest)))
|
|
fatal_error("Malformed uint constant '%s'.\n", *line);
|
|
|
|
*u = val;
|
|
if (*u != val)
|
|
fatal_error("Out of range uint constant '%.*s'.\n", (int)(rest - *line), *line);
|
|
|
|
*line = rest + (!is_uniform && *rest == ',');
|
|
}
|
|
|
|
static void read_int4(const char **line, struct ivec4 *v, bool is_uniform)
|
|
{
|
|
read_int(line, &v->x, is_uniform);
|
|
read_int(line, &v->y, is_uniform);
|
|
read_int(line, &v->z, is_uniform);
|
|
read_int(line, &v->w, is_uniform);
|
|
}
|
|
|
|
static void read_uint4(const char **line, struct uvec4 *v, bool is_uniform)
|
|
{
|
|
read_uint(line, &v->x, is_uniform);
|
|
read_uint(line, &v->y, is_uniform);
|
|
read_uint(line, &v->z, is_uniform);
|
|
read_uint(line, &v->w, is_uniform);
|
|
}
|
|
|
|
static void read_int64(const char **line, int64_t *i, bool is_uniform)
|
|
{
|
|
char *rest;
|
|
int64_t val;
|
|
|
|
errno = 0;
|
|
val = strtoll(*line, &rest, 0);
|
|
|
|
if (errno != 0 || (is_uniform && *rest != '\0' && !isspace((unsigned char)*rest)))
|
|
fatal_error("Malformed int64 constant '%s'.\n", *line);
|
|
|
|
*i = val;
|
|
*line = rest + (!is_uniform && *rest == ',');
|
|
}
|
|
|
|
static void read_uint64(const char **line, uint64_t *u, bool is_uniform)
|
|
{
|
|
char *rest;
|
|
uint64_t val;
|
|
|
|
errno = 0;
|
|
val = strtoull(*line, &rest, 0);
|
|
|
|
if (errno != 0 || (is_uniform && *rest != '\0' && !isspace((unsigned char)*rest)))
|
|
fatal_error("Malformed uint64 constant '%s'.\n", *line);
|
|
|
|
*u = val;
|
|
*line = rest + (!is_uniform && *rest == ',');
|
|
}
|
|
|
|
static void read_int64_t2(const char **line, struct i64vec2 *v)
|
|
{
|
|
read_int64(line, &v->x, true);
|
|
read_int64(line, &v->y, true);
|
|
}
|
|
|
|
static void read_uint64_t2(const char **line, struct u64vec2 *v)
|
|
{
|
|
read_uint64(line, &v->x, true);
|
|
read_uint64(line, &v->y, true);
|
|
}
|
|
|
|
static void parse_test_directive(struct shader_runner *runner, const char *line)
|
|
{
|
|
bool skip_directive = false;
|
|
const char *line_ini;
|
|
bool match = true;
|
|
char *rest;
|
|
int ret;
|
|
|
|
runner->is_todo = false;
|
|
|
|
while (match)
|
|
{
|
|
match = false;
|
|
|
|
if (match_string_with_args(runner, line, "todo", &line))
|
|
{
|
|
runner->is_todo = true;
|
|
match = true;
|
|
}
|
|
|
|
line_ini = line;
|
|
if (match_string_with_args(runner, line, "if", &line))
|
|
{
|
|
match = true;
|
|
}
|
|
else if (line != line_ini)
|
|
{
|
|
/* Matched "if" but for other shader models. */
|
|
skip_directive = true;
|
|
match = true;
|
|
}
|
|
}
|
|
|
|
if (skip_directive)
|
|
{
|
|
const char *new_line;
|
|
|
|
if ((new_line = strchr(line, '\n')))
|
|
line = new_line + 1;
|
|
else
|
|
line += strlen(line);
|
|
return;
|
|
}
|
|
|
|
if (match_string(line, "dispatch", &line))
|
|
{
|
|
unsigned int x, y, z;
|
|
|
|
ret = sscanf(line, "%u %u %u", &x, &y, &z);
|
|
if (ret < 3)
|
|
fatal_error("Malformed dispatch arguments '%s'.\n", line);
|
|
|
|
runner->last_render_failed = !runner->ops->dispatch(runner, x, y, z);
|
|
}
|
|
else if (match_string(line, "clear rtv", &line))
|
|
{
|
|
struct resource *resource;
|
|
unsigned int slot;
|
|
struct vec4 v;
|
|
|
|
if (sscanf(line, "%u %f %f %f %f", &slot, &v.x, &v.y, &v.z, &v.w) < 5)
|
|
fatal_error("Malformed rtv clear arguments '%s'.\n", line);
|
|
|
|
set_default_target(runner);
|
|
|
|
if (!(resource = shader_runner_get_resource(runner, RESOURCE_TYPE_RENDER_TARGET, slot)))
|
|
fatal_error("Resource not found.\n");
|
|
runner->ops->clear(runner, resource, &v);
|
|
}
|
|
else if (match_string(line, "clear dsv", &line))
|
|
{
|
|
struct resource *resource;
|
|
struct vec4 v = {0};
|
|
|
|
if (!sscanf(line, "%f", &v.x))
|
|
fatal_error("Malformed dsv clear arguments '%s'.\n", line);
|
|
|
|
if (!(resource = shader_runner_get_resource(runner, RESOURCE_TYPE_DEPTH_STENCIL, 0)))
|
|
fatal_error("Resource not found.\n");
|
|
runner->ops->clear(runner, resource, &v);
|
|
}
|
|
else if (match_string(line, "depth-bounds", &line))
|
|
{
|
|
if (sscanf(line, "%f %f", &runner->depth_min, &runner->depth_max) != 2)
|
|
fatal_error("Malformed depth-bounds arguments '%s'.\n", line);
|
|
if (!runner->caps->shader_caps[SHADER_CAP_DEPTH_BOUNDS])
|
|
fatal_error("depth-bounds set but runner does not support depth bounds testing.");
|
|
runner->depth_bounds = true;
|
|
}
|
|
else if (match_string(line, "depth", &line))
|
|
{
|
|
runner->depth_func = parse_comparison_func(line, &line);
|
|
}
|
|
else if (match_string(line, "draw quad", &line))
|
|
{
|
|
struct resource_params params;
|
|
struct input_element *element;
|
|
unsigned int i;
|
|
|
|
/* For simplicity, draw a large triangle instead. */
|
|
static const struct vec2 quad[] =
|
|
{
|
|
{-2.0f, -2.0f},
|
|
{-2.0f, 4.0f},
|
|
{ 4.0f, -2.0f},
|
|
};
|
|
|
|
static const char vs_source[] =
|
|
"float4 main(float4 pos : position) : sv_position\n"
|
|
"{\n"
|
|
" return pos;\n"
|
|
"}";
|
|
|
|
if (!runner->shader_source[SHADER_TYPE_HS] != !runner->shader_source[SHADER_TYPE_DS])
|
|
fatal_error("Have a domain or hull shader but not both.\n");
|
|
|
|
set_default_target(runner);
|
|
|
|
for (i = 0; i < runner->input_element_count; ++i)
|
|
free(runner->input_elements[i].name);
|
|
|
|
vkd3d_array_reserve((void **)&runner->input_elements, &runner->input_element_capacity,
|
|
1, sizeof(*runner->input_elements));
|
|
element = &runner->input_elements[0];
|
|
element->name = strdup("position");
|
|
element->slot = 0;
|
|
element->format = DXGI_FORMAT_R32G32_FLOAT;
|
|
element->texel_size = sizeof(*quad);
|
|
element->index = 0;
|
|
runner->input_element_count = 1;
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
params.desc.slot = 0;
|
|
params.desc.type = RESOURCE_TYPE_VERTEX_BUFFER;
|
|
params.desc.dimension = RESOURCE_DIMENSION_BUFFER;
|
|
params.desc.width = sizeof(quad);
|
|
params.data = malloc(sizeof(quad));
|
|
memcpy(params.data, quad, sizeof(quad));
|
|
params.data_size = sizeof(quad);
|
|
set_resource(runner, ¶ms);
|
|
free(params.data);
|
|
|
|
if (!runner->shader_source[SHADER_TYPE_VS])
|
|
runner->shader_source[SHADER_TYPE_VS] = strdup(vs_source);
|
|
|
|
runner->sample_count = 1;
|
|
for (i = 0; i < runner->resource_count; ++i)
|
|
{
|
|
if (runner->resources[i]->desc.type == RESOURCE_TYPE_RENDER_TARGET
|
|
|| runner->resources[i]->desc.type == RESOURCE_TYPE_DEPTH_STENCIL)
|
|
runner->sample_count = max(runner->sample_count, runner->resources[i]->desc.sample_count);
|
|
}
|
|
|
|
runner->last_render_failed = !runner->ops->draw(runner, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, 3, 1);
|
|
}
|
|
else if (match_string(line, "draw", &line))
|
|
{
|
|
unsigned int vertex_count, instance_count;
|
|
D3D_PRIMITIVE_TOPOLOGY topology;
|
|
|
|
if (!runner->shader_source[SHADER_TYPE_HS] != !runner->shader_source[SHADER_TYPE_DS])
|
|
fatal_error("Have a domain or hull shader but not both.\n");
|
|
|
|
set_default_target(runner);
|
|
|
|
if (match_string(line, "triangle list", &line))
|
|
topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
|
|
else if (match_string(line, "triangle strip", &line))
|
|
topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
|
|
else if (match_string(line, "point list", &line))
|
|
topology = D3D_PRIMITIVE_TOPOLOGY_POINTLIST;
|
|
else if (match_string(line, "1 control point patch list", &line))
|
|
topology = D3D_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST;
|
|
else if (match_string(line, "2 control point patch list", &line))
|
|
topology = D3D_PRIMITIVE_TOPOLOGY_2_CONTROL_POINT_PATCHLIST;
|
|
else if (match_string(line, "3 control point patch list", &line))
|
|
topology = D3D_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST;
|
|
else if (match_string(line, "4 control point patch list", &line))
|
|
topology = D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST;
|
|
else
|
|
fatal_error("Unknown primitive topology '%s'.\n", line);
|
|
|
|
vertex_count = strtoul(line, &rest, 10);
|
|
if (line == rest)
|
|
fatal_error("Malformed vertex count '%s'.\n", line);
|
|
instance_count = strtoul(line = rest, &rest, 10);
|
|
if (line == rest)
|
|
instance_count = 1;
|
|
|
|
runner->sample_count = 1;
|
|
for (unsigned int i = 0; i < runner->resource_count; ++i)
|
|
{
|
|
if (runner->resources[i]->desc.type == RESOURCE_TYPE_RENDER_TARGET
|
|
|| runner->resources[i]->desc.type == RESOURCE_TYPE_DEPTH_STENCIL)
|
|
runner->sample_count = max(runner->sample_count, runner->resources[i]->desc.sample_count);
|
|
}
|
|
|
|
runner->last_render_failed = !runner->ops->draw(runner, topology, vertex_count, instance_count);
|
|
}
|
|
else if (match_string(line, "probe", &line))
|
|
{
|
|
unsigned int left, top, right, bottom, ulps, slot;
|
|
struct resource_readback *rb;
|
|
struct resource *resource;
|
|
bool is_signed = false;
|
|
RECT rect;
|
|
int len;
|
|
|
|
if (runner->last_render_failed)
|
|
return;
|
|
|
|
if (match_string(line, "uav", &line))
|
|
{
|
|
slot = strtoul(line, &rest, 10);
|
|
|
|
if (rest == line)
|
|
fatal_error("Malformed UAV index '%s'.\n", line);
|
|
line = rest;
|
|
|
|
resource = shader_runner_get_resource(runner, RESOURCE_TYPE_UAV, slot);
|
|
}
|
|
else if (match_string(line, "rtv", &line))
|
|
{
|
|
slot = strtoul(line, &rest, 10);
|
|
|
|
if (rest == line)
|
|
fatal_error("Malformed render target index '%s'.\n", line);
|
|
line = rest;
|
|
|
|
resource = shader_runner_get_resource(runner, RESOURCE_TYPE_RENDER_TARGET, slot);
|
|
}
|
|
else if (match_string(line, "dsv", &line))
|
|
{
|
|
resource = shader_runner_get_resource(runner, RESOURCE_TYPE_DEPTH_STENCIL, 0);
|
|
}
|
|
else
|
|
{
|
|
resource = shader_runner_get_resource(runner, RESOURCE_TYPE_RENDER_TARGET, 0);
|
|
}
|
|
|
|
rb = runner->ops->get_resource_readback(runner, resource);
|
|
|
|
if (sscanf(line, " ( %d , %d , %d , %d )%n", &left, &top, &right, &bottom, &len) == 4)
|
|
{
|
|
set_rect(&rect, left, top, right, bottom);
|
|
line += len;
|
|
}
|
|
else if (sscanf(line, " ( %u , %u )%n", &left, &top, &len) == 2)
|
|
{
|
|
set_rect(&rect, left, top, left + 1, top + 1);
|
|
line += len;
|
|
}
|
|
else if (sscanf(line, " ( %u )%n", &left, &len) == 1)
|
|
{
|
|
set_rect(&rect, left, 0, left + 1, 1);
|
|
line += len;
|
|
}
|
|
else
|
|
{
|
|
fatal_error("Malformed probe arguments '%s'.\n", line);
|
|
}
|
|
|
|
if (match_string(line, "rgbaui", &line))
|
|
{
|
|
struct uvec4 v;
|
|
|
|
if (*line != '(')
|
|
fatal_error("Malformed probe arguments '%s'.\n", line);
|
|
++line;
|
|
read_uint4(&line, &v, false);
|
|
line = close_parentheses(line);
|
|
todo_if(runner->is_todo) check_readback_data_uvec4(rb, &rect, &v);
|
|
}
|
|
else if (match_string(line, "rgbai", &line))
|
|
{
|
|
struct ivec4 v;
|
|
|
|
if (*line != '(')
|
|
fatal_error("Malformed probe arguments '%s'.\n", line);
|
|
++line;
|
|
read_int4(&line, &v, false);
|
|
line = close_parentheses(line);
|
|
todo_if(runner->is_todo) check_readback_data_ivec4(rb, &rect, &v);
|
|
}
|
|
else if (match_string(line, "rgba", &line))
|
|
{
|
|
struct vec4 v;
|
|
|
|
ret = sscanf(line, "( %f , %f , %f , %f ) %u", &v.x, &v.y, &v.z, &v.w, &ulps);
|
|
if (ret < 4)
|
|
fatal_error("Malformed probe arguments '%s'.\n", line);
|
|
if (ret < 5)
|
|
ulps = 0;
|
|
todo_if(runner->is_todo) check_readback_data_vec4(rb, &rect, &v, ulps);
|
|
}
|
|
else if (match_string(line, "rg", &line))
|
|
{
|
|
struct vec4 v;
|
|
|
|
ret = sscanf(line, "( %f , %f ) %u", &v.x, &v.y, &ulps);
|
|
if (ret < 2)
|
|
fatal_error("Malformed probe arguments '%s'.\n", line);
|
|
if (ret < 3)
|
|
ulps = 0;
|
|
todo_if(runner->is_todo) check_readback_data_vec2(rb, &rect, &v, ulps);
|
|
}
|
|
else if (match_string(line, "rui", &line) || (is_signed = match_string(line, "ri", &line)))
|
|
{
|
|
unsigned int expect;
|
|
D3D12_BOX box;
|
|
|
|
box.left = rect.left;
|
|
box.right = rect.right;
|
|
box.top = rect.top;
|
|
box.bottom = rect.bottom;
|
|
box.front = 0;
|
|
box.back = 1;
|
|
if (*line != '(')
|
|
fatal_error("Malformed probe arguments '%s'.\n", line);
|
|
++line;
|
|
if (is_signed)
|
|
read_int(&line, (int *)&expect, false);
|
|
else
|
|
read_uint(&line, &expect, false);
|
|
line = close_parentheses(line);
|
|
todo_if(runner->is_todo) check_readback_data_uint(rb, &box, expect, 0);
|
|
}
|
|
else if (match_string(line, "rui64", &line) || (is_signed = match_string(line, "ri64", &line)))
|
|
{
|
|
uint64_t expect;
|
|
D3D12_BOX box;
|
|
|
|
box.left = rect.left;
|
|
box.right = rect.right;
|
|
box.top = rect.top;
|
|
box.bottom = rect.bottom;
|
|
box.front = 0;
|
|
box.back = 1;
|
|
if (*line != '(')
|
|
fatal_error("Malformed probe arguments '%s'.\n", line);
|
|
++line;
|
|
if (is_signed)
|
|
read_int64(&line, (int64_t *)&expect, false);
|
|
else
|
|
read_uint64(&line, &expect, false);
|
|
line = close_parentheses(line);
|
|
todo_if(runner->is_todo) check_readback_data_uint64(rb, &box, expect, 0);
|
|
}
|
|
else if (match_string(line, "rd", &line))
|
|
{
|
|
double expect;
|
|
|
|
ret = sscanf(line, "( %lf ) %u", &expect, &ulps);
|
|
if (ret < 1)
|
|
fatal_error("Malformed probe arguments '%s'.\n", line);
|
|
if (ret < 2)
|
|
ulps = 0;
|
|
todo_if(runner->is_todo) check_readback_data_double(rb, &rect, expect, ulps);
|
|
}
|
|
else if (match_string(line, "r", &line))
|
|
{
|
|
float expect;
|
|
|
|
ret = sscanf(line, "( %f ) %u", &expect, &ulps);
|
|
if (ret < 1)
|
|
fatal_error("Malformed probe arguments '%s'.\n", line);
|
|
if (ret < 2)
|
|
ulps = 0;
|
|
todo_if(runner->is_todo) check_readback_data_float(rb, &rect, expect, ulps);
|
|
}
|
|
else
|
|
{
|
|
fatal_error("Malformed probe arguments '%s'.\n", line);
|
|
}
|
|
|
|
runner->ops->release_readback(runner, rb);
|
|
}
|
|
else if (match_string(line, "uniform", &line))
|
|
{
|
|
unsigned int offset;
|
|
|
|
if (!sscanf(line, "%u", &offset))
|
|
fatal_error("Malformed uniform offset '%s'.\n", line);
|
|
line = strchr(line, ' ') + 1;
|
|
|
|
if (match_string(line, "float4", &line))
|
|
{
|
|
struct vec4 v;
|
|
|
|
if (sscanf(line, "%f %f %f %f", &v.x, &v.y, &v.z, &v.w) < 4)
|
|
fatal_error("Malformed float4 constant '%s'.\n", line);
|
|
set_uniforms(runner, offset, 4, &v);
|
|
}
|
|
else if (match_string(line, "float", &line))
|
|
{
|
|
float f;
|
|
|
|
if (sscanf(line, "%f", &f) < 1)
|
|
fatal_error("Malformed float constant '%s'.\n", line);
|
|
set_uniforms(runner, offset, 1, &f);
|
|
}
|
|
else if (match_string(line, "double2", &line))
|
|
{
|
|
struct dvec2 v;
|
|
|
|
if (sscanf(line, "%lf %lf", &v.x, &v.y) < 2)
|
|
fatal_error("Malformed double2 constant '%s'.\n", line);
|
|
set_uniforms(runner, offset, 4, &v);
|
|
}
|
|
else if (match_string(line, "int4", &line))
|
|
{
|
|
struct ivec4 v;
|
|
|
|
read_int4(&line, &v, true);
|
|
set_uniforms(runner, offset, 4, &v);
|
|
}
|
|
else if (match_string(line, "uint4", &line))
|
|
{
|
|
struct uvec4 v;
|
|
|
|
read_uint4(&line, &v, true);
|
|
set_uniforms(runner, offset, 4, &v);
|
|
}
|
|
else if (match_string(line, "int", &line))
|
|
{
|
|
int i;
|
|
|
|
read_int(&line, &i, true);
|
|
set_uniforms(runner, offset, 1, &i);
|
|
}
|
|
else if (match_string(line, "uint", &line))
|
|
{
|
|
unsigned int u;
|
|
|
|
read_uint(&line, &u, true);
|
|
set_uniforms(runner, offset, 1, &u);
|
|
}
|
|
else if (match_string(line, "int64_t2", &line))
|
|
{
|
|
struct i64vec2 v;
|
|
|
|
read_int64_t2(&line, &v);
|
|
set_uniforms(runner, offset, 4, &v);
|
|
}
|
|
else if (match_string(line, "uint64_t2", &line))
|
|
{
|
|
struct u64vec2 v;
|
|
|
|
read_uint64_t2(&line, &v);
|
|
set_uniforms(runner, offset, 4, &v);
|
|
}
|
|
else
|
|
{
|
|
fatal_error("Unknown uniform type '%s'.\n", line);
|
|
}
|
|
}
|
|
else if (match_string(line, "sample mask", &line))
|
|
{
|
|
unsigned int sample_mask;
|
|
|
|
read_uint(&line, &sample_mask, false);
|
|
runner->sample_mask = sample_mask;
|
|
}
|
|
else if (match_string(line, "alpha test", &line))
|
|
{
|
|
runner->alpha_test_func = (enum vkd3d_shader_comparison_func)parse_comparison_func(line, &line);
|
|
runner->alpha_test_ref = strtof(line, &rest);
|
|
line = rest;
|
|
}
|
|
else if (match_string(line, "shade mode", &line))
|
|
{
|
|
if (match_string(line, "flat", &line))
|
|
runner->flat_shading = true;
|
|
else
|
|
runner->flat_shading = false;
|
|
}
|
|
else if (match_string(line, "clip-plane", &line))
|
|
{
|
|
unsigned int index;
|
|
struct vec4 *v;
|
|
|
|
index = strtoul(line, (char **)&rest, 10);
|
|
if (rest == line || index >= 8)
|
|
fatal_error("Malformed clip plane directive '%s'.\n", line);
|
|
line = rest;
|
|
|
|
v = &runner->clip_planes[index];
|
|
|
|
if (match_string(line, "disable", &line))
|
|
runner->clip_plane_mask &= ~(1u << index);
|
|
else
|
|
{
|
|
if (sscanf(line, "%f %f %f %f", &v->x, &v->y, &v->z, &v->w) < 4)
|
|
fatal_error("Malformed float4 constant '%s'.\n", line);
|
|
runner->clip_plane_mask |= (1u << index);
|
|
}
|
|
}
|
|
else if (match_string(line, "point-size", &line))
|
|
{
|
|
runner->point_size = strtof(line, &rest);
|
|
line = rest;
|
|
runner->point_size_min = strtof(line, &rest);
|
|
line = rest;
|
|
runner->point_size_max = strtof(line, &rest);
|
|
}
|
|
else if (match_string(line, "point-sprite", &line))
|
|
{
|
|
if (match_string(line, "on", &line))
|
|
runner->point_sprite = true;
|
|
else
|
|
runner->point_sprite = false;
|
|
}
|
|
else
|
|
{
|
|
fatal_error("Unknown test directive '%s'.\n", line);
|
|
}
|
|
}
|
|
|
|
struct sampler *shader_runner_get_sampler(struct shader_runner *runner, unsigned int slot)
|
|
{
|
|
struct sampler *sampler;
|
|
size_t i;
|
|
|
|
for (i = 0; i < runner->sampler_count; ++i)
|
|
{
|
|
sampler = &runner->samplers[i];
|
|
|
|
if (sampler->slot == slot)
|
|
return sampler;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
unsigned int get_vb_stride(const struct shader_runner *runner, unsigned int slot)
|
|
{
|
|
unsigned int stride = 0;
|
|
size_t i;
|
|
|
|
/* We currently don't deal with vertex formats less than 32 bits, so don't
|
|
* bother with alignment. */
|
|
for (i = 0; i < runner->input_element_count; ++i)
|
|
{
|
|
const struct input_element *element = &runner->input_elements[i];
|
|
|
|
if (element->slot == slot)
|
|
stride += element->texel_size;
|
|
}
|
|
|
|
return stride;
|
|
}
|
|
|
|
static HRESULT map_special_hrs(HRESULT hr)
|
|
{
|
|
if (hr == 0x88760b59)
|
|
{
|
|
trace("Mapping hr %#x (D3DXERR_INVALIDDATA) as %#x.\n", hr, E_FAIL);
|
|
return E_FAIL;
|
|
}
|
|
if (hr == 0x80010064)
|
|
{
|
|
trace("Mapping unidentified hr %#x as %#x.\n", hr, E_FAIL);
|
|
return E_FAIL;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
const char *shader_type_string(enum shader_type type)
|
|
{
|
|
static const char *const shader_types[] =
|
|
{
|
|
[SHADER_TYPE_CS] = "cs",
|
|
[SHADER_TYPE_PS] = "ps",
|
|
[SHADER_TYPE_VS] = "vs",
|
|
[SHADER_TYPE_HS] = "hs",
|
|
[SHADER_TYPE_DS] = "ds",
|
|
[SHADER_TYPE_GS] = "gs",
|
|
[SHADER_TYPE_FX] = "fx",
|
|
};
|
|
assert(type < ARRAY_SIZE(shader_types));
|
|
return shader_types[type];
|
|
}
|
|
|
|
static HRESULT d3d10_blob_from_vkd3d_shader_code(const struct vkd3d_shader_code *blob, ID3D10Blob **blob_out)
|
|
{
|
|
ID3D10Blob *d3d_blob;
|
|
HRESULT hr;
|
|
|
|
if (FAILED(hr = D3DCreateBlob(blob->size, (ID3DBlob **)&d3d_blob)))
|
|
{
|
|
trace("Failed to create blob, hr %#x.\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
memcpy(ID3D10Blob_GetBufferPointer(d3d_blob), blob->code, blob->size);
|
|
*blob_out = d3d_blob;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT dxc_compiler_compile_shader(void *dxc_compiler, enum shader_type type,
|
|
unsigned int compile_options, const char *hlsl, ID3D10Blob **blob_out)
|
|
{
|
|
struct vkd3d_shader_code blob;
|
|
HRESULT hr;
|
|
|
|
static const WCHAR *const shader_profiles[] =
|
|
{
|
|
[SHADER_TYPE_CS] = L"cs_6_0",
|
|
[SHADER_TYPE_PS] = L"ps_6_0",
|
|
[SHADER_TYPE_VS] = L"vs_6_0",
|
|
[SHADER_TYPE_HS] = L"hs_6_0",
|
|
[SHADER_TYPE_DS] = L"ds_6_0",
|
|
[SHADER_TYPE_GS] = L"gs_6_0",
|
|
};
|
|
|
|
*blob_out = NULL;
|
|
|
|
if (FAILED(hr = dxc_compile(dxc_compiler, shader_profiles[type], compile_options, hlsl, &blob)))
|
|
return hr;
|
|
|
|
hr = d3d10_blob_from_vkd3d_shader_code(&blob, blob_out);
|
|
free((void *)blob.code);
|
|
|
|
return hr;
|
|
}
|
|
|
|
static void compile_shader(struct shader_runner *runner, IDxcCompiler3 *dxc_compiler, const char *source, size_t len,
|
|
enum shader_type type, HRESULT expect)
|
|
{
|
|
bool use_dxcompiler = runner->minimum_shader_model >= SHADER_MODEL_6_0;
|
|
ID3D10Blob *blob = NULL, *errors = NULL;
|
|
char profile[7];
|
|
HRESULT hr;
|
|
|
|
static const char *const shader_models[] =
|
|
{
|
|
[SHADER_MODEL_2_0] = "2_0",
|
|
[SHADER_MODEL_3_0] = "3_0",
|
|
[SHADER_MODEL_4_0] = "4_0",
|
|
[SHADER_MODEL_4_1] = "4_1",
|
|
[SHADER_MODEL_5_0] = "5_0",
|
|
[SHADER_MODEL_5_1] = "5_1",
|
|
[SHADER_MODEL_6_0] = "6_0",
|
|
};
|
|
|
|
static const char *const effect_models[] =
|
|
{
|
|
[SHADER_MODEL_2_0] = "2_0",
|
|
[SHADER_MODEL_4_0] = "4_0",
|
|
[SHADER_MODEL_4_1] = "4_1",
|
|
[SHADER_MODEL_5_0] = "5_0",
|
|
};
|
|
|
|
if (use_dxcompiler)
|
|
{
|
|
assert(dxc_compiler);
|
|
hr = dxc_compiler_compile_shader(dxc_compiler, type, runner->compile_options, source, &blob);
|
|
}
|
|
else
|
|
{
|
|
if (type == SHADER_TYPE_FX)
|
|
sprintf(profile, "%s_%s", shader_type_string(type), effect_models[runner->minimum_shader_model]);
|
|
else
|
|
sprintf(profile, "%s_%s", shader_type_string(type), shader_models[runner->minimum_shader_model]);
|
|
hr = D3DCompile(source, len, NULL, NULL, NULL, "main", profile, runner->compile_options, 0, &blob, &errors);
|
|
}
|
|
hr = map_special_hrs(hr);
|
|
ok(hr == expect, "Got unexpected hr %#x.\n", hr);
|
|
if (hr == S_OK)
|
|
{
|
|
ID3D10Blob_Release(blob);
|
|
}
|
|
else
|
|
{
|
|
assert_that(!blob, "Expected no compiled shader blob.\n");
|
|
if (!use_dxcompiler)
|
|
assert_that(!!errors, "Expected non-NULL error blob.\n");
|
|
}
|
|
if (errors)
|
|
{
|
|
if (vkd3d_test_state.debug_level)
|
|
trace("%s\n", (char *)ID3D10Blob_GetBufferPointer(errors));
|
|
ID3D10Blob_Release(errors);
|
|
}
|
|
}
|
|
|
|
static enum parse_state get_parse_state_todo(enum parse_state state)
|
|
{
|
|
if (state == STATE_SHADER_COMPUTE)
|
|
return STATE_SHADER_COMPUTE_TODO;
|
|
else if (state == STATE_SHADER_PIXEL)
|
|
return STATE_SHADER_PIXEL_TODO;
|
|
else if (state == STATE_SHADER_VERTEX)
|
|
return STATE_SHADER_VERTEX_TODO;
|
|
else if (state == STATE_SHADER_HULL)
|
|
return STATE_SHADER_HULL_TODO;
|
|
else if (state == STATE_SHADER_DOMAIN)
|
|
return STATE_SHADER_DOMAIN_TODO;
|
|
else if (state == STATE_SHADER_GEOMETRY)
|
|
return STATE_SHADER_GEOMETRY_TODO;
|
|
else
|
|
return STATE_SHADER_EFFECT_TODO;
|
|
}
|
|
|
|
static enum parse_state read_shader_directive(struct shader_runner *runner, enum parse_state state, const char *line,
|
|
const char *src, HRESULT *expect_hr)
|
|
{
|
|
while (*src && *src != ']')
|
|
{
|
|
const char *src_start = src;
|
|
|
|
if (match_directive_substring_with_args(runner, src, "todo", &src))
|
|
{
|
|
/* 'todo' is not meaningful when dxcompiler is in use. */
|
|
if (runner->minimum_shader_model >= SHADER_MODEL_6_0)
|
|
continue;
|
|
state = get_parse_state_todo(state);
|
|
}
|
|
else if (match_directive_substring_with_args(runner, src, "fail", &src))
|
|
{
|
|
*expect_hr = E_FAIL;
|
|
}
|
|
else if (match_directive_substring_with_args(runner, src, "notimpl", &src))
|
|
{
|
|
*expect_hr = E_NOTIMPL;
|
|
}
|
|
|
|
if (src == src_start)
|
|
{
|
|
fatal_error("Malformed line '%s'.\n", line);
|
|
}
|
|
}
|
|
|
|
if (strcmp(src, "]\n"))
|
|
fatal_error("Malformed line '%s'.\n", line);
|
|
|
|
return state;
|
|
}
|
|
|
|
static bool check_capabilities(const struct shader_runner *runner, const struct shader_runner_caps *caps)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < SHADER_CAP_COUNT; ++i)
|
|
{
|
|
if (runner->require_shader_caps[i] && !caps->shader_caps[i])
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(runner->require_format_caps); ++i)
|
|
{
|
|
if (runner->require_format_caps[i] & ~caps->format_caps[i])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void trace_tags(const struct shader_runner_caps *caps)
|
|
{
|
|
char tags[80], *p;
|
|
size_t rem;
|
|
int rc;
|
|
|
|
p = tags;
|
|
rem = ARRAY_SIZE(tags);
|
|
rc = snprintf(p, rem, "%8s:", "tags");
|
|
p += rc;
|
|
rem -= rc;
|
|
|
|
for (size_t i = 0; i < caps->tag_count; ++i)
|
|
{
|
|
rc = snprintf(p, rem, " \"%s\"%s", caps->tags[i], i == caps->tag_count - 1 ? "" : ",");
|
|
if (!(rc >= 0 && (size_t)rc < rem))
|
|
{
|
|
*p = 0;
|
|
trace("%s\n", tags);
|
|
|
|
p = tags;
|
|
rem = ARRAY_SIZE(tags);
|
|
rc = snprintf(p, rem, "%8s ", "");
|
|
--i;
|
|
}
|
|
p += rc;
|
|
rem -= rc;
|
|
}
|
|
trace("%s.\n", tags);
|
|
}
|
|
|
|
static void trace_shader_caps(const bool *caps)
|
|
{
|
|
char buffer[80], *p;
|
|
size_t rem;
|
|
int rc;
|
|
|
|
p = buffer;
|
|
rem = ARRAY_SIZE(buffer);
|
|
rc = snprintf(p, rem, "%8s:", "caps");
|
|
p += rc;
|
|
rem -= rc;
|
|
|
|
for (size_t i = 0; i < SHADER_CAP_COUNT; ++i)
|
|
{
|
|
if (!caps[i])
|
|
continue;
|
|
|
|
rc = snprintf(p, rem, " %s", shader_cap_strings[i]);
|
|
if (!(rc >= 0 && (size_t)rc < rem))
|
|
{
|
|
*p = 0;
|
|
trace("%s\n", buffer);
|
|
|
|
p = buffer;
|
|
rem = ARRAY_SIZE(buffer);
|
|
rc = snprintf(p, rem, "%8s ", "");
|
|
--i;
|
|
}
|
|
p += rc;
|
|
rem -= rc;
|
|
}
|
|
trace("%s.\n", buffer);
|
|
}
|
|
|
|
static void trace_format_cap(const struct shader_runner_caps *caps, enum format_cap cap, const char *cap_name)
|
|
{
|
|
bool show_none = true;
|
|
char buffer[80], *p;
|
|
size_t rem;
|
|
int rc;
|
|
|
|
p = buffer;
|
|
rem = ARRAY_SIZE(buffer);
|
|
rc = snprintf(p, rem, "%8s:", cap_name);
|
|
p += rc;
|
|
rem -= rc;
|
|
|
|
for (unsigned int i = 0; i < ARRAY_SIZE(caps->format_caps); ++i)
|
|
{
|
|
if (caps->format_caps[i] & cap)
|
|
{
|
|
rc = snprintf(p, rem, " 0x%x", i);
|
|
if (!(rc >= 0 && (size_t)rc < rem))
|
|
{
|
|
*p = 0;
|
|
trace("%s\n", buffer);
|
|
|
|
p = buffer;
|
|
rem = ARRAY_SIZE(buffer);
|
|
rc = snprintf(p, rem, "%8s ", "");
|
|
--i;
|
|
}
|
|
p += rc;
|
|
rem -= rc;
|
|
show_none = false;
|
|
}
|
|
}
|
|
if (show_none)
|
|
snprintf(p, rem, " (none)");
|
|
trace("%s.\n", buffer);
|
|
}
|
|
|
|
static void update_line_number_context(const char *testname, unsigned int line_number)
|
|
{
|
|
vkd3d_test_pop_context();
|
|
vkd3d_test_push_context("%s:%u", testname, line_number);
|
|
}
|
|
|
|
enum test_action
|
|
{
|
|
TEST_ACTION_RUN,
|
|
TEST_ACTION_SKIP_COMPILATION,
|
|
TEST_ACTION_SKIP_EXECUTION,
|
|
};
|
|
|
|
void run_shader_tests(struct shader_runner *runner, const struct shader_runner_caps *caps,
|
|
const struct shader_runner_ops *ops, void *dxc_compiler)
|
|
{
|
|
size_t shader_source_size = 0, shader_source_len = 0;
|
|
struct resource_params current_resource;
|
|
struct sampler *current_sampler = NULL;
|
|
enum parse_state state = STATE_NONE;
|
|
unsigned int i, line_number = 0, block_start_line_number = 0;
|
|
enum test_action test_action = TEST_ACTION_RUN;
|
|
char *shader_source = NULL;
|
|
HRESULT expect_hr = S_OK;
|
|
char line_buffer[256];
|
|
const char *testname;
|
|
FILE *f;
|
|
|
|
trace("Compiling SM%s-SM%s shaders with %s and executing with %s.\n",
|
|
model_strings[caps->minimum_shader_model], model_strings[caps->maximum_shader_model],
|
|
dxc_compiler ? "dxcompiler" : HLSL_COMPILER, caps->runner);
|
|
if (caps->tag_count)
|
|
trace_tags(caps);
|
|
trace_shader_caps(caps->shader_caps);
|
|
trace_format_cap(caps, FORMAT_CAP_UAV_LOAD, "uav-load");
|
|
|
|
if (!test_options.filename)
|
|
fatal_error("No filename specified.\n");
|
|
|
|
if (!(f = fopen(test_options.filename, "r")))
|
|
fatal_error("Unable to open '%s' for reading: %s\n", test_options.filename, strerror(errno));
|
|
|
|
memset(runner, 0, sizeof(*runner));
|
|
runner->ops = ops;
|
|
runner->caps = caps;
|
|
runner->minimum_shader_model = caps->minimum_shader_model;
|
|
runner->maximum_shader_model = caps->maximum_shader_model;
|
|
runner->alpha_test_func = VKD3D_SHADER_COMPARISON_FUNC_ALWAYS;
|
|
runner->point_size = 1.0f;
|
|
runner->point_size_min = 1.0f;
|
|
runner->point_size_max = FLT_MAX;
|
|
|
|
runner->sample_mask = ~0u;
|
|
runner->depth_bounds = false;
|
|
runner->depth_min = 0.0f;
|
|
runner->depth_max = 1.0f;
|
|
|
|
if ((testname = strrchr(test_options.filename, '/')))
|
|
++testname;
|
|
else
|
|
testname = test_options.filename;
|
|
|
|
vkd3d_test_push_context("%s:%u", testname, line_number);
|
|
|
|
for (;;)
|
|
{
|
|
char *ret = fgets(line_buffer, sizeof(line_buffer), f);
|
|
const char *line = line_buffer;
|
|
|
|
line_number++;
|
|
|
|
if (!ret || line[0] == '[')
|
|
{
|
|
update_line_number_context(testname, block_start_line_number);
|
|
|
|
switch (state)
|
|
{
|
|
case STATE_INPUT_LAYOUT:
|
|
case STATE_NONE:
|
|
case STATE_SAMPLER:
|
|
break;
|
|
|
|
case STATE_TEST:
|
|
if (test_action == TEST_ACTION_SKIP_EXECUTION)
|
|
vkd3d_test_skip(line_number, "Missing capabilities.\n");
|
|
break;
|
|
|
|
case STATE_REQUIRE:
|
|
if (runner->maximum_shader_model < runner->minimum_shader_model)
|
|
test_action = TEST_ACTION_SKIP_COMPILATION;
|
|
else if (!check_capabilities(runner, caps))
|
|
test_action = TEST_ACTION_SKIP_EXECUTION;
|
|
break;
|
|
|
|
case STATE_RESOURCE:
|
|
if (current_resource.desc.type == RESOURCE_TYPE_VERTEX_BUFFER)
|
|
current_resource.desc.width = current_resource.data_size;
|
|
|
|
if (current_resource.desc.type == RESOURCE_TYPE_UAV)
|
|
assert_that(current_resource.explicit_format, "Format must be specified for UAV resources.\n");
|
|
|
|
/* Not every backend supports every resource type
|
|
* (specifically, D3D9 doesn't support UAVs and
|
|
* textures with data type other than float). */
|
|
if (test_action == TEST_ACTION_RUN)
|
|
set_resource(runner, ¤t_resource);
|
|
free(current_resource.data);
|
|
break;
|
|
|
|
case STATE_SHADER_COMPUTE:
|
|
case STATE_SHADER_COMPUTE_TODO:
|
|
if (test_action != TEST_ACTION_SKIP_COMPILATION)
|
|
{
|
|
todo_if (state == STATE_SHADER_COMPUTE_TODO)
|
|
compile_shader(runner, dxc_compiler, shader_source, shader_source_len, SHADER_TYPE_CS,
|
|
expect_hr);
|
|
}
|
|
free(runner->shader_source[SHADER_TYPE_CS]);
|
|
runner->shader_source[SHADER_TYPE_CS] = shader_source;
|
|
shader_source = NULL;
|
|
shader_source_len = 0;
|
|
shader_source_size = 0;
|
|
break;
|
|
|
|
case STATE_SHADER_PIXEL:
|
|
case STATE_SHADER_PIXEL_TODO:
|
|
if (test_action != TEST_ACTION_SKIP_COMPILATION)
|
|
{
|
|
todo_if (state == STATE_SHADER_PIXEL_TODO)
|
|
compile_shader(runner, dxc_compiler, shader_source, shader_source_len, SHADER_TYPE_PS,
|
|
expect_hr);
|
|
}
|
|
free(runner->shader_source[SHADER_TYPE_PS]);
|
|
runner->shader_source[SHADER_TYPE_PS] = shader_source;
|
|
shader_source = NULL;
|
|
shader_source_len = 0;
|
|
shader_source_size = 0;
|
|
break;
|
|
|
|
case STATE_SHADER_VERTEX:
|
|
case STATE_SHADER_VERTEX_TODO:
|
|
if (test_action != TEST_ACTION_SKIP_COMPILATION)
|
|
{
|
|
todo_if (state == STATE_SHADER_VERTEX_TODO)
|
|
compile_shader(runner, dxc_compiler, shader_source, shader_source_len, SHADER_TYPE_VS,
|
|
expect_hr);
|
|
}
|
|
free(runner->shader_source[SHADER_TYPE_VS]);
|
|
runner->shader_source[SHADER_TYPE_VS] = shader_source;
|
|
shader_source = NULL;
|
|
shader_source_len = 0;
|
|
shader_source_size = 0;
|
|
break;
|
|
|
|
case STATE_SHADER_EFFECT:
|
|
case STATE_SHADER_EFFECT_TODO:
|
|
if (test_action != TEST_ACTION_SKIP_COMPILATION)
|
|
{
|
|
todo_if (state == STATE_SHADER_EFFECT_TODO)
|
|
compile_shader(runner, dxc_compiler, shader_source, shader_source_len, SHADER_TYPE_FX,
|
|
expect_hr);
|
|
}
|
|
free(runner->shader_source[SHADER_TYPE_FX]);
|
|
runner->shader_source[SHADER_TYPE_FX] = shader_source;
|
|
shader_source = NULL;
|
|
shader_source_len = 0;
|
|
shader_source_size = 0;
|
|
break;
|
|
|
|
case STATE_SHADER_HULL:
|
|
case STATE_SHADER_HULL_TODO:
|
|
if (test_action != TEST_ACTION_SKIP_COMPILATION)
|
|
{
|
|
todo_if (state == STATE_SHADER_HULL_TODO)
|
|
compile_shader(runner, dxc_compiler, shader_source, shader_source_len, SHADER_TYPE_HS,
|
|
expect_hr);
|
|
}
|
|
free(runner->shader_source[SHADER_TYPE_HS]);
|
|
runner->shader_source[SHADER_TYPE_HS] = shader_source;
|
|
shader_source = NULL;
|
|
shader_source_len = 0;
|
|
shader_source_size = 0;
|
|
break;
|
|
|
|
case STATE_SHADER_DOMAIN:
|
|
case STATE_SHADER_DOMAIN_TODO:
|
|
if (test_action != TEST_ACTION_SKIP_COMPILATION)
|
|
{
|
|
todo_if (state == STATE_SHADER_DOMAIN_TODO)
|
|
compile_shader(runner, dxc_compiler, shader_source, shader_source_len, SHADER_TYPE_DS,
|
|
expect_hr);
|
|
}
|
|
free(runner->shader_source[SHADER_TYPE_DS]);
|
|
runner->shader_source[SHADER_TYPE_DS] = shader_source;
|
|
shader_source = NULL;
|
|
shader_source_len = 0;
|
|
shader_source_size = 0;
|
|
break;
|
|
|
|
case STATE_SHADER_GEOMETRY:
|
|
case STATE_SHADER_GEOMETRY_TODO:
|
|
if (test_action != TEST_ACTION_SKIP_COMPILATION)
|
|
{
|
|
todo_if (state == STATE_SHADER_GEOMETRY_TODO)
|
|
compile_shader(runner, dxc_compiler, shader_source, shader_source_len, SHADER_TYPE_GS,
|
|
expect_hr);
|
|
}
|
|
free(runner->shader_source[SHADER_TYPE_GS]);
|
|
runner->shader_source[SHADER_TYPE_GS] = shader_source;
|
|
shader_source = NULL;
|
|
shader_source_len = 0;
|
|
shader_source_size = 0;
|
|
break;
|
|
|
|
case STATE_PREPROC_INVALID:
|
|
{
|
|
ID3D10Blob *blob = NULL, *errors = NULL;
|
|
HRESULT hr;
|
|
|
|
if (test_action != TEST_ACTION_RUN)
|
|
break;
|
|
|
|
hr = D3DPreprocess(shader_source, strlen(shader_source), NULL, NULL, NULL, &blob, &errors);
|
|
ok(hr == E_FAIL, "Got unexpected hr %#x.\n", hr);
|
|
ok(!blob, "Expected no compiled shader blob.\n");
|
|
ok(!!errors, "Expected non-NULL error blob.\n");
|
|
|
|
if (errors)
|
|
{
|
|
if (vkd3d_test_state.debug_level)
|
|
trace("%s\n", (char *)ID3D10Blob_GetBufferPointer(errors));
|
|
ID3D10Blob_Release(errors);
|
|
}
|
|
|
|
shader_source_len = 0;
|
|
break;
|
|
}
|
|
|
|
case STATE_PREPROC:
|
|
{
|
|
ID3D10Blob *blob = NULL, *errors = NULL;
|
|
SIZE_T size;
|
|
HRESULT hr;
|
|
char *text;
|
|
|
|
if (test_action != TEST_ACTION_RUN)
|
|
break;
|
|
|
|
hr = D3DPreprocess(shader_source, strlen(shader_source), NULL, NULL, NULL, &blob, &errors);
|
|
ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
|
|
if (hr == S_OK)
|
|
{
|
|
if (errors)
|
|
{
|
|
if (vkd3d_test_state.debug_level)
|
|
trace("%s\n", (char *)ID3D10Blob_GetBufferPointer(errors));
|
|
ID3D10Blob_Release(errors);
|
|
}
|
|
|
|
text = ID3D10Blob_GetBufferPointer(blob);
|
|
size = ID3D10Blob_GetBufferSize(blob);
|
|
ok(vkd3d_memmem(text, size, "pass", strlen("pass")),
|
|
"'pass' not found in preprocessed shader.\n");
|
|
ok(!vkd3d_memmem(text, size, "fail", strlen("fail")),
|
|
"'fail' found in preprocessed shader.\n");
|
|
ID3D10Blob_Release(blob);
|
|
}
|
|
|
|
shader_source_len = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ret)
|
|
break;
|
|
}
|
|
|
|
if (line[0] == '[')
|
|
{
|
|
unsigned int index;
|
|
|
|
block_start_line_number = line_number;
|
|
update_line_number_context(testname, line_number);
|
|
|
|
if (match_directive_substring(line, "[compute shader", &line))
|
|
{
|
|
state = STATE_SHADER_COMPUTE;
|
|
expect_hr = S_OK;
|
|
state = read_shader_directive(runner, state, line_buffer, line, &expect_hr);
|
|
}
|
|
else if (!strcmp(line, "[require]\n"))
|
|
{
|
|
state = STATE_REQUIRE;
|
|
runner->minimum_shader_model = caps->minimum_shader_model;
|
|
runner->maximum_shader_model = caps->maximum_shader_model;
|
|
memset(runner->require_shader_caps, 0, sizeof(runner->require_shader_caps));
|
|
memset(runner->require_format_caps, 0, sizeof(runner->require_format_caps));
|
|
runner->compile_options = 0;
|
|
test_action = TEST_ACTION_RUN;
|
|
}
|
|
else if (match_directive_substring(line, "[pixel shader", &line))
|
|
{
|
|
state = STATE_SHADER_PIXEL;
|
|
expect_hr = S_OK;
|
|
state = read_shader_directive(runner, state, line_buffer, line, &expect_hr);
|
|
}
|
|
else if (sscanf(line, "[sampler %u]\n", &index))
|
|
{
|
|
state = STATE_SAMPLER;
|
|
|
|
if (!(current_sampler = shader_runner_get_sampler(runner, index)))
|
|
{
|
|
if (runner->sampler_count == MAX_SAMPLERS)
|
|
fatal_error("Too many samplers declared.\n");
|
|
|
|
current_sampler = &runner->samplers[runner->sampler_count++];
|
|
}
|
|
memset(current_sampler, 0, sizeof(*current_sampler));
|
|
current_sampler->slot = index;
|
|
current_sampler->filter = D3D12_FILTER_MIN_MAG_MIP_POINT;
|
|
current_sampler->u_address = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
|
current_sampler->v_address = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
|
current_sampler->w_address = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
|
}
|
|
else if (sscanf(line, "[rtv %u]\n", &index))
|
|
{
|
|
state = STATE_RESOURCE;
|
|
|
|
memset(¤t_resource, 0, sizeof(current_resource));
|
|
|
|
current_resource.desc.slot = index;
|
|
current_resource.desc.type = RESOURCE_TYPE_RENDER_TARGET;
|
|
current_resource.desc.format = DXGI_FORMAT_R32G32B32A32_FLOAT;
|
|
current_resource.data_type = TEXTURE_DATA_FLOAT;
|
|
current_resource.desc.texel_size = 16;
|
|
current_resource.desc.level_count = 1;
|
|
}
|
|
else if (!strcmp(line, "[dsv]\n"))
|
|
{
|
|
state = STATE_RESOURCE;
|
|
|
|
memset(¤t_resource, 0, sizeof(current_resource));
|
|
|
|
current_resource.desc.slot = 0;
|
|
current_resource.desc.type = RESOURCE_TYPE_DEPTH_STENCIL;
|
|
current_resource.desc.format = DXGI_FORMAT_D32_FLOAT;
|
|
current_resource.is_shadow = true;
|
|
current_resource.data_type = TEXTURE_DATA_FLOAT;
|
|
current_resource.desc.texel_size = 4;
|
|
current_resource.desc.level_count = 1;
|
|
}
|
|
else if (sscanf(line, "[srv %u]\n", &index))
|
|
{
|
|
state = STATE_RESOURCE;
|
|
|
|
memset(¤t_resource, 0, sizeof(current_resource));
|
|
|
|
current_resource.desc.slot = index;
|
|
current_resource.desc.type = RESOURCE_TYPE_TEXTURE;
|
|
current_resource.desc.format = DXGI_FORMAT_R32G32B32A32_FLOAT;
|
|
current_resource.data_type = TEXTURE_DATA_FLOAT;
|
|
current_resource.desc.texel_size = 16;
|
|
current_resource.desc.level_count = 1;
|
|
}
|
|
else if (sscanf(line, "[uav %u]\n", &index))
|
|
{
|
|
state = STATE_RESOURCE;
|
|
|
|
memset(¤t_resource, 0, sizeof(current_resource));
|
|
|
|
current_resource.desc.slot = index;
|
|
current_resource.desc.type = RESOURCE_TYPE_UAV;
|
|
current_resource.desc.format = DXGI_FORMAT_R32G32B32A32_FLOAT;
|
|
current_resource.data_type = TEXTURE_DATA_FLOAT;
|
|
current_resource.desc.texel_size = 16;
|
|
current_resource.desc.level_count = 1;
|
|
}
|
|
else if (sscanf(line, "[vb %u]\n", &index))
|
|
{
|
|
state = STATE_RESOURCE;
|
|
|
|
memset(¤t_resource, 0, sizeof(current_resource));
|
|
|
|
current_resource.desc.slot = index;
|
|
current_resource.desc.type = RESOURCE_TYPE_VERTEX_BUFFER;
|
|
current_resource.desc.dimension = RESOURCE_DIMENSION_BUFFER;
|
|
current_resource.data_type = TEXTURE_DATA_FLOAT;
|
|
}
|
|
else if (!strcmp(line, "[test]\n"))
|
|
{
|
|
state = STATE_TEST;
|
|
}
|
|
else if (!strcmp(line, "[preproc]\n"))
|
|
{
|
|
state = STATE_PREPROC;
|
|
}
|
|
else if (!strcmp(line, "[preproc fail]\n"))
|
|
{
|
|
state = STATE_PREPROC_INVALID;
|
|
}
|
|
else if (match_directive_substring(line, "[vertex shader", &line))
|
|
{
|
|
state = STATE_SHADER_VERTEX;
|
|
expect_hr = S_OK;
|
|
state = read_shader_directive(runner, state, line_buffer, line, &expect_hr);
|
|
}
|
|
else if (match_directive_substring(line, "[effect", &line))
|
|
{
|
|
state = STATE_SHADER_EFFECT;
|
|
expect_hr = S_OK;
|
|
state = read_shader_directive(runner, state, line_buffer, line, &expect_hr);
|
|
}
|
|
else if (match_directive_substring(line, "[hull shader", &line))
|
|
{
|
|
state = STATE_SHADER_HULL;
|
|
expect_hr = S_OK;
|
|
state = read_shader_directive(runner, state, line_buffer, line, &expect_hr);
|
|
}
|
|
else if (match_directive_substring(line, "[domain shader", &line))
|
|
{
|
|
state = STATE_SHADER_DOMAIN;
|
|
expect_hr = S_OK;
|
|
state = read_shader_directive(runner, state, line_buffer, line, &expect_hr);
|
|
}
|
|
else if (match_directive_substring(line, "[geometry shader", &line))
|
|
{
|
|
state = STATE_SHADER_GEOMETRY;
|
|
expect_hr = S_OK;
|
|
state = read_shader_directive(runner, state, line_buffer, line, &expect_hr);
|
|
}
|
|
else if (!strcmp(line, "[input layout]\n"))
|
|
{
|
|
state = STATE_INPUT_LAYOUT;
|
|
|
|
for (i = 0; i < runner->input_element_count; ++i)
|
|
free(runner->input_elements[i].name);
|
|
runner->input_element_count = 0;
|
|
}
|
|
}
|
|
else if (line[0] != '%' && line[0] != '\n')
|
|
{
|
|
update_line_number_context(testname, line_number);
|
|
|
|
switch (state)
|
|
{
|
|
case STATE_NONE:
|
|
fatal_error("Malformed line '%s'.\n", line);
|
|
break;
|
|
|
|
case STATE_INPUT_LAYOUT:
|
|
parse_input_layout_directive(runner, line);
|
|
break;
|
|
|
|
case STATE_PREPROC:
|
|
case STATE_PREPROC_INVALID:
|
|
case STATE_SHADER_COMPUTE:
|
|
case STATE_SHADER_COMPUTE_TODO:
|
|
case STATE_SHADER_PIXEL:
|
|
case STATE_SHADER_PIXEL_TODO:
|
|
case STATE_SHADER_VERTEX:
|
|
case STATE_SHADER_VERTEX_TODO:
|
|
case STATE_SHADER_EFFECT:
|
|
case STATE_SHADER_EFFECT_TODO:
|
|
case STATE_SHADER_HULL:
|
|
case STATE_SHADER_HULL_TODO:
|
|
case STATE_SHADER_DOMAIN:
|
|
case STATE_SHADER_DOMAIN_TODO:
|
|
case STATE_SHADER_GEOMETRY:
|
|
case STATE_SHADER_GEOMETRY_TODO:
|
|
{
|
|
size_t len = strlen(line);
|
|
|
|
vkd3d_array_reserve((void **)&shader_source, &shader_source_size, shader_source_len + len + 1, 1);
|
|
memcpy(shader_source + shader_source_len, line, len + 1);
|
|
shader_source_len += len;
|
|
break;
|
|
}
|
|
|
|
case STATE_REQUIRE:
|
|
parse_require_directive(runner, line);
|
|
break;
|
|
|
|
case STATE_RESOURCE:
|
|
parse_resource_directive(¤t_resource, line);
|
|
break;
|
|
|
|
case STATE_SAMPLER:
|
|
parse_sampler_directive(current_sampler, line);
|
|
break;
|
|
|
|
case STATE_TEST:
|
|
/* Compilation which fails with dxcompiler is not 'todo', therefore the tests are
|
|
* not 'todo' either. They cannot run, so skip them entirely. */
|
|
if (!runner->failed_resource_count && test_action == TEST_ACTION_RUN && SUCCEEDED(expect_hr))
|
|
parse_test_directive(runner, line);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Pop line_number context. */
|
|
vkd3d_test_pop_context();
|
|
|
|
for (i = 0; i < runner->input_element_count; ++i)
|
|
free(runner->input_elements[i].name);
|
|
free(runner->input_elements);
|
|
for (i = 0; i < SHADER_TYPE_COUNT; ++i)
|
|
free(runner->shader_source[i]);
|
|
free(runner->uniforms);
|
|
for (i = 0; i < runner->resource_count; ++i)
|
|
{
|
|
if (runner->resources[i])
|
|
runner->ops->destroy_resource(runner, runner->resources[i]);
|
|
}
|
|
|
|
fclose(f);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
static void print_dll_version(const char *file_name)
|
|
{
|
|
BOOL (WINAPI *GetFileVersionInfoA)(const char *, DWORD, DWORD, void *);
|
|
BOOL (WINAPI *VerQueryValueA)(void *, char *, void **, UINT*);
|
|
DWORD (WINAPI *GetFileVersionInfoSizeA)(const char *, DWORD *);
|
|
HMODULE version_module;
|
|
DWORD size, handle;
|
|
bool done = false;
|
|
|
|
version_module = LoadLibraryA("version.dll");
|
|
if (!version_module)
|
|
goto out;
|
|
|
|
#define X(name) name = (void *)GetProcAddress(version_module, #name);
|
|
X(GetFileVersionInfoSizeA);
|
|
X(GetFileVersionInfoA);
|
|
X(VerQueryValueA);
|
|
#undef X
|
|
|
|
if (!GetFileVersionInfoSizeA || !GetFileVersionInfoA || !VerQueryValueA)
|
|
{
|
|
FreeLibrary(version_module);
|
|
goto out;
|
|
}
|
|
|
|
size = GetFileVersionInfoSizeA(file_name, &handle);
|
|
if (size)
|
|
{
|
|
char *data = malloc(size);
|
|
|
|
if (GetFileVersionInfoA(file_name, handle, size, data))
|
|
{
|
|
VS_FIXEDFILEINFO *info;
|
|
UINT len;
|
|
|
|
if (VerQueryValueA(data, "\\", (void **)&info, &len))
|
|
{
|
|
trace("%s version: %lu.%lu.%lu.%lu\n", file_name,
|
|
info->dwFileVersionMS >> 16, info->dwFileVersionMS & 0xffff,
|
|
info->dwFileVersionLS >> 16, info->dwFileVersionLS & 0xffff);
|
|
done = true;
|
|
}
|
|
}
|
|
free(data);
|
|
}
|
|
|
|
FreeLibrary(version_module);
|
|
|
|
out:
|
|
if (!done)
|
|
trace("%s version: unknown\n", file_name);
|
|
}
|
|
#endif
|
|
|
|
START_TEST(shader_runner)
|
|
{
|
|
IDxcCompiler3 *dxc_compiler;
|
|
|
|
parse_args(argc, argv);
|
|
|
|
#if defined(VKD3D_CROSSTEST)
|
|
trace("Running tests from a Windows cross build\n");
|
|
|
|
run_shader_tests_d3d9();
|
|
|
|
run_shader_tests_d3d11();
|
|
|
|
dxc_compiler = dxcompiler_create();
|
|
run_shader_tests_d3d12(dxc_compiler);
|
|
|
|
if (dxc_compiler)
|
|
{
|
|
IDxcCompiler3_Release(dxc_compiler);
|
|
print_dll_version("dxcompiler.dll");
|
|
}
|
|
|
|
print_dll_version("d3dcompiler_47.dll");
|
|
print_dll_version("dxgi.dll");
|
|
print_dll_version("d3d9.dll");
|
|
print_dll_version("d3d11.dll");
|
|
print_dll_version("d3d12.dll");
|
|
print_dll_version("d3d12core.dll");
|
|
print_dll_version("d3d10warp.dll");
|
|
if (test_options.enable_debug_layer)
|
|
print_dll_version("d3d12sdklayers.dll");
|
|
|
|
#elif defined(_WIN32)
|
|
trace("Running tests from a Windows non-cross build\n");
|
|
|
|
run_shader_tests_d3d9();
|
|
|
|
run_shader_tests_d3d11();
|
|
|
|
dxc_compiler = dxcompiler_create();
|
|
run_shader_tests_d3d12(dxc_compiler);
|
|
|
|
if (dxc_compiler)
|
|
{
|
|
IDxcCompiler3_Release(dxc_compiler);
|
|
print_dll_version(SONAME_LIBDXCOMPILER);
|
|
}
|
|
print_dll_version("d3d9.dll");
|
|
print_dll_version("d3d11.dll");
|
|
|
|
#else
|
|
trace("Running tests from a Unix build\n");
|
|
|
|
# ifdef HAVE_OPENGL
|
|
run_shader_tests_gl();
|
|
# endif
|
|
|
|
run_shader_tests_vulkan();
|
|
|
|
dxc_compiler = dxcompiler_create();
|
|
run_shader_tests_d3d12(dxc_compiler);
|
|
|
|
if (dxc_compiler)
|
|
IDxcCompiler3_Release(dxc_compiler);
|
|
|
|
#endif
|
|
}
|