401 lines
14 KiB
C
401 lines
14 KiB
C
/*
|
|
* Direct3D asm shader parser
|
|
*
|
|
* Copyright 2008 Stefan Dösinger
|
|
* Copyright 2009 Matteo Bruni
|
|
*
|
|
* 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
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "wine/port.h"
|
|
#include "wine/debug.h"
|
|
|
|
#include "d3dx9_36_private.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(asmshader);
|
|
WINE_DECLARE_DEBUG_CHANNEL(parsed_shader);
|
|
|
|
|
|
/****************************************************************
|
|
* Common(non-version specific) shader parser control code *
|
|
****************************************************************/
|
|
|
|
static void asmparser_end(struct asm_parser *This) {
|
|
TRACE("Finalizing shader\n");
|
|
}
|
|
|
|
static void asmparser_constF(struct asm_parser *This, DWORD reg, float x, float y, float z, float w) {
|
|
if(!This->shader) return;
|
|
TRACE("Adding float constant %u at pos %u\n", reg, This->shader->num_cf);
|
|
TRACE_(parsed_shader)("def c%u, %f, %f, %f, %f\n", reg, x, y, z, w);
|
|
if(!add_constF(This->shader, reg, x, y, z, w)) {
|
|
ERR("Out of memory\n");
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
}
|
|
|
|
static void asmparser_constB(struct asm_parser *This, DWORD reg, BOOL x) {
|
|
if(!This->shader) return;
|
|
TRACE("Adding boolean constant %u at pos %u\n", reg, This->shader->num_cb);
|
|
TRACE_(parsed_shader)("def b%u, %s\n", reg, x ? "true" : "false");
|
|
if(!add_constB(This->shader, reg, x)) {
|
|
ERR("Out of memory\n");
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
}
|
|
|
|
static void asmparser_constI(struct asm_parser *This, DWORD reg, INT x, INT y, INT z, INT w) {
|
|
if(!This->shader) return;
|
|
TRACE("Adding integer constant %u at pos %u\n", reg, This->shader->num_ci);
|
|
TRACE_(parsed_shader)("def i%u, %d, %d, %d, %d\n", reg, x, y, z, w);
|
|
if(!add_constI(This->shader, reg, x, y, z, w)) {
|
|
ERR("Out of memory\n");
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
}
|
|
|
|
static void asmparser_dcl_output(struct asm_parser *This, DWORD usage, DWORD num,
|
|
const struct shader_reg *reg) {
|
|
if(!This->shader) return;
|
|
if(This->shader->type == ST_PIXEL) {
|
|
asmparser_message(This, "Line %u: Output register declared in a pixel shader\n", This->line_no);
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
if(!record_declaration(This->shader, usage, num, TRUE, reg->regnum, reg->writemask)) {
|
|
ERR("Out of memory\n");
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
}
|
|
|
|
static void asmparser_dcl_input(struct asm_parser *This, DWORD usage, DWORD num,
|
|
const struct shader_reg *reg) {
|
|
if(!This->shader) return;
|
|
if(!record_declaration(This->shader, usage, num, FALSE, reg->regnum, reg->writemask)) {
|
|
ERR("Out of memory\n");
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
}
|
|
|
|
static void asmparser_dcl_sampler(struct asm_parser *This, DWORD samptype, DWORD regnum, unsigned int line_no) {
|
|
if(!This->shader) return;
|
|
if(!record_sampler(This->shader, samptype, regnum)) {
|
|
ERR("Out of memory\n");
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
}
|
|
|
|
static void asmparser_instr(struct asm_parser *This, DWORD opcode,
|
|
DWORD mod, DWORD shift,
|
|
BWRITER_COMPARISON_TYPE comp,
|
|
const struct shader_reg *dst,
|
|
const struct src_regs *srcs, int expectednsrcs) {
|
|
struct instruction *instr;
|
|
unsigned int i;
|
|
BOOL firstreg = TRUE;
|
|
unsigned int src_count = srcs ? srcs->count : 0;
|
|
|
|
if(!This->shader) return;
|
|
|
|
TRACE_(parsed_shader)("%s%s%s ", debug_print_opcode(opcode),
|
|
debug_print_dstmod(mod),
|
|
debug_print_comp(comp));
|
|
if(dst) {
|
|
TRACE_(parsed_shader)("%s", debug_print_dstreg(dst, This->shader->type));
|
|
firstreg = FALSE;
|
|
}
|
|
for(i = 0; i < src_count; i++) {
|
|
if(!firstreg) TRACE_(parsed_shader)(", ");
|
|
else firstreg = FALSE;
|
|
TRACE_(parsed_shader)("%s", debug_print_srcreg(&srcs->reg[i],
|
|
This->shader->type));
|
|
}
|
|
TRACE_(parsed_shader)("\n");
|
|
|
|
if(src_count != expectednsrcs) {
|
|
asmparser_message(This, "Line %u: Wrong number of source registers\n", This->line_no);
|
|
set_parse_status(This, PARSE_ERR);
|
|
return;
|
|
}
|
|
|
|
instr = alloc_instr(src_count);
|
|
if(!instr) {
|
|
ERR("Error allocating memory for the instruction\n");
|
|
set_parse_status(This, PARSE_ERR);
|
|
return;
|
|
}
|
|
|
|
instr->opcode = opcode;
|
|
instr->dstmod = mod;
|
|
instr->shift = shift;
|
|
instr->comptype = comp;
|
|
if(dst) This->funcs->dstreg(This, instr, dst);
|
|
for(i = 0; i < src_count; i++) {
|
|
This->funcs->srcreg(This, instr, i, &srcs->reg[i]);
|
|
}
|
|
|
|
if(!add_instruction(This->shader, instr)) {
|
|
ERR("Out of memory\n");
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
}
|
|
|
|
/* Checks for unsupported source modifiers in VS (all versions) or
|
|
PS 2.0 and newer */
|
|
static void check_legacy_srcmod(struct asm_parser *This, DWORD srcmod) {
|
|
if(srcmod == BWRITERSPSM_BIAS || srcmod == BWRITERSPSM_BIASNEG ||
|
|
srcmod == BWRITERSPSM_SIGN || srcmod == BWRITERSPSM_SIGNNEG ||
|
|
srcmod == BWRITERSPSM_COMP || srcmod == BWRITERSPSM_X2 ||
|
|
srcmod == BWRITERSPSM_X2NEG || srcmod == BWRITERSPSM_DZ ||
|
|
srcmod == BWRITERSPSM_DW) {
|
|
asmparser_message(This, "Line %u: Source modifier %s not supported in this shader version\n",
|
|
This->line_no,
|
|
debug_print_srcmod(srcmod));
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
}
|
|
|
|
static void check_loop_swizzle(struct asm_parser *This,
|
|
const struct shader_reg *src) {
|
|
if((src->type == BWRITERSPR_LOOP && src->swizzle != BWRITERVS_NOSWIZZLE) ||
|
|
(src->rel_reg && src->rel_reg->type == BWRITERSPR_LOOP &&
|
|
src->rel_reg->swizzle != BWRITERVS_NOSWIZZLE)) {
|
|
asmparser_message(This, "Line %u: Swizzle not allowed on aL register\n", This->line_no);
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
}
|
|
|
|
static void check_shift_dstmod(struct asm_parser *This, DWORD shift) {
|
|
if(shift != 0) {
|
|
asmparser_message(This, "Line %u: Shift modifiers not supported in this shader version\n",
|
|
This->line_no);
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
}
|
|
|
|
static void check_ps_dstmod(struct asm_parser *This, DWORD dstmod) {
|
|
if(dstmod == BWRITERSPDM_PARTIALPRECISION ||
|
|
dstmod == BWRITERSPDM_MSAMPCENTROID) {
|
|
asmparser_message(This, "Line %u: Instruction modifier %s not supported in this shader version\n",
|
|
This->line_no,
|
|
debug_print_dstmod(dstmod));
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
}
|
|
|
|
struct allowed_reg_type {
|
|
DWORD type;
|
|
DWORD count;
|
|
};
|
|
|
|
static BOOL check_reg_type(const struct shader_reg *reg,
|
|
const struct allowed_reg_type *allowed) {
|
|
unsigned int i = 0;
|
|
|
|
while(allowed[i].type != ~0U) {
|
|
if(reg->type == allowed[i].type) {
|
|
if(reg->rel_reg) return TRUE; /* The relative addressing register
|
|
can have a negative value, we
|
|
can't check the register index */
|
|
if(reg->regnum < allowed[i].count) return TRUE;
|
|
return FALSE;
|
|
}
|
|
i++;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Native assembler doesn't do separate checks for src and dst registers */
|
|
static const struct allowed_reg_type vs_3_reg_allowed[] = {
|
|
{ BWRITERSPR_TEMP, 32 },
|
|
{ BWRITERSPR_INPUT, 16 },
|
|
{ BWRITERSPR_CONST, ~0U },
|
|
{ BWRITERSPR_ADDR, 1 },
|
|
{ BWRITERSPR_CONSTBOOL, 16 },
|
|
{ BWRITERSPR_CONSTINT, 16 },
|
|
{ BWRITERSPR_LOOP, 1 },
|
|
{ BWRITERSPR_LABEL, 2048 },
|
|
{ BWRITERSPR_PREDICATE, 1 },
|
|
{ BWRITERSPR_SAMPLER, 4 },
|
|
{ BWRITERSPR_OUTPUT, 12 },
|
|
{ ~0U, 0 } /* End tag */
|
|
};
|
|
|
|
static void asmparser_srcreg_vs_3(struct asm_parser *This,
|
|
struct instruction *instr, int num,
|
|
const struct shader_reg *src) {
|
|
if(!check_reg_type(src, vs_3_reg_allowed)) {
|
|
asmparser_message(This, "Line %u: Source register %s not supported in VS 3.0\n",
|
|
This->line_no,
|
|
debug_print_srcreg(src, ST_VERTEX));
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
check_loop_swizzle(This, src);
|
|
check_legacy_srcmod(This, src->srcmod);
|
|
memcpy(&instr->src[num], src, sizeof(*src));
|
|
}
|
|
|
|
static const struct allowed_reg_type ps_3_reg_allowed[] = {
|
|
{ BWRITERSPR_INPUT, 10 },
|
|
{ BWRITERSPR_TEMP, 32 },
|
|
{ BWRITERSPR_CONST, 224 },
|
|
{ BWRITERSPR_CONSTINT, 16 },
|
|
{ BWRITERSPR_CONSTBOOL, 16 },
|
|
{ BWRITERSPR_PREDICATE, 1 },
|
|
{ BWRITERSPR_SAMPLER, 16 },
|
|
{ BWRITERSPR_MISCTYPE, 2 }, /* vPos and vFace */
|
|
{ BWRITERSPR_LOOP, 1 },
|
|
{ BWRITERSPR_LABEL, 2048 },
|
|
{ BWRITERSPR_COLOROUT, ~0U },
|
|
{ BWRITERSPR_DEPTHOUT, 1 },
|
|
{ ~0U, 0 } /* End tag */
|
|
};
|
|
|
|
static void asmparser_srcreg_ps_3(struct asm_parser *This,
|
|
struct instruction *instr, int num,
|
|
const struct shader_reg *src) {
|
|
if(!check_reg_type(src, ps_3_reg_allowed)) {
|
|
asmparser_message(This, "Line %u: Source register %s not supported in PS 3.0\n",
|
|
This->line_no,
|
|
debug_print_srcreg(src, ST_PIXEL));
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
check_loop_swizzle(This, src);
|
|
check_legacy_srcmod(This, src->srcmod);
|
|
memcpy(&instr->src[num], src, sizeof(*src));
|
|
}
|
|
|
|
static void asmparser_dstreg_vs_3(struct asm_parser *This,
|
|
struct instruction *instr,
|
|
const struct shader_reg *dst) {
|
|
if(!check_reg_type(dst, vs_3_reg_allowed)) {
|
|
asmparser_message(This, "Line %u: Destination register %s not supported in VS 3.0\n",
|
|
This->line_no,
|
|
debug_print_dstreg(dst, ST_VERTEX));
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
check_ps_dstmod(This, instr->dstmod);
|
|
check_shift_dstmod(This, instr->shift);
|
|
memcpy(&instr->dst, dst, sizeof(*dst));
|
|
instr->has_dst = TRUE;
|
|
}
|
|
|
|
static void asmparser_dstreg_ps_3(struct asm_parser *This,
|
|
struct instruction *instr,
|
|
const struct shader_reg *dst) {
|
|
if(!check_reg_type(dst, ps_3_reg_allowed)) {
|
|
asmparser_message(This, "Line %u: Destination register %s not supported in PS 3.0\n",
|
|
This->line_no,
|
|
debug_print_dstreg(dst, ST_PIXEL));
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
check_shift_dstmod(This, instr->shift);
|
|
memcpy(&instr->dst, dst, sizeof(*dst));
|
|
instr->has_dst = TRUE;
|
|
}
|
|
|
|
static void asmparser_predicate_supported(struct asm_parser *This,
|
|
const struct shader_reg *predicate) {
|
|
/* this sets the predicate of the last instruction added to the shader */
|
|
if(!This->shader) return;
|
|
if(This->shader->num_instrs == 0) ERR("Predicate without an instruction\n");
|
|
This->shader->instr[This->shader->num_instrs - 1]->has_predicate = TRUE;
|
|
memcpy(&This->shader->instr[This->shader->num_instrs - 1]->predicate, predicate, sizeof(*predicate));
|
|
}
|
|
|
|
#if 0
|
|
static void asmparser_predicate_unsupported(struct asm_parser *This,
|
|
const struct shader_reg *predicate) {
|
|
asmparser_message(This, "Line %u: Predicate not supported in < VS 2.0 or PS 2.x\n", This->line_no);
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
#endif
|
|
|
|
static void asmparser_coissue_unsupported(struct asm_parser *This) {
|
|
asmparser_message(This, "Line %u: Coissue is only supported in pixel shaders versions <= 1.4\n", This->line_no);
|
|
set_parse_status(This, PARSE_ERR);
|
|
}
|
|
|
|
static const struct asmparser_backend parser_vs_3 = {
|
|
asmparser_constF,
|
|
asmparser_constI,
|
|
asmparser_constB,
|
|
|
|
asmparser_dstreg_vs_3,
|
|
asmparser_srcreg_vs_3,
|
|
|
|
asmparser_predicate_supported,
|
|
asmparser_coissue_unsupported,
|
|
|
|
asmparser_dcl_output,
|
|
asmparser_dcl_input,
|
|
asmparser_dcl_sampler,
|
|
|
|
asmparser_end,
|
|
|
|
asmparser_instr,
|
|
};
|
|
|
|
static const struct asmparser_backend parser_ps_3 = {
|
|
asmparser_constF,
|
|
asmparser_constI,
|
|
asmparser_constB,
|
|
|
|
asmparser_dstreg_ps_3,
|
|
asmparser_srcreg_ps_3,
|
|
|
|
asmparser_predicate_supported,
|
|
asmparser_coissue_unsupported,
|
|
|
|
asmparser_dcl_output,
|
|
asmparser_dcl_input,
|
|
asmparser_dcl_sampler,
|
|
|
|
asmparser_end,
|
|
|
|
asmparser_instr,
|
|
};
|
|
|
|
void create_vs30_parser(struct asm_parser *ret) {
|
|
TRACE_(parsed_shader)("vs_3_0\n");
|
|
|
|
ret->shader = asm_alloc(sizeof(*ret->shader));
|
|
if(!ret->shader) {
|
|
ERR("Failed to allocate memory for the shader\n");
|
|
set_parse_status(ret, PARSE_ERR);
|
|
return;
|
|
}
|
|
|
|
ret->shader->type = ST_VERTEX;
|
|
ret->shader->version = BWRITERVS_VERSION(3, 0);
|
|
ret->funcs = &parser_vs_3;
|
|
}
|
|
|
|
void create_ps30_parser(struct asm_parser *ret) {
|
|
TRACE_(parsed_shader)("ps_3_0\n");
|
|
|
|
ret->shader = asm_alloc(sizeof(*ret->shader));
|
|
if(!ret->shader) {
|
|
ERR("Failed to allocate memory for the shader\n");
|
|
set_parse_status(ret, PARSE_ERR);
|
|
return;
|
|
}
|
|
|
|
ret->shader->type = ST_PIXEL;
|
|
ret->shader->version = BWRITERPS_VERSION(3, 0);
|
|
ret->funcs = &parser_ps_3;
|
|
}
|