wined3d: Created generate_base_shader which will be shared by pixel & vertex shaders.
[wine/wine64.git] / dlls / wined3d / baseshader.c
blobf1929d6b5957ce6f932a59260591f4de56776a07
1 /*
2 * shaders implementation
4 * Copyright 2002-2003 Jason Edmeades
5 * Copyright 2002-2003 Raphael Junqueira
6 * Copyright 2005 Oliver Stieber
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "config.h"
24 #include <string.h>
25 #include <stdio.h>
26 #include "wined3d_private.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(d3d_shader);
30 #define GLNAME_REQUIRE_GLSL ((const char *)1)
32 inline static BOOL shader_is_version_token(DWORD token) {
33 return 0xFFFF0000 == (token & 0xFFFF0000) ||
34 0xFFFE0000 == (token & 0xFFFF0000);
37 inline static BOOL shader_is_comment_token(DWORD token) {
38 return D3DSIO_COMMENT == (token & D3DSI_OPCODE_MASK);
41 int shader_addline(
42 SHADER_BUFFER* buffer,
43 const char *format, ...) {
45 char* base = buffer->buffer + buffer->bsize;
46 int rc;
48 va_list args;
49 va_start(args, format);
50 rc = vsnprintf(base, SHADER_PGMSIZE - 1 - buffer->bsize, format, args);
51 va_end(args);
53 if (rc < 0 || /* C89 */
54 rc > SHADER_PGMSIZE - 1 - buffer->bsize) { /* C99 */
56 ERR("The buffer allocated for the shader program string "
57 "is too small at %d bytes.\n", SHADER_PGMSIZE);
58 buffer->bsize = SHADER_PGMSIZE - 1;
59 return -1;
62 buffer->bsize += rc;
63 buffer->lineNo++;
64 TRACE("GL HW (%u, %u) : %s", buffer->lineNo, buffer->bsize, base);
65 return 0;
68 const SHADER_OPCODE* shader_get_opcode(
69 IWineD3DBaseShader *iface, const DWORD code) {
71 IWineD3DBaseShaderImpl *This = (IWineD3DBaseShaderImpl*) iface;
73 DWORD i = 0;
74 DWORD version = This->baseShader.version;
75 DWORD hex_version = This->baseShader.hex_version;
76 const SHADER_OPCODE *shader_ins = This->baseShader.shader_ins;
78 /** TODO: use dichotomic search */
79 while (NULL != shader_ins[i].name) {
80 if (((code & D3DSI_OPCODE_MASK) == shader_ins[i].opcode) &&
81 (((hex_version >= shader_ins[i].min_version) && (hex_version <= shader_ins[i].max_version)) ||
82 ((shader_ins[i].min_version == 0) && (shader_ins[i].max_version == 0)))) {
83 return &shader_ins[i];
85 ++i;
87 FIXME("Unsupported opcode %lx(%ld) masked %lx version %ld\n",
88 code, code, code & D3DSI_OPCODE_MASK, version);
89 return NULL;
92 /* Note: For vertex shaders,
93 * texUsed = addrUsed, and
94 * D3DSPR_TEXTURE = D3DSPR_ADDR.
96 * Also note that this does not count the loop register
97 * as an address register. */
99 void shader_get_registers_used(
100 IWineD3DBaseShader *iface,
101 CONST DWORD* pToken) {
103 IWineD3DBaseShaderImpl* This = (IWineD3DBaseShaderImpl*) iface;
104 DWORD* tempsUsed = &This->baseShader.temps_used;
105 DWORD* texUsed = &This->baseShader.textures_used;
107 if (pToken == NULL)
108 return;
110 *tempsUsed = 0;
111 *texUsed = 0;
113 while (D3DVS_END() != *pToken) {
114 CONST SHADER_OPCODE* curOpcode;
116 /* Skip version */
117 if (shader_is_version_token(*pToken)) {
118 ++pToken;
119 continue;
121 /* Skip comments */
122 } else if (shader_is_comment_token(*pToken)) {
123 DWORD comment_len = (*pToken & D3DSI_COMMENTSIZE_MASK) >> D3DSI_COMMENTSIZE_SHIFT;
124 ++pToken;
125 pToken += comment_len;
126 continue;
129 /* Fetch opcode */
130 curOpcode = shader_get_opcode(iface, *pToken);
131 ++pToken;
133 /* Unhandled opcode, and its parameters */
134 if (NULL == curOpcode) {
135 while (*pToken & 0x80000000)
136 ++pToken;
137 continue;
139 /* Skip declarations (for now) */
140 } else if (D3DSIO_DCL == curOpcode->opcode) {
141 pToken += curOpcode->num_params;
142 continue;
144 /* Skip definitions (for now) */
145 } else if (D3DSIO_DEF == curOpcode->opcode) {
146 pToken += curOpcode->num_params;
147 continue;
149 /* Set texture registers, and temporary registers */
150 } else {
151 int i;
153 for (i = 0; i < curOpcode->num_params; ++i) {
154 DWORD regtype = (((*pToken) & D3DSP_REGTYPE_MASK) >> D3DSP_REGTYPE_SHIFT);
155 DWORD reg = (*pToken) & D3DSP_REGNUM_MASK;
156 if (D3DSPR_TEXTURE == regtype)
157 *texUsed |= (1 << reg);
158 if (D3DSPR_TEMP == regtype)
159 *tempsUsed |= (1 << reg);
160 ++pToken;
166 void shader_program_dump_decl_usage(
167 DWORD decl,
168 DWORD param) {
170 DWORD regtype = shader_get_regtype(param);
171 TRACE("dcl_");
173 if (regtype == D3DSPR_SAMPLER) {
174 DWORD ttype = decl & D3DSP_TEXTURETYPE_MASK;
176 switch (ttype) {
177 case D3DSTT_2D: TRACE("2d"); break;
178 case D3DSTT_CUBE: TRACE("cube"); break;
179 case D3DSTT_VOLUME: TRACE("volume"); break;
180 default: TRACE("unknown_ttype(%08lx)", ttype);
183 } else {
185 DWORD usage = decl & D3DSP_DCL_USAGE_MASK;
186 DWORD idx = (decl & D3DSP_DCL_USAGEINDEX_MASK) >> D3DSP_DCL_USAGEINDEX_SHIFT;
188 switch(usage) {
189 case D3DDECLUSAGE_POSITION:
190 TRACE("%s%ld", "position", idx);
191 break;
192 case D3DDECLUSAGE_BLENDINDICES:
193 TRACE("%s", "blend");
194 break;
195 case D3DDECLUSAGE_BLENDWEIGHT:
196 TRACE("%s", "weight");
197 break;
198 case D3DDECLUSAGE_NORMAL:
199 TRACE("%s%ld", "normal", idx);
200 break;
201 case D3DDECLUSAGE_PSIZE:
202 TRACE("%s", "psize");
203 break;
204 case D3DDECLUSAGE_COLOR:
205 if(idx == 0) {
206 TRACE("%s", "color");
207 } else {
208 TRACE("%s%ld", "specular", (idx - 1));
210 break;
211 case D3DDECLUSAGE_TEXCOORD:
212 TRACE("%s%ld", "texture", idx);
213 break;
214 case D3DDECLUSAGE_TANGENT:
215 TRACE("%s", "tangent");
216 break;
217 case D3DDECLUSAGE_BINORMAL:
218 TRACE("%s", "binormal");
219 break;
220 case D3DDECLUSAGE_TESSFACTOR:
221 TRACE("%s", "tessfactor");
222 break;
223 case D3DDECLUSAGE_POSITIONT:
224 TRACE("%s%ld", "positionT", idx);
225 break;
226 case D3DDECLUSAGE_FOG:
227 TRACE("%s", "fog");
228 break;
229 case D3DDECLUSAGE_DEPTH:
230 TRACE("%s", "depth");
231 break;
232 case D3DDECLUSAGE_SAMPLE:
233 TRACE("%s", "sample");
234 break;
235 default:
236 FIXME("unknown_semantics(%08lx)", usage);
241 /** Shared code in order to generate the bulk of the shader string.
242 Use the shader_header_fct & shader_footer_fct to add strings
243 that are specific to pixel or vertex functions
244 NOTE: A description of how to parse tokens can be found at:
245 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/graphics/hh/graphics/usermodedisplaydriver_shader_cc8e4e05-f5c3-4ec0-8853-8ce07c1551b2.xml.asp */
246 void generate_base_shader(
247 IWineD3DBaseShader *iface,
248 SHADER_BUFFER* buffer,
249 CONST DWORD* pFunction) {
251 IWineD3DBaseShaderImpl* This = (IWineD3DBaseShaderImpl*) iface;
252 const DWORD *pToken = pFunction;
253 const SHADER_OPCODE *curOpcode = NULL;
254 DWORD i;
256 /* Initialize current parsing state */
257 This->baseShader.parse_state.current_row = 0;
259 /* First pass: figure out which temporary and texture registers are used */
260 shader_get_registers_used(iface, pToken);
261 TRACE("Texture/Address registers used: %#lx, Temp registers used %#lx\n",
262 This->baseShader.textures_used, This->baseShader.temps_used);
264 /* TODO: check register usage against GL/Directx limits, and fail if they're exceeded
265 nUseAddressRegister < = GL_MAX_PROGRAM_ADDRESS_REGISTERS_AR
266 nUseTempRegister <= GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB
269 /* Pre-declare registers */
270 for(i = 0; i < This->baseShader.limits.temporary; i++) {
271 if (This->baseShader.temps_used & (1 << i))
272 shader_addline(buffer, "TEMP R%lu;\n", i);
275 for (i = 0; i < This->baseShader.limits.address; i++) {
276 if (This->baseShader.textures_used & (1 << i))
277 shader_addline(buffer, "ADDRESS A%ld;\n", i);
280 for(i = 0; i < This->baseShader.limits.texture; i++) {
281 if (This->baseShader.textures_used & (1 << i))
282 shader_addline(buffer,"TEMP T%lu;\n", i);
285 /* Texture coordinate registers must be pre-loaded */
286 for (i = 0; i < This->baseShader.limits.texture; i++) {
287 if (This->baseShader.textures_used & (1 << i))
288 shader_addline(buffer, "MOV T%lu, fragment.texcoord[%lu];\n", i, i);
291 /* Second pass, process opcodes */
292 if (NULL != pToken) {
293 while (D3DPS_END() != *pToken) {
295 /* Skip version token */
296 if (shader_is_version_token(*pToken)) {
297 ++pToken;
298 continue;
301 /* Skip comment tokens */
302 if (shader_is_comment_token(*pToken)) {
303 DWORD comment_len = (*pToken & D3DSI_COMMENTSIZE_MASK) >> D3DSI_COMMENTSIZE_SHIFT;
304 ++pToken;
305 TRACE("#%s\n", (char*)pToken);
306 pToken += comment_len;
307 continue;
310 /* Read opcode */
311 curOpcode = shader_get_opcode(iface, *pToken);
312 ++pToken;
314 /* Unknown opcode and its parameters */
315 if (NULL == curOpcode) {
316 while (*pToken & 0x80000000) { /* TODO: Think of a sensible name for 0x80000000 */
317 FIXME("unrecognized opcode: %08lx\n", *pToken);
318 ++pToken;
321 /* Unhandled opcode */
322 } else if (GLNAME_REQUIRE_GLSL == curOpcode->glname) {
324 FIXME("Token %s requires greater functionality than "
325 "Vertex or Fragment_Program_ARB supports\n", curOpcode->name);
326 pToken += curOpcode->num_params;
328 /* If a generator function is set, use it */
329 } else if (curOpcode->hw_fct != NULL) {
331 SHADER_OPCODE_ARG hw_arg;
333 hw_arg.shader = iface;
334 hw_arg.opcode = curOpcode;
335 hw_arg.buffer = buffer;
336 if (curOpcode->num_params > 0) {
337 hw_arg.dst = *pToken;
339 /* FIXME: this does not account for relative address tokens */
340 for (i = 1; i < curOpcode->num_params; i++)
341 hw_arg.src[i-1] = *(pToken + i);
344 curOpcode->hw_fct(&hw_arg);
345 pToken += curOpcode->num_params;
347 } else {
349 TRACE("Found opcode D3D:%s GL:%s, PARAMS:%d, \n",
350 curOpcode->name, curOpcode->glname, curOpcode->num_params);
352 /* Unless we encounter a no-op command, this opcode is unrecognized */
353 if (curOpcode->opcode != D3DSIO_NOP) {
354 FIXME("Can't handle opcode %s in hwShader\n", curOpcode->name);
355 pToken += curOpcode->num_params;
359 /* TODO: What about result.depth? */
365 /* TODO: Move other shared code here */