/* * shaders implementation * * Copyright 2002-2003 Jason Edmeades * Copyright 2002-2003 Raphael Junqueira * Copyright 2004 Christian Costa * Copyright 2005 Oliver Stieber * Copyright 2006 Ivan Gyurdiev * Copyright 2007-2008 Stefan Dösinger for CodeWeavers * Copyright 2009 Henri Verbeet 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 */ #include "config.h" #include #include #include "wined3d_private.h" WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader); WINE_DECLARE_DEBUG_CHANNEL(d3d); static const char *shader_opcode_names[] = { /* WINED3DSIH_ABS */ "abs", /* WINED3DSIH_ADD */ "add", /* WINED3DSIH_BEM */ "bem", /* WINED3DSIH_BREAK */ "break", /* WINED3DSIH_BREAKC */ "breakc", /* WINED3DSIH_BREAKP */ "breakp", /* WINED3DSIH_CALL */ "call", /* WINED3DSIH_CALLNZ */ "callnz", /* WINED3DSIH_CMP */ "cmp", /* WINED3DSIH_CND */ "cnd", /* WINED3DSIH_CRS */ "crs", /* WINED3DSIH_DCL */ "dcl", /* WINED3DSIH_DEF */ "def", /* WINED3DSIH_DEFB */ "defb", /* WINED3DSIH_DEFI */ "defi", /* WINED3DSIH_DP2ADD */ "dp2add", /* WINED3DSIH_DP3 */ "dp3", /* WINED3DSIH_DP4 */ "dp4", /* WINED3DSIH_DST */ "dst", /* WINED3DSIH_DSX */ "dsx", /* WINED3DSIH_DSY */ "dsy", /* WINED3DSIH_ELSE */ "else", /* WINED3DSIH_ENDIF */ "endif", /* WINED3DSIH_ENDLOOP */ "endloop", /* WINED3DSIH_ENDREP */ "endrep", /* WINED3DSIH_EXP */ "exp", /* WINED3DSIH_EXPP */ "expp", /* WINED3DSIH_FRC */ "frc", /* WINED3DSIH_IF */ "if", /* WINED3DSIH_IFC */ "ifc", /* WINED3DSIH_LABEL */ "label", /* WINED3DSIH_LIT */ "lit", /* WINED3DSIH_LOG */ "log", /* WINED3DSIH_LOGP */ "logp", /* WINED3DSIH_LOOP */ "loop", /* WINED3DSIH_LRP */ "lrp", /* WINED3DSIH_M3x2 */ "m3x2", /* WINED3DSIH_M3x3 */ "m3x3", /* WINED3DSIH_M3x4 */ "m3x4", /* WINED3DSIH_M4x3 */ "m4x3", /* WINED3DSIH_M4x4 */ "m4x4", /* WINED3DSIH_MAD */ "mad", /* WINED3DSIH_MAX */ "max", /* WINED3DSIH_MIN */ "min", /* WINED3DSIH_MOV */ "mov", /* WINED3DSIH_MOVA */ "mova", /* WINED3DSIH_MUL */ "mul", /* WINED3DSIH_NOP */ "nop", /* WINED3DSIH_NRM */ "nrm", /* WINED3DSIH_PHASE */ "phase", /* WINED3DSIH_POW */ "pow", /* WINED3DSIH_RCP */ "rcp", /* WINED3DSIH_REP */ "rep", /* WINED3DSIH_RET */ "ret", /* WINED3DSIH_RSQ */ "rsq", /* WINED3DSIH_SETP */ "setp", /* WINED3DSIH_SGE */ "sge", /* WINED3DSIH_SGN */ "sgn", /* WINED3DSIH_SINCOS */ "sincos", /* WINED3DSIH_SLT */ "slt", /* WINED3DSIH_SUB */ "sub", /* WINED3DSIH_TEX */ "texld", /* WINED3DSIH_TEXBEM */ "texbem", /* WINED3DSIH_TEXBEML */ "texbeml", /* WINED3DSIH_TEXCOORD */ "texcrd", /* WINED3DSIH_TEXDEPTH */ "texdepth", /* WINED3DSIH_TEXDP3 */ "texdp3", /* WINED3DSIH_TEXDP3TEX */ "texdp3tex", /* WINED3DSIH_TEXKILL */ "texkill", /* WINED3DSIH_TEXLDD */ "texldd", /* WINED3DSIH_TEXLDL */ "texldl", /* WINED3DSIH_TEXM3x2DEPTH */ "texm3x2depth", /* WINED3DSIH_TEXM3x2PAD */ "texm3x2pad", /* WINED3DSIH_TEXM3x2TEX */ "texm3x2tex", /* WINED3DSIH_TEXM3x3 */ "texm3x3", /* WINED3DSIH_TEXM3x3DIFF */ "texm3x3diff", /* WINED3DSIH_TEXM3x3PAD */ "texm3x3pad", /* WINED3DSIH_TEXM3x3SPEC */ "texm3x3spec", /* WINED3DSIH_TEXM3x3TEX */ "texm3x3tex", /* WINED3DSIH_TEXM3x3VSPEC */ "texm3x3vspec", /* WINED3DSIH_TEXREG2AR */ "texreg2ar", /* WINED3DSIH_TEXREG2GB */ "texreg2gb", /* WINED3DSIH_TEXREG2RGB */ "texreg2rgb", }; const struct wined3d_shader_frontend *shader_select_frontend(DWORD version_token) { switch (version_token >> 16) { case WINED3D_SM1_VS: case WINED3D_SM1_PS: return &sm1_shader_frontend; case WINED3D_SM4_PS: case WINED3D_SM4_VS: case WINED3D_SM4_GS: return &sm4_shader_frontend; default: FIXME("Unrecognised version token %#x\n", version_token); return NULL; } } static inline BOOL shader_is_version_token(DWORD token) { return shader_is_pshader_version(token) || shader_is_vshader_version(token); } void shader_buffer_init(struct SHADER_BUFFER *buffer) { buffer->buffer = HeapAlloc(GetProcessHeap(), 0, SHADER_PGMSIZE); buffer->buffer[0] = '\0'; buffer->bsize = 0; buffer->lineNo = 0; buffer->newline = TRUE; } void shader_buffer_free(struct SHADER_BUFFER *buffer) { HeapFree(GetProcessHeap(), 0, buffer->buffer); } int shader_vaddline(SHADER_BUFFER* buffer, const char *format, va_list args) { char* base = buffer->buffer + buffer->bsize; int rc; rc = vsnprintf(base, SHADER_PGMSIZE - 1 - buffer->bsize, format, args); if (rc < 0 || /* C89 */ rc > SHADER_PGMSIZE - 1 - buffer->bsize) { /* C99 */ ERR("The buffer allocated for the shader program string " "is too small at %d bytes.\n", SHADER_PGMSIZE); buffer->bsize = SHADER_PGMSIZE - 1; return -1; } if (buffer->newline) { TRACE("GL HW (%u, %u) : %s", buffer->lineNo + 1, buffer->bsize, base); buffer->newline = FALSE; } else { TRACE("%s", base); } buffer->bsize += rc; if (buffer->buffer[buffer->bsize-1] == '\n') { buffer->lineNo++; buffer->newline = TRUE; } return 0; } int shader_addline(SHADER_BUFFER* buffer, const char *format, ...) { int ret; va_list args; va_start(args, format); ret = shader_vaddline(buffer, format, args); va_end(args); return ret; } void shader_init(struct IWineD3DBaseShaderClass *shader, IWineD3DDevice *device) { shader->ref = 1; shader->device = device; list_init(&shader->linked_programs); } /* Convert floating point offset relative * to a register file to an absolute offset for float constants */ static unsigned int shader_get_float_offset(WINED3DSHADER_PARAM_REGISTER_TYPE register_type, UINT register_idx) { switch (register_type) { case WINED3DSPR_CONST: return register_idx; case WINED3DSPR_CONST2: return 2048 + register_idx; case WINED3DSPR_CONST3: return 4096 + register_idx; case WINED3DSPR_CONST4: return 6144 + register_idx; default: FIXME("Unsupported register type: %d\n", register_type); return register_idx; } } static void shader_delete_constant_list(struct list* clist) { struct list *ptr; struct local_constant* constant; ptr = list_head(clist); while (ptr) { constant = LIST_ENTRY(ptr, struct local_constant, entry); ptr = list_next(clist, ptr); HeapFree(GetProcessHeap(), 0, constant); } list_init(clist); } static inline void set_bitmap_bit(DWORD *bitmap, DWORD bit) { DWORD idx, shift; idx = bit >> 5; shift = bit & 0x1f; bitmap[idx] |= (1 << shift); } static void shader_record_register_usage(IWineD3DBaseShaderImpl *This, struct shader_reg_maps *reg_maps, const struct wined3d_shader_register *reg, BOOL pshader) { switch (reg->type) { case WINED3DSPR_TEXTURE: /* WINED3DSPR_ADDR */ if (pshader) reg_maps->texcoord[reg->idx] = 1; else reg_maps->address[reg->idx] = 1; break; case WINED3DSPR_TEMP: reg_maps->temporary[reg->idx] = 1; break; case WINED3DSPR_INPUT: if (!pshader) reg_maps->input_registers |= 1 << reg->idx; else { if (reg->rel_addr) { /* If relative addressing is used, we must assume that all registers * are used. Even if it is a construct like v3[aL], we can't assume * that v0, v1 and v2 aren't read because aL can be negative */ unsigned int i; for (i = 0; i < MAX_REG_INPUT; ++i) { ((IWineD3DPixelShaderImpl *)This)->input_reg_used[i] = TRUE; } } else { ((IWineD3DPixelShaderImpl *)This)->input_reg_used[reg->idx] = TRUE; } } break; case WINED3DSPR_RASTOUT: if (reg->idx == 1) reg_maps->fog = 1; break; case WINED3DSPR_MISCTYPE: if (pshader && reg->idx == 0) reg_maps->vpos = 1; if (pshader && reg->idx == 1) reg_maps->usesfacing = 1; break; case WINED3DSPR_CONST: if (reg->rel_addr) { if (!pshader) { if (reg->idx <= ((IWineD3DVertexShaderImpl *)This)->min_rel_offset) ((IWineD3DVertexShaderImpl *)This)->min_rel_offset = reg->idx; else if (reg->idx >= ((IWineD3DVertexShaderImpl *)This)->max_rel_offset) ((IWineD3DVertexShaderImpl *)This)->max_rel_offset = reg->idx; } reg_maps->usesrelconstF = TRUE; } else { set_bitmap_bit(reg_maps->constf, reg->idx); } break; case WINED3DSPR_CONSTINT: reg_maps->integer_constants |= (1 << reg->idx); break; case WINED3DSPR_CONSTBOOL: reg_maps->boolean_constants |= (1 << reg->idx); break; default: TRACE("Not recording register of type %#x and idx %u\n", reg->type, reg->idx); break; } } static unsigned int get_instr_extra_regcount(enum WINED3D_SHADER_INSTRUCTION_HANDLER instr, unsigned int param) { switch(instr) { case WINED3DSIH_M4x4: case WINED3DSIH_M3x4: return param == 1 ? 3 : 0; case WINED3DSIH_M4x3: case WINED3DSIH_M3x3: return param == 1 ? 2 : 0; case WINED3DSIH_M3x2: return param == 1 ? 1 : 0; default: return 0; } } static const char *shader_semantic_name_from_usage(WINED3DDECLUSAGE usage) { static const char *semantic_names[] = { /* WINED3DDECLUSAGE_POSITION */ "SV_POSITION", /* WINED3DDECLUSAGE_BLENDWEIGHT */ "BLENDWEIGHT", /* WINED3DDECLUSAGE_BLENDINDICES */ "BLENDINDICES", /* WINED3DDECLUSAGE_NORMAL */ "NORMAL", /* WINED3DDECLUSAGE_PSIZE */ "PSIZE", /* WINED3DDECLUSAGE_TEXCOORD */ "TEXCOORD", /* WINED3DDECLUSAGE_TANGENT */ "TANGENT", /* WINED3DDECLUSAGE_BINORMAL */ "BINORMAL", /* WINED3DDECLUSAGE_TESSFACTOR */ "TESSFACTOR", /* WINED3DDECLUSAGE_POSITIONT */ "POSITIONT", /* WINED3DDECLUSAGE_COLOR */ "COLOR", /* WINED3DDECLUSAGE_FOG */ "FOG", /* WINED3DDECLUSAGE_DEPTH */ "DEPTH", /* WINED3DDECLUSAGE_SAMPLE */ "SAMPLE", }; if (usage >= sizeof(semantic_names) / sizeof(*semantic_names)) { FIXME("Unrecognized usage %#x\n", usage); return "UNRECOGNIZED"; } return semantic_names[usage]; } BOOL shader_match_semantic(const char *semantic_name, WINED3DDECLUSAGE usage) { return !strcmp(semantic_name, shader_semantic_name_from_usage(usage)); } static void shader_signature_from_semantic(struct wined3d_shader_signature_element *e, const struct wined3d_shader_semantic *s) { e->semantic_name = shader_semantic_name_from_usage(s->usage); e->semantic_idx = s->usage_idx; e->sysval_semantic = 0; e->component_type = 0; e->register_idx = s->reg.reg.idx; e->mask = s->reg.write_mask; } /* Note that this does not count the loop register * as an address register. */ HRESULT shader_get_registers_used(IWineD3DBaseShader *iface, const struct wined3d_shader_frontend *fe, struct shader_reg_maps *reg_maps, struct wined3d_shader_attribute *attributes, struct wined3d_shader_signature_element *input_signature, struct wined3d_shader_signature_element *output_signature, const DWORD *byte_code, DWORD constf_size) { IWineD3DBaseShaderImpl* This = (IWineD3DBaseShaderImpl*) iface; void *fe_data = This->baseShader.frontend_data; struct wined3d_shader_version shader_version; unsigned int cur_loop_depth = 0, max_loop_depth = 0; const DWORD* pToken = byte_code; char pshader; /* There are some minor differences between pixel and vertex shaders */ memset(reg_maps, 0, sizeof(*reg_maps)); /* get_registers_used is called on every compile on some 1.x shaders, which can result * in stacking up a collection of local constants. Delete the old constants if existing */ shader_delete_constant_list(&This->baseShader.constantsF); shader_delete_constant_list(&This->baseShader.constantsB); shader_delete_constant_list(&This->baseShader.constantsI); fe->shader_read_header(fe_data, &pToken, &shader_version); reg_maps->shader_version = shader_version; pshader = shader_is_pshader_version(shader_version.type); reg_maps->constf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*reg_maps->constf) * ((constf_size + 31) / 32)); if(!reg_maps->constf) { ERR("Out of memory\n"); return E_OUTOFMEMORY; } while (!fe->shader_is_end(fe_data, &pToken)) { struct wined3d_shader_instruction ins; const char *comment; UINT param_size; /* Skip comments */ fe->shader_read_comment(&pToken, &comment); if (comment) continue; /* Fetch opcode */ fe->shader_read_opcode(fe_data, &pToken, &ins, ¶m_size); /* Unhandled opcode, and its parameters */ if (ins.handler_idx == WINED3DSIH_TABLE_SIZE) { TRACE("Skipping unrecognized instruction.\n"); pToken += param_size; continue; } /* Handle declarations */ if (ins.handler_idx == WINED3DSIH_DCL) { struct wined3d_shader_semantic semantic; fe->shader_read_semantic(&pToken, &semantic); switch (semantic.reg.reg.type) { /* Vshader: mark attributes used * Pshader: mark 3.0 input registers used, save token */ case WINED3DSPR_INPUT: reg_maps->input_registers |= 1 << semantic.reg.reg.idx; if (shader_version.type == WINED3D_SHADER_TYPE_VERTEX) { attributes[semantic.reg.reg.idx].usage = semantic.usage; attributes[semantic.reg.reg.idx].usage_idx = semantic.usage_idx; } else { shader_signature_from_semantic(&input_signature[semantic.reg.reg.idx], &semantic); } break; /* Vshader: mark 3.0 output registers used, save token */ case WINED3DSPR_OUTPUT: reg_maps->output_registers |= 1 << semantic.reg.reg.idx; shader_signature_from_semantic(&output_signature[semantic.reg.reg.idx], &semantic); if (semantic.usage == WINED3DDECLUSAGE_FOG) reg_maps->fog = 1; break; /* Save sampler usage token */ case WINED3DSPR_SAMPLER: reg_maps->sampler_type[semantic.reg.reg.idx] = semantic.sampler_type; break; default: TRACE("Not recording DCL register type %#x.\n", semantic.reg.reg.type); break; } } else if (ins.handler_idx == WINED3DSIH_DEF) { struct wined3d_shader_dst_param dst; struct wined3d_shader_src_param rel_addr; local_constant* lconst = HeapAlloc(GetProcessHeap(), 0, sizeof(local_constant)); if (!lconst) return E_OUTOFMEMORY; fe->shader_read_dst_param(fe_data, &pToken, &dst, &rel_addr); lconst->idx = dst.reg.idx; memcpy(lconst->value, pToken, 4 * sizeof(DWORD)); pToken += 4; /* In pixel shader 1.X shaders, the constants are clamped between [-1;1] */ if (shader_version.major == 1 && pshader) { float *value = (float *) lconst->value; if(value[0] < -1.0) value[0] = -1.0; else if(value[0] > 1.0) value[0] = 1.0; if(value[1] < -1.0) value[1] = -1.0; else if(value[1] > 1.0) value[1] = 1.0; if(value[2] < -1.0) value[2] = -1.0; else if(value[2] > 1.0) value[2] = 1.0; if(value[3] < -1.0) value[3] = -1.0; else if(value[3] > 1.0) value[3] = 1.0; } list_add_head(&This->baseShader.constantsF, &lconst->entry); } else if (ins.handler_idx == WINED3DSIH_DEFI) { struct wined3d_shader_dst_param dst; struct wined3d_shader_src_param rel_addr; local_constant* lconst = HeapAlloc(GetProcessHeap(), 0, sizeof(local_constant)); if (!lconst) return E_OUTOFMEMORY; fe->shader_read_dst_param(fe_data, &pToken, &dst, &rel_addr); lconst->idx = dst.reg.idx; memcpy(lconst->value, pToken, 4 * sizeof(DWORD)); pToken += 4; list_add_head(&This->baseShader.constantsI, &lconst->entry); reg_maps->local_int_consts |= (1 << dst.reg.idx); } else if (ins.handler_idx == WINED3DSIH_DEFB) { struct wined3d_shader_dst_param dst; struct wined3d_shader_src_param rel_addr; local_constant* lconst = HeapAlloc(GetProcessHeap(), 0, sizeof(local_constant)); if (!lconst) return E_OUTOFMEMORY; fe->shader_read_dst_param(fe_data, &pToken, &dst, &rel_addr); lconst->idx = dst.reg.idx; memcpy(lconst->value, pToken, sizeof(DWORD)); ++pToken; list_add_head(&This->baseShader.constantsB, &lconst->entry); reg_maps->local_bool_consts |= (1 << dst.reg.idx); } /* If there's a loop in the shader */ else if (ins.handler_idx == WINED3DSIH_LOOP || ins.handler_idx == WINED3DSIH_REP) { struct wined3d_shader_src_param src, rel_addr; fe->shader_read_src_param(fe_data, &pToken, &src, &rel_addr); /* Rep and Loop always use an integer constant for the control parameters */ if (ins.handler_idx == WINED3DSIH_REP) { reg_maps->integer_constants |= 1 << src.reg.idx; } else { fe->shader_read_src_param(fe_data, &pToken, &src, &rel_addr); reg_maps->integer_constants |= 1 << src.reg.idx; } cur_loop_depth++; if(cur_loop_depth > max_loop_depth) max_loop_depth = cur_loop_depth; } else if (ins.handler_idx == WINED3DSIH_ENDLOOP || ins.handler_idx == WINED3DSIH_ENDREP) { cur_loop_depth--; } /* For subroutine prototypes */ else if (ins.handler_idx == WINED3DSIH_LABEL) { struct wined3d_shader_src_param src, rel_addr; fe->shader_read_src_param(fe_data, &pToken, &src, &rel_addr); reg_maps->labels[src.reg.idx] = 1; } /* Set texture, address, temporary registers */ else { int i, limit; BOOL color0_mov = FALSE; /* This will loop over all the registers and try to * make a bitmask of the ones we're interested in. * * Relative addressing tokens are ignored, but that's * okay, since we'll catch any address registers when * they are initialized (required by spec) */ if (ins.dst_count) { struct wined3d_shader_dst_param dst_param; struct wined3d_shader_src_param dst_rel_addr; fe->shader_read_dst_param(fe_data, &pToken, &dst_param, &dst_rel_addr); /* WINED3DSPR_TEXCRDOUT is the same as WINED3DSPR_OUTPUT. _OUTPUT can be > MAX_REG_TEXCRD and * is used in >= 3.0 shaders. Filter 3.0 shaders to prevent overflows, and also filter pixel * shaders because TECRDOUT isn't used in them, but future register types might cause issues */ if (!pshader && shader_version.major < 3 && dst_param.reg.type == WINED3DSPR_TEXCRDOUT) { reg_maps->texcoord_mask[dst_param.reg.type] |= dst_param.write_mask; } else { if(pshader && dst_param.reg.type == WINED3DSPR_COLOROUT && dst_param.reg.idx == 0) { IWineD3DPixelShaderImpl *ps = (IWineD3DPixelShaderImpl *) This; ps->color0_mov = FALSE; } shader_record_register_usage(This, reg_maps, &dst_param.reg, pshader); } /* Declare 1.X samplers implicitly, based on the destination reg. number */ if (shader_version.major == 1 && pshader /* Filter different instructions with the same enum values in VS */ && (ins.handler_idx == WINED3DSIH_TEX || ins.handler_idx == WINED3DSIH_TEXBEM || ins.handler_idx == WINED3DSIH_TEXBEML || ins.handler_idx == WINED3DSIH_TEXDP3TEX || ins.handler_idx == WINED3DSIH_TEXM3x2TEX || ins.handler_idx == WINED3DSIH_TEXM3x3SPEC || ins.handler_idx == WINED3DSIH_TEXM3x3TEX || ins.handler_idx == WINED3DSIH_TEXM3x3VSPEC || ins.handler_idx == WINED3DSIH_TEXREG2AR || ins.handler_idx == WINED3DSIH_TEXREG2GB || ins.handler_idx == WINED3DSIH_TEXREG2RGB)) { /* Fake sampler usage, only set reserved bit and ttype */ DWORD sampler_code = dst_param.reg.idx; TRACE("Setting fake 2D sampler for 1.x pixelshader\n"); reg_maps->sampler_type[sampler_code] = WINED3DSTT_2D; /* texbem is only valid with < 1.4 pixel shaders */ if (ins.handler_idx == WINED3DSIH_TEXBEM || ins.handler_idx == WINED3DSIH_TEXBEML) { reg_maps->bumpmat[sampler_code] = TRUE; if (ins.handler_idx == WINED3DSIH_TEXBEML) { reg_maps->luminanceparams[sampler_code] = TRUE; } } } else if (pshader && ins.handler_idx == WINED3DSIH_BEM) { reg_maps->bumpmat[dst_param.reg.idx] = TRUE; } else if(pshader && ins.handler_idx == WINED3DSIH_MOV) { /* Many 2.0 and 3.0 pixel shaders end with a MOV from a temp register to * COLOROUT 0. If we know this in advance, the ARB shader backend can skip * the mov and perform the sRGB write correction from the source register. * * However, if the mov is only partial, we can't do this, and if the write * comes from an instruction other than MOV it is hard to do as well. If * COLOROUT 0 is overwritten partially later, the marker is dropped again */ if(dst_param.reg.type == WINED3DSPR_COLOROUT && dst_param.reg.idx == 0) { /* Used later when the source register is read */ color0_mov = TRUE; } } } if (ins.handler_idx == WINED3DSIH_NRM) { reg_maps->usesnrm = 1; } else if (ins.handler_idx == WINED3DSIH_DSY) { reg_maps->usesdsy = 1; } else if (ins.handler_idx == WINED3DSIH_DSX) { reg_maps->usesdsx = 1; } else if(ins.handler_idx == WINED3DSIH_TEXLDD) { reg_maps->usestexldd = 1; } else if(ins.handler_idx == WINED3DSIH_TEXLDL) { reg_maps->usestexldl = 1; } else if(ins.handler_idx == WINED3DSIH_MOVA) { reg_maps->usesmova = 1; } limit = ins.src_count + (ins.predicate ? 1 : 0); for (i = 0; i < limit; ++i) { struct wined3d_shader_src_param src_param, src_rel_addr; unsigned int count; fe->shader_read_src_param(fe_data, &pToken, &src_param, &src_rel_addr); count = get_instr_extra_regcount(ins.handler_idx, i); shader_record_register_usage(This, reg_maps, &src_param.reg, pshader); while (count) { ++src_param.reg.idx; shader_record_register_usage(This, reg_maps, &src_param.reg, pshader); --count; } if(color0_mov) { IWineD3DPixelShaderImpl *ps = (IWineD3DPixelShaderImpl *) This; if(src_param.reg.type == WINED3DSPR_TEMP && src_param.swizzle == WINED3DSP_NOSWIZZLE) { ps->color0_mov = TRUE; ps->color0_reg = src_param.reg.idx; } } } } } reg_maps->loop_depth = max_loop_depth; This->baseShader.functionLength = ((char *)pToken - (char *)byte_code); return WINED3D_OK; } static void shader_dump_decl_usage(const struct wined3d_shader_semantic *semantic, const struct wined3d_shader_version *shader_version) { TRACE("dcl"); if (semantic->reg.reg.type == WINED3DSPR_SAMPLER) { switch (semantic->sampler_type) { case WINED3DSTT_2D: TRACE("_2d"); break; case WINED3DSTT_CUBE: TRACE("_cube"); break; case WINED3DSTT_VOLUME: TRACE("_volume"); break; default: TRACE("_unknown_ttype(0x%08x)", semantic->sampler_type); } } else { /* Pixel shaders 3.0 don't have usage semantics */ if (shader_is_pshader_version(shader_version->type) && shader_version->major < 3) return; else TRACE("_"); switch (semantic->usage) { case WINED3DDECLUSAGE_POSITION: TRACE("position%d", semantic->usage_idx); break; case WINED3DDECLUSAGE_BLENDINDICES: TRACE("blend"); break; case WINED3DDECLUSAGE_BLENDWEIGHT: TRACE("weight"); break; case WINED3DDECLUSAGE_NORMAL: TRACE("normal%d", semantic->usage_idx); break; case WINED3DDECLUSAGE_PSIZE: TRACE("psize"); break; case WINED3DDECLUSAGE_COLOR: if (semantic->usage_idx == 0) TRACE("color"); else TRACE("specular%d", (semantic->usage_idx - 1)); break; case WINED3DDECLUSAGE_TEXCOORD: TRACE("texture%d", semantic->usage_idx); break; case WINED3DDECLUSAGE_TANGENT: TRACE("tangent"); break; case WINED3DDECLUSAGE_BINORMAL: TRACE("binormal"); break; case WINED3DDECLUSAGE_TESSFACTOR: TRACE("tessfactor"); break; case WINED3DDECLUSAGE_POSITIONT: TRACE("positionT%d", semantic->usage_idx); break; case WINED3DDECLUSAGE_FOG: TRACE("fog"); break; case WINED3DDECLUSAGE_DEPTH: TRACE("depth"); break; case WINED3DDECLUSAGE_SAMPLE: TRACE("sample"); break; default: FIXME("unknown_semantics(0x%08x)", semantic->usage); } } } static void shader_dump_register(const struct wined3d_shader_register *reg, const struct wined3d_shader_version *shader_version) { static const char * const rastout_reg_names[] = {"oPos", "oFog", "oPts"}; static const char * const misctype_reg_names[] = {"vPos", "vFace"}; UINT offset = reg->idx; switch (reg->type) { case WINED3DSPR_TEMP: TRACE("r"); break; case WINED3DSPR_INPUT: TRACE("v"); break; case WINED3DSPR_CONST: case WINED3DSPR_CONST2: case WINED3DSPR_CONST3: case WINED3DSPR_CONST4: TRACE("c"); offset = shader_get_float_offset(reg->type, reg->idx); break; case WINED3DSPR_TEXTURE: /* vs: case WINED3DSPR_ADDR */ TRACE("%c", shader_is_pshader_version(shader_version->type) ? 't' : 'a'); break; case WINED3DSPR_RASTOUT: TRACE("%s", rastout_reg_names[reg->idx]); break; case WINED3DSPR_COLOROUT: TRACE("oC"); break; case WINED3DSPR_DEPTHOUT: TRACE("oDepth"); break; case WINED3DSPR_ATTROUT: TRACE("oD"); break; case WINED3DSPR_TEXCRDOUT: /* Vertex shaders >= 3.0 use general purpose output registers * (WINED3DSPR_OUTPUT), which can include an address token */ if (shader_version->major >= 3) TRACE("o"); else TRACE("oT"); break; case WINED3DSPR_CONSTINT: TRACE("i"); break; case WINED3DSPR_CONSTBOOL: TRACE("b"); break; case WINED3DSPR_LABEL: TRACE("l"); break; case WINED3DSPR_LOOP: TRACE("aL"); break; case WINED3DSPR_SAMPLER: TRACE("s"); break; case WINED3DSPR_MISCTYPE: if (reg->idx > 1) FIXME("Unhandled misctype register %d\n", reg->idx); else TRACE("%s", misctype_reg_names[reg->idx]); break; case WINED3DSPR_PREDICATE: TRACE("p"); break; case WINED3DSPR_IMMCONST: TRACE("l"); break; default: TRACE("unhandled_rtype(%#x)", reg->type); break; } if (reg->type == WINED3DSPR_IMMCONST) { TRACE("("); switch (reg->immconst_type) { case WINED3D_IMMCONST_FLOAT: TRACE("%.8e", *(float *)reg->immconst_data); break; case WINED3D_IMMCONST_FLOAT4: TRACE("%.8e, %.8e, %.8e, %.8e", *(float *)®->immconst_data[0], *(float *)®->immconst_data[1], *(float *)®->immconst_data[2], *(float *)®->immconst_data[3]); break; default: TRACE("", reg->immconst_type); break; } TRACE(")"); } else if (reg->type != WINED3DSPR_RASTOUT && reg->type != WINED3DSPR_MISCTYPE) { if (reg->rel_addr) { TRACE("["); shader_dump_src_param(reg->rel_addr, shader_version); TRACE(" + "); } TRACE("%u", offset); if (reg->rel_addr) TRACE("]"); } } void shader_dump_dst_param(const struct wined3d_shader_dst_param *param, const struct wined3d_shader_version *shader_version) { DWORD write_mask = param->write_mask; shader_dump_register(¶m->reg, shader_version); if (write_mask != WINED3DSP_WRITEMASK_ALL) { static const char *write_mask_chars = "xyzw"; TRACE("."); if (write_mask & WINED3DSP_WRITEMASK_0) TRACE("%c", write_mask_chars[0]); if (write_mask & WINED3DSP_WRITEMASK_1) TRACE("%c", write_mask_chars[1]); if (write_mask & WINED3DSP_WRITEMASK_2) TRACE("%c", write_mask_chars[2]); if (write_mask & WINED3DSP_WRITEMASK_3) TRACE("%c", write_mask_chars[3]); } } void shader_dump_src_param(const struct wined3d_shader_src_param *param, const struct wined3d_shader_version *shader_version) { DWORD src_modifier = param->modifiers; DWORD swizzle = param->swizzle; if (src_modifier == WINED3DSPSM_NEG || src_modifier == WINED3DSPSM_BIASNEG || src_modifier == WINED3DSPSM_SIGNNEG || src_modifier == WINED3DSPSM_X2NEG || src_modifier == WINED3DSPSM_ABSNEG) TRACE("-"); else if (src_modifier == WINED3DSPSM_COMP) TRACE("1-"); else if (src_modifier == WINED3DSPSM_NOT) TRACE("!"); if (src_modifier == WINED3DSPSM_ABS || src_modifier == WINED3DSPSM_ABSNEG) TRACE("abs("); shader_dump_register(¶m->reg, shader_version); if (src_modifier) { switch (src_modifier) { case WINED3DSPSM_NONE: break; case WINED3DSPSM_NEG: break; case WINED3DSPSM_NOT: break; case WINED3DSPSM_BIAS: TRACE("_bias"); break; case WINED3DSPSM_BIASNEG: TRACE("_bias"); break; case WINED3DSPSM_SIGN: TRACE("_bx2"); break; case WINED3DSPSM_SIGNNEG: TRACE("_bx2"); break; case WINED3DSPSM_COMP: break; case WINED3DSPSM_X2: TRACE("_x2"); break; case WINED3DSPSM_X2NEG: TRACE("_x2"); break; case WINED3DSPSM_DZ: TRACE("_dz"); break; case WINED3DSPSM_DW: TRACE("_dw"); break; case WINED3DSPSM_ABSNEG: TRACE(")"); break; case WINED3DSPSM_ABS: TRACE(")"); break; default: TRACE("_unknown_modifier(%#x)", src_modifier); } } if (swizzle != WINED3DSP_NOSWIZZLE) { static const char *swizzle_chars = "xyzw"; DWORD swizzle_x = swizzle & 0x03; DWORD swizzle_y = (swizzle >> 2) & 0x03; DWORD swizzle_z = (swizzle >> 4) & 0x03; DWORD swizzle_w = (swizzle >> 6) & 0x03; if (swizzle_x == swizzle_y && swizzle_x == swizzle_z && swizzle_x == swizzle_w) { TRACE(".%c", swizzle_chars[swizzle_x]); } else { TRACE(".%c%c%c%c", swizzle_chars[swizzle_x], swizzle_chars[swizzle_y], swizzle_chars[swizzle_z], swizzle_chars[swizzle_w]); } } } /* Shared code in order to generate the bulk of the shader string. * NOTE: A description of how to parse tokens can be found on msdn */ void shader_generate_main(IWineD3DBaseShader *iface, SHADER_BUFFER *buffer, const shader_reg_maps *reg_maps, const DWORD *pFunction, void *backend_ctx) { IWineD3DBaseShaderImpl* This = (IWineD3DBaseShaderImpl*) iface; IWineD3DDeviceImpl *device = (IWineD3DDeviceImpl *) This->baseShader.device; /* To access shader backend callbacks */ const struct wined3d_shader_frontend *fe = This->baseShader.frontend; void *fe_data = This->baseShader.frontend_data; struct wined3d_shader_src_param src_rel_addr[4]; struct wined3d_shader_src_param src_param[4]; struct wined3d_shader_version shader_version; struct wined3d_shader_src_param dst_rel_addr; struct wined3d_shader_dst_param dst_param; struct wined3d_shader_instruction ins; struct wined3d_shader_context ctx; const DWORD *pToken = pFunction; DWORD i; /* Initialize current parsing state */ ctx.shader = iface; ctx.reg_maps = reg_maps; ctx.buffer = buffer; ctx.backend_data = backend_ctx; ins.ctx = &ctx; ins.dst = &dst_param; ins.src = src_param; This->baseShader.parse_state.current_row = 0; fe->shader_read_header(fe_data, &pToken, &shader_version); while (!fe->shader_is_end(fe_data, &pToken)) { const char *comment; UINT param_size; /* Skip comment tokens */ fe->shader_read_comment(&pToken, &comment); if (comment) continue; /* Read opcode */ fe->shader_read_opcode(fe_data, &pToken, &ins, ¶m_size); /* Unknown opcode and its parameters */ if (ins.handler_idx == WINED3DSIH_TABLE_SIZE) { TRACE("Skipping unrecognized instruction.\n"); pToken += param_size; continue; } /* Nothing to do */ if (ins.handler_idx == WINED3DSIH_DCL || ins.handler_idx == WINED3DSIH_NOP || ins.handler_idx == WINED3DSIH_DEF || ins.handler_idx == WINED3DSIH_DEFI || ins.handler_idx == WINED3DSIH_DEFB || ins.handler_idx == WINED3DSIH_PHASE || ins.handler_idx == WINED3DSIH_RET) { pToken += param_size; continue; } /* Destination token */ if (ins.dst_count) fe->shader_read_dst_param(fe_data, &pToken, &dst_param, &dst_rel_addr); /* Predication token */ if (ins.predicate) ins.predicate = *pToken++; /* Other source tokens */ for (i = 0; i < ins.src_count; ++i) { fe->shader_read_src_param(fe_data, &pToken, &src_param[i], &src_rel_addr[i]); } /* Call appropriate function for output target */ device->shader_backend->shader_handle_instruction(&ins); } } static void shader_dump_ins_modifiers(const struct wined3d_shader_dst_param *dst) { DWORD mmask = dst->modifiers; switch (dst->shift) { case 0: break; case 13: TRACE("_d8"); break; case 14: TRACE("_d4"); break; case 15: TRACE("_d2"); break; case 1: TRACE("_x2"); break; case 2: TRACE("_x4"); break; case 3: TRACE("_x8"); break; default: TRACE("_unhandled_shift(%d)", dst->shift); break; } if (mmask & WINED3DSPDM_SATURATE) TRACE("_sat"); if (mmask & WINED3DSPDM_PARTIALPRECISION) TRACE("_pp"); if (mmask & WINED3DSPDM_MSAMPCENTROID) TRACE("_centroid"); mmask &= ~(WINED3DSPDM_SATURATE | WINED3DSPDM_PARTIALPRECISION | WINED3DSPDM_MSAMPCENTROID); if (mmask) FIXME("_unrecognized_modifier(%#x)", mmask); } void shader_trace_init(const struct wined3d_shader_frontend *fe, void *fe_data, const DWORD *pFunction) { struct wined3d_shader_version shader_version; const DWORD* pToken = pFunction; DWORD i; TRACE("Parsing %p\n", pFunction); fe->shader_read_header(fe_data, &pToken, &shader_version); TRACE("%s_%u_%u\n", shader_is_pshader_version(shader_version.type) ? "ps": "vs", shader_version.major, shader_version.minor); while (!fe->shader_is_end(fe_data, &pToken)) { struct wined3d_shader_instruction ins; const char *comment; UINT param_size; /* comment */ fe->shader_read_comment(&pToken, &comment); if (comment) { TRACE("//%s\n", comment); continue; } fe->shader_read_opcode(fe_data, &pToken, &ins, ¶m_size); if (ins.handler_idx == WINED3DSIH_TABLE_SIZE) { TRACE("Skipping unrecognized instruction.\n"); pToken += param_size; continue; } if (ins.handler_idx == WINED3DSIH_DCL) { struct wined3d_shader_semantic semantic; fe->shader_read_semantic(&pToken, &semantic); shader_dump_decl_usage(&semantic, &shader_version); shader_dump_ins_modifiers(&semantic.reg); TRACE(" "); shader_dump_dst_param(&semantic.reg, &shader_version); } else if (ins.handler_idx == WINED3DSIH_DEF) { struct wined3d_shader_dst_param dst; struct wined3d_shader_src_param rel_addr; fe->shader_read_dst_param(fe_data, &pToken, &dst, &rel_addr); TRACE("def c%u = %f, %f, %f, %f", shader_get_float_offset(dst.reg.type, dst.reg.idx), *(const float *)(pToken), *(const float *)(pToken + 1), *(const float *)(pToken + 2), *(const float *)(pToken + 3)); pToken += 4; } else if (ins.handler_idx == WINED3DSIH_DEFI) { struct wined3d_shader_dst_param dst; struct wined3d_shader_src_param rel_addr; fe->shader_read_dst_param(fe_data, &pToken, &dst, &rel_addr); TRACE("defi i%u = %d, %d, %d, %d", dst.reg.idx, *(pToken), *(pToken + 1), *(pToken + 2), *(pToken + 3)); pToken += 4; } else if (ins.handler_idx == WINED3DSIH_DEFB) { struct wined3d_shader_dst_param dst; struct wined3d_shader_src_param rel_addr; fe->shader_read_dst_param(fe_data, &pToken, &dst, &rel_addr); TRACE("defb b%u = %s", dst.reg.idx, *pToken ? "true" : "false"); ++pToken; } else { struct wined3d_shader_src_param dst_rel_addr, src_rel_addr; struct wined3d_shader_dst_param dst_param; struct wined3d_shader_src_param src_param; if (ins.dst_count) { fe->shader_read_dst_param(fe_data, &pToken, &dst_param, &dst_rel_addr); } /* Print out predication source token first - it follows * the destination token. */ if (ins.predicate) { fe->shader_read_src_param(fe_data, &pToken, &src_param, &src_rel_addr); TRACE("("); shader_dump_src_param(&src_param, &shader_version); TRACE(") "); } /* PixWin marks instructions with the coissue flag with a '+' */ if (ins.coissue) TRACE("+"); TRACE("%s", shader_opcode_names[ins.handler_idx]); if (ins.handler_idx == WINED3DSIH_IFC || ins.handler_idx == WINED3DSIH_BREAKC) { switch (ins.flags) { case COMPARISON_GT: TRACE("_gt"); break; case COMPARISON_EQ: TRACE("_eq"); break; case COMPARISON_GE: TRACE("_ge"); break; case COMPARISON_LT: TRACE("_lt"); break; case COMPARISON_NE: TRACE("_ne"); break; case COMPARISON_LE: TRACE("_le"); break; default: TRACE("_(%u)", ins.flags); } } else if (ins.handler_idx == WINED3DSIH_TEX && shader_version.major >= 2 && (ins.flags & WINED3DSI_TEXLD_PROJECT)) { TRACE("p"); } /* We already read the destination token, print it. */ if (ins.dst_count) { shader_dump_ins_modifiers(&dst_param); TRACE(" "); shader_dump_dst_param(&dst_param, &shader_version); } /* Other source tokens */ for (i = ins.dst_count; i < (ins.dst_count + ins.src_count); ++i) { fe->shader_read_src_param(fe_data, &pToken, &src_param, &src_rel_addr); TRACE(!i ? " " : ", "); shader_dump_src_param(&src_param, &shader_version); } } TRACE("\n"); } } void shader_cleanup(IWineD3DBaseShader *iface) { IWineD3DBaseShaderImpl *This = (IWineD3DBaseShaderImpl *)iface; ((IWineD3DDeviceImpl *)This->baseShader.device)->shader_backend->shader_destroy(iface); HeapFree(GetProcessHeap(), 0, This->baseShader.reg_maps.constf); HeapFree(GetProcessHeap(), 0, This->baseShader.function); shader_delete_constant_list(&This->baseShader.constantsF); shader_delete_constant_list(&This->baseShader.constantsB); shader_delete_constant_list(&This->baseShader.constantsI); list_remove(&This->baseShader.shader_list_entry); if (This->baseShader.frontend && This->baseShader.frontend_data) { This->baseShader.frontend->shader_free(This->baseShader.frontend_data); } } static void shader_none_handle_instruction(const struct wined3d_shader_instruction *ins) {} static void shader_none_select(IWineD3DDevice *iface, BOOL usePS, BOOL useVS) {} static void shader_none_select_depth_blt(IWineD3DDevice *iface, enum tex_types tex_type) {} static void shader_none_deselect_depth_blt(IWineD3DDevice *iface) {} static void shader_none_update_float_vertex_constants(IWineD3DDevice *iface, UINT start, UINT count) {} static void shader_none_update_float_pixel_constants(IWineD3DDevice *iface, UINT start, UINT count) {} static void shader_none_load_constants(IWineD3DDevice *iface, char usePS, char useVS) {} static void shader_none_load_np2fixup_constants(IWineD3DDevice *iface, char usePS, char useVS) {} static void shader_none_destroy(IWineD3DBaseShader *iface) {} static HRESULT shader_none_alloc(IWineD3DDevice *iface) {return WINED3D_OK;} static void shader_none_free(IWineD3DDevice *iface) {} static BOOL shader_none_dirty_const(IWineD3DDevice *iface) {return FALSE;} #define GLINFO_LOCATION (*gl_info) static void shader_none_get_caps(WINED3DDEVTYPE devtype, const WineD3D_GL_Info *gl_info, struct shader_caps *pCaps) { /* Set the shader caps to 0 for the none shader backend */ pCaps->VertexShaderVersion = 0; pCaps->PixelShaderVersion = 0; pCaps->PixelShader1xMaxValue = 0.0; } #undef GLINFO_LOCATION static BOOL shader_none_color_fixup_supported(struct color_fixup_desc fixup) { if (TRACE_ON(d3d_shader) && TRACE_ON(d3d)) { TRACE("Checking support for fixup:\n"); dump_color_fixup_desc(fixup); } /* Faked to make some apps happy. */ if (!is_yuv_fixup(fixup)) { TRACE("[OK]\n"); return TRUE; } TRACE("[FAILED]\n"); return FALSE; } const shader_backend_t none_shader_backend = { shader_none_handle_instruction, shader_none_select, shader_none_select_depth_blt, shader_none_deselect_depth_blt, shader_none_update_float_vertex_constants, shader_none_update_float_pixel_constants, shader_none_load_constants, shader_none_load_np2fixup_constants, shader_none_destroy, shader_none_alloc, shader_none_free, shader_none_dirty_const, shader_none_get_caps, shader_none_color_fixup_supported, };