1 /*
2  * Copyright 2009 Matteo Bruni
3  * Copyright 2010 Matteo Bruni for CodeWeavers
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #include "d3dcompiler_private.h"
21 #include "wine/unicode.h"
22 #include "wine/wpp.h"
23 
24 WINE_DEFAULT_DEBUG_CHANNEL(d3dcompiler);
25 
26 #define D3DXERR_INVALIDDATA                      0x88760b59
27 
28 #define BUFFER_INITIAL_CAPACITY 256
29 
30 struct mem_file_desc
31 {
32     const char *buffer;
33     unsigned int size;
34     unsigned int pos;
35 };
36 
37 static struct mem_file_desc current_shader;
38 static ID3DInclude *current_include;
39 static const char *initial_filename;
40 
41 #define INCLUDES_INITIAL_CAPACITY 4
42 
43 struct loaded_include
44 {
45     const char *name;
46     const char *data;
47 };
48 
49 static struct loaded_include *includes;
50 static int includes_capacity, includes_size;
51 static const char *parent_include;
52 
53 static char *wpp_output;
54 static int wpp_output_capacity, wpp_output_size;
55 
56 static char *wpp_messages;
57 static int wpp_messages_capacity, wpp_messages_size;
58 
59 /* Mutex used to guarantee a single invocation
60    of the D3DXAssembleShader function (or its variants) at a time.
61    This is needed as wpp isn't thread-safe */
62 static CRITICAL_SECTION wpp_mutex;
63 static CRITICAL_SECTION_DEBUG wpp_mutex_debug =
64 {
65     0, 0, &wpp_mutex,
66     { &wpp_mutex_debug.ProcessLocksList,
67       &wpp_mutex_debug.ProcessLocksList },
68       0, 0, { (DWORD_PTR)(__FILE__ ": wpp_mutex") }
69 };
70 static CRITICAL_SECTION wpp_mutex = { &wpp_mutex_debug, -1, 0, 0, 0, 0 };
71 
72 /* Preprocessor error reporting functions */
73 static void wpp_write_message(const char *fmt, va_list args)
74 {
75     char* newbuffer;
76     int rc, newsize;
77 
78     if(wpp_messages_capacity == 0)
79     {
80         wpp_messages = HeapAlloc(GetProcessHeap(), 0, MESSAGEBUFFER_INITIAL_SIZE);
81         if(wpp_messages == NULL)
82             return;
83 
84         wpp_messages_capacity = MESSAGEBUFFER_INITIAL_SIZE;
85     }
86 
87     while(1)
88     {
89         rc = vsnprintf(wpp_messages + wpp_messages_size,
90                        wpp_messages_capacity - wpp_messages_size, fmt, args);
91 
92         if (rc < 0 ||                                           /* C89 */
93             rc >= wpp_messages_capacity - wpp_messages_size) {  /* C99 */
94             /* Resize the buffer */
95             newsize = wpp_messages_capacity * 2;
96             newbuffer = HeapReAlloc(GetProcessHeap(), 0, wpp_messages, newsize);
97             if(newbuffer == NULL)
98             {
99                 ERR("Error reallocating memory for parser messages\n");
100                 return;
101             }
102             wpp_messages = newbuffer;
103             wpp_messages_capacity = newsize;
104         }
105         else
106         {
107             wpp_messages_size += rc;
108             return;
109         }
110     }
111 }
112 
113 static void PRINTF_ATTR(1,2) wpp_write_message_var(const char *fmt, ...)
114 {
115     va_list args;
116 
117     va_start(args, fmt);
118     wpp_write_message(fmt, args);
119     va_end(args);
120 }
121 
122 static void wpp_error(const char *file, int line, int col, const char *_near,
123                       const char *msg, va_list ap)
124 {
125     wpp_write_message_var("%s:%d:%d: %s: ", file ? file : "'main file'",
126                           line, col, "Error");
127     wpp_write_message(msg, ap);
128     wpp_write_message_var("\n");
129 }
130 
131 static void wpp_warning(const char *file, int line, int col, const char *_near,
132                         const char *msg, va_list ap)
133 {
134     wpp_write_message_var("%s:%d:%d: %s: ", file ? file : "'main file'",
135                           line, col, "Warning");
136     wpp_write_message(msg, ap);
137     wpp_write_message_var("\n");
138 }
139 
140 static char *wpp_lookup_mem(const char *filename, int type, const char *parent_name,
141                             char **include_path, int include_path_count)
142 {
143     /* We don't check for file existence here. We will potentially fail on
144      * the following wpp_open_mem(). */
145     char *path;
146     int i;
147 
148     TRACE("Looking for include %s, parent %s.\n", debugstr_a(filename), debugstr_a(parent_name));
149 
150     parent_include = NULL;
151     if (strcmp(parent_name, initial_filename))
152     {
153         for(i = 0; i < includes_size; i++)
154         {
155             if(!strcmp(parent_name, includes[i].name))
156             {
157                 parent_include = includes[i].data;
158                 break;
159             }
160         }
161         if(parent_include == NULL)
162         {
163             ERR("Parent include %s missing.\n", debugstr_a(parent_name));
164             return NULL;
165         }
166     }
167 
168     path = malloc(strlen(filename) + 1);
169     if(path)
170         memcpy(path, filename, strlen(filename) + 1);
171     return path;
172 }
173 
174 static void *wpp_open_mem(const char *filename, int type)
175 {
176     struct mem_file_desc *desc;
177     HRESULT hr;
178 
179     TRACE("Opening include %s.\n", debugstr_a(filename));
180 
181     if(!strcmp(filename, initial_filename))
182     {
183         current_shader.pos = 0;
184         return &current_shader;
185     }
186 
187     if(current_include == NULL) return NULL;
188     desc = HeapAlloc(GetProcessHeap(), 0, sizeof(*desc));
189     if(!desc)
190         return NULL;
191 
192     if (FAILED(hr = ID3DInclude_Open(current_include, type ? D3D_INCLUDE_LOCAL : D3D_INCLUDE_SYSTEM,
193             filename, parent_include, (const void **)&desc->buffer, &desc->size)))
194     {
195         HeapFree(GetProcessHeap(), 0, desc);
196         return NULL;
197     }
198 
199     if(includes_capacity == includes_size)
200     {
201         if(includes_capacity == 0)
202         {
203             includes = HeapAlloc(GetProcessHeap(), 0, INCLUDES_INITIAL_CAPACITY * sizeof(*includes));
204             if(includes == NULL)
205             {
206                 ERR("Error allocating memory for the loaded includes structure\n");
207                 goto error;
208             }
209             includes_capacity = INCLUDES_INITIAL_CAPACITY * sizeof(*includes);
210         }
211         else
212         {
213             int newcapacity = includes_capacity * 2;
214             struct loaded_include *newincludes =
215                 HeapReAlloc(GetProcessHeap(), 0, includes, newcapacity);
216             if(newincludes == NULL)
217             {
218                 ERR("Error reallocating memory for the loaded includes structure\n");
219                 goto error;
220             }
221             includes = newincludes;
222             includes_capacity = newcapacity;
223         }
224     }
225     includes[includes_size].name = filename;
226     includes[includes_size++].data = desc->buffer;
227 
228     desc->pos = 0;
229     return desc;
230 
231 error:
232     ID3DInclude_Close(current_include, desc->buffer);
233     HeapFree(GetProcessHeap(), 0, desc);
234     return NULL;
235 }
236 
237 static void wpp_close_mem(void *file)
238 {
239     struct mem_file_desc *desc = file;
240 
241     if(desc != &current_shader)
242     {
243         if(current_include)
244             ID3DInclude_Close(current_include, desc->buffer);
245         else
246             ERR("current_include == NULL, desc == %p, buffer = %s\n",
247                 desc, desc->buffer);
248 
249         HeapFree(GetProcessHeap(), 0, desc);
250     }
251 }
252 
253 static int wpp_read_mem(void *file, char *buffer, unsigned int len)
254 {
255     struct mem_file_desc *desc = file;
256 
257     len = min(len, desc->size - desc->pos);
258     memcpy(buffer, desc->buffer + desc->pos, len);
259     desc->pos += len;
260     return len;
261 }
262 
263 static void wpp_write_mem(const char *buffer, unsigned int len)
264 {
265     char *new_wpp_output;
266 
267     if(wpp_output_capacity == 0)
268     {
269         wpp_output = HeapAlloc(GetProcessHeap(), 0, BUFFER_INITIAL_CAPACITY);
270         if(!wpp_output)
271             return;
272 
273         wpp_output_capacity = BUFFER_INITIAL_CAPACITY;
274     }
275     if(len > wpp_output_capacity - wpp_output_size)
276     {
277         while(len > wpp_output_capacity - wpp_output_size)
278         {
279             wpp_output_capacity *= 2;
280         }
281         new_wpp_output = HeapReAlloc(GetProcessHeap(), 0, wpp_output,
282                                      wpp_output_capacity);
283         if(!new_wpp_output)
284         {
285             ERR("Error allocating memory\n");
286             return;
287         }
288         wpp_output = new_wpp_output;
289     }
290     memcpy(wpp_output + wpp_output_size, buffer, len);
291     wpp_output_size += len;
292 }
293 
294 static int wpp_close_output(void)
295 {
296     char *new_wpp_output = HeapReAlloc(GetProcessHeap(), 0, wpp_output,
297                                        wpp_output_size + 1);
298     if(!new_wpp_output) return 0;
299     wpp_output = new_wpp_output;
300     wpp_output[wpp_output_size]='\0';
301     wpp_output_size++;
302     return 1;
303 }
304 
305 static HRESULT preprocess_shader(const void *data, SIZE_T data_size, const char *filename,
306         const D3D_SHADER_MACRO *defines, ID3DInclude *include, ID3DBlob **error_messages)
307 {
308     int ret;
309     HRESULT hr = S_OK;
310     const D3D_SHADER_MACRO *def = defines;
311 
312     static const struct wpp_callbacks wpp_callbacks =
313     {
314         wpp_lookup_mem,
315         wpp_open_mem,
316         wpp_close_mem,
317         wpp_read_mem,
318         wpp_write_mem,
319         wpp_error,
320         wpp_warning,
321     };
322 
323     if (def != NULL)
324     {
325         while (def->Name != NULL)
326         {
327             wpp_add_define(def->Name, def->Definition);
328             def++;
329         }
330     }
331     current_include = include;
332     includes_size = 0;
333 
334     wpp_output_size = wpp_output_capacity = 0;
335     wpp_output = NULL;
336 
337     wpp_set_callbacks(&wpp_callbacks);
338     wpp_messages_size = wpp_messages_capacity = 0;
339     wpp_messages = NULL;
340     current_shader.buffer = data;
341     current_shader.size = data_size;
342     initial_filename = filename ? filename : "";
343 
344     ret = wpp_parse(initial_filename, NULL);
345     if (!wpp_close_output())
346         ret = 1;
347     if (ret)
348     {
349         TRACE("Error during shader preprocessing\n");
350         if (wpp_messages)
351         {
352             int size;
353             ID3DBlob *buffer;
354 
355             TRACE("Preprocessor messages:\n%s\n", debugstr_a(wpp_messages));
356 
357             if (error_messages)
358             {
359                 size = strlen(wpp_messages) + 1;
360                 hr = D3DCreateBlob(size, &buffer);
361                 if (FAILED(hr))
362                     goto cleanup;
363                 CopyMemory(ID3D10Blob_GetBufferPointer(buffer), wpp_messages, size);
364                 *error_messages = buffer;
365             }
366         }
367         if (data)
368             TRACE("Shader source:\n%s\n", debugstr_an(data, data_size));
369         hr = E_FAIL;
370     }
371 
372 cleanup:
373     /* Remove the previously added defines */
374     if (defines != NULL)
375     {
376         while (defines->Name != NULL)
377         {
378             wpp_del_define(defines->Name);
379             defines++;
380         }
381     }
382     HeapFree(GetProcessHeap(), 0, wpp_messages);
383     return hr;
384 }
385 
386 static HRESULT assemble_shader(const char *preproc_shader,
387         ID3DBlob **shader_blob, ID3DBlob **error_messages)
388 {
389     struct bwriter_shader *shader;
390     char *messages = NULL;
391     HRESULT hr;
392     DWORD *res, size;
393     ID3DBlob *buffer;
394     char *pos;
395 
396     shader = SlAssembleShader(preproc_shader, &messages);
397 
398     if (messages)
399     {
400         TRACE("Assembler messages:\n");
401         TRACE("%s\n", debugstr_a(messages));
402 
403         TRACE("Shader source:\n");
404         TRACE("%s\n", debugstr_a(preproc_shader));
405 
406         if (error_messages)
407         {
408             const char *preproc_messages = *error_messages ? ID3D10Blob_GetBufferPointer(*error_messages) : NULL;
409 
410             size = strlen(messages) + (preproc_messages ? strlen(preproc_messages) : 0) + 1;
411             hr = D3DCreateBlob(size, &buffer);
412             if (FAILED(hr))
413             {
414                 HeapFree(GetProcessHeap(), 0, messages);
415                 if (shader) SlDeleteShader(shader);
416                 return hr;
417             }
418             pos = ID3D10Blob_GetBufferPointer(buffer);
419             if (preproc_messages)
420             {
421                 CopyMemory(pos, preproc_messages, strlen(preproc_messages) + 1);
422                 pos += strlen(preproc_messages);
423             }
424             CopyMemory(pos, messages, strlen(messages) + 1);
425 
426             if (*error_messages) ID3D10Blob_Release(*error_messages);
427             *error_messages = buffer;
428         }
429         HeapFree(GetProcessHeap(), 0, messages);
430     }
431 
432     if (shader == NULL)
433     {
434         ERR("Asm reading failed\n");
435         return D3DXERR_INVALIDDATA;
436     }
437 
438     hr = SlWriteBytecode(shader, 9, &res, &size);
439     SlDeleteShader(shader);
440     if (FAILED(hr))
441     {
442         ERR("SlWriteBytecode failed with 0x%08x\n", hr);
443         return D3DXERR_INVALIDDATA;
444     }
445 
446     if (shader_blob)
447     {
448         hr = D3DCreateBlob(size, &buffer);
449         if (FAILED(hr))
450         {
451             HeapFree(GetProcessHeap(), 0, res);
452             return hr;
453         }
454         CopyMemory(ID3D10Blob_GetBufferPointer(buffer), res, size);
455         *shader_blob = buffer;
456     }
457 
458     HeapFree(GetProcessHeap(), 0, res);
459 
460     return S_OK;
461 }
462 
463 HRESULT WINAPI D3DAssemble(const void *data, SIZE_T datasize, const char *filename,
464         const D3D_SHADER_MACRO *defines, ID3DInclude *include, UINT flags,
465         ID3DBlob **shader, ID3DBlob **error_messages)
466 {
467     HRESULT hr;
468 
469     TRACE("data %p, datasize %lu, filename %s, defines %p, include %p, sflags %#x, "
470             "shader %p, error_messages %p.\n",
471             data, datasize, debugstr_a(filename), defines, include, flags, shader, error_messages);
472 
473     EnterCriticalSection(&wpp_mutex);
474 
475     /* TODO: flags */
476     if (flags) FIXME("flags %x\n", flags);
477 
478     if (shader) *shader = NULL;
479     if (error_messages) *error_messages = NULL;
480 
481     hr = preprocess_shader(data, datasize, filename, defines, include, error_messages);
482     if (SUCCEEDED(hr))
483         hr = assemble_shader(wpp_output, shader, error_messages);
484 
485     HeapFree(GetProcessHeap(), 0, wpp_output);
486     LeaveCriticalSection(&wpp_mutex);
487     return hr;
488 }
489 
490 struct target_info {
491     const char *name;
492     enum shader_type type;
493     DWORD sm_major;
494     DWORD sm_minor;
495     DWORD level_major;
496     DWORD level_minor;
497     BOOL sw;
498     BOOL support;
499 };
500 
501 /* Must be kept sorted for binary search */
502 static const struct target_info targets_info[] = {
503     { "cs_4_0",            ST_UNKNOWN, 4, 0, 0, 0, FALSE, FALSE },
504     { "cs_4_1",            ST_UNKNOWN, 4, 1, 0, 0, FALSE, FALSE },
505     { "cs_5_0",            ST_UNKNOWN, 5, 0, 0, 0, FALSE, FALSE },
506     { "ds_5_0",            ST_UNKNOWN, 5, 0, 0, 0, FALSE, FALSE },
507     { "fx_2_0",            ST_UNKNOWN, 2, 0, 0, 0, FALSE, FALSE },
508     { "fx_4_0",            ST_UNKNOWN, 4, 0, 0, 0, FALSE, FALSE },
509     { "fx_4_1",            ST_UNKNOWN, 4, 1, 0, 0, FALSE, FALSE },
510     { "fx_5_0",            ST_UNKNOWN, 5, 0, 0, 0, FALSE, FALSE },
511     { "gs_4_0",            ST_UNKNOWN, 4, 0, 0, 0, FALSE, FALSE },
512     { "gs_4_1",            ST_UNKNOWN, 4, 1, 0, 0, FALSE, FALSE },
513     { "gs_5_0",            ST_UNKNOWN, 5, 0, 0, 0, FALSE, FALSE },
514     { "hs_5_0",            ST_UNKNOWN, 5, 0, 0, 0, FALSE, FALSE },
515     { "ps.1.0",            ST_PIXEL,   1, 0, 0, 0, FALSE, TRUE  },
516     { "ps.1.1",            ST_PIXEL,   1, 1, 0, 0, FALSE, FALSE },
517     { "ps.1.2",            ST_PIXEL,   1, 2, 0, 0, FALSE, FALSE },
518     { "ps.1.3",            ST_PIXEL,   1, 3, 0, 0, FALSE, FALSE },
519     { "ps.1.4",            ST_PIXEL,   1, 4, 0, 0, FALSE, FALSE },
520     { "ps.2.0",            ST_PIXEL,   2, 0, 0, 0, FALSE, TRUE  },
521     { "ps.2.a",            ST_PIXEL,   2, 1, 0, 0, FALSE, FALSE },
522     { "ps.2.b",            ST_PIXEL,   2, 2, 0, 0, FALSE, FALSE },
523     { "ps.2.sw",           ST_PIXEL,   2, 0, 0, 0, TRUE,  FALSE },
524     { "ps.3.0",            ST_PIXEL,   3, 0, 0, 0, FALSE, TRUE  },
525     { "ps_1_0",            ST_PIXEL,   1, 0, 0, 0, FALSE, TRUE  },
526     { "ps_1_1",            ST_PIXEL,   1, 1, 0, 0, FALSE, FALSE },
527     { "ps_1_2",            ST_PIXEL,   1, 2, 0, 0, FALSE, FALSE },
528     { "ps_1_3",            ST_PIXEL,   1, 3, 0, 0, FALSE, FALSE },
529     { "ps_1_4",            ST_PIXEL,   1, 4, 0, 0, FALSE, FALSE },
530     { "ps_2_0",            ST_PIXEL,   2, 0, 0, 0, FALSE, TRUE  },
531     { "ps_2_a",            ST_PIXEL,   2, 1, 0, 0, FALSE, FALSE },
532     { "ps_2_b",            ST_PIXEL,   2, 2, 0, 0, FALSE, FALSE },
533     { "ps_2_sw",           ST_PIXEL,   2, 0, 0, 0, TRUE,  FALSE },
534     { "ps_3_0",            ST_PIXEL,   3, 0, 0, 0, FALSE, TRUE  },
535     { "ps_3_sw",           ST_PIXEL,   3, 0, 0, 0, TRUE,  FALSE },
536     { "ps_4_0",            ST_PIXEL,   4, 0, 0, 0, FALSE, TRUE  },
537     { "ps_4_0_level_9_0",  ST_PIXEL,   4, 0, 9, 0, FALSE, FALSE },
538     { "ps_4_0_level_9_1",  ST_PIXEL,   4, 0, 9, 1, FALSE, FALSE },
539     { "ps_4_0_level_9_3",  ST_PIXEL,   4, 0, 9, 3, FALSE, FALSE },
540     { "ps_4_1",            ST_PIXEL,   4, 1, 0, 0, FALSE, TRUE  },
541     { "ps_5_0",            ST_PIXEL,   5, 0, 0, 0, FALSE, TRUE  },
542     { "tx_1_0",            ST_UNKNOWN, 1, 0, 0, 0, FALSE, FALSE },
543     { "vs.1.0",            ST_VERTEX,  1, 0, 0, 0, FALSE, TRUE  },
544     { "vs.1.1",            ST_VERTEX,  1, 1, 0, 0, FALSE, TRUE  },
545     { "vs.2.0",            ST_VERTEX,  2, 0, 0, 0, FALSE, TRUE  },
546     { "vs.2.a",            ST_VERTEX,  2, 1, 0, 0, FALSE, FALSE },
547     { "vs.2.sw",           ST_VERTEX,  2, 0, 0, 0, TRUE,  FALSE },
548     { "vs.3.0",            ST_VERTEX,  3, 0, 0, 0, FALSE, TRUE  },
549     { "vs.3.sw",           ST_VERTEX,  3, 0, 0, 0, TRUE,  FALSE },
550     { "vs_1_0",            ST_VERTEX,  1, 0, 0, 0, FALSE, TRUE  },
551     { "vs_1_1",            ST_VERTEX,  1, 1, 0, 0, FALSE, TRUE  },
552     { "vs_2_0",            ST_VERTEX,  2, 0, 0, 0, FALSE, TRUE  },
553     { "vs_2_a",            ST_VERTEX,  2, 1, 0, 0, FALSE, FALSE },
554     { "vs_2_sw",           ST_VERTEX,  2, 0, 0, 0, TRUE,  FALSE },
555     { "vs_3_0",            ST_VERTEX,  3, 0, 0, 0, FALSE, TRUE  },
556     { "vs_3_sw",           ST_VERTEX,  3, 0, 0, 0, TRUE,  FALSE },
557     { "vs_4_0",            ST_VERTEX,  4, 0, 0, 0, FALSE, TRUE  },
558     { "vs_4_0_level_9_0",  ST_VERTEX,  4, 0, 9, 0, FALSE, FALSE },
559     { "vs_4_0_level_9_1",  ST_VERTEX,  4, 0, 9, 1, FALSE, FALSE },
560     { "vs_4_0_level_9_3",  ST_VERTEX,  4, 0, 9, 3, FALSE, FALSE },
561     { "vs_4_1",            ST_VERTEX,  4, 1, 0, 0, FALSE, TRUE  },
562     { "vs_5_0",            ST_VERTEX,  5, 0, 0, 0, FALSE, TRUE  },
563 };
564 
565 static const struct target_info * get_target_info(const char *target)
566 {
567     LONG min = 0;
568     LONG max = sizeof(targets_info) / sizeof(targets_info[0]) - 1;
569     LONG cur;
570     int res;
571 
572     while (min <= max)
573     {
574         cur = (min + max) / 2;
575         res = strcmp(target, targets_info[cur].name);
576         if (res < 0)
577             max = cur - 1;
578         else if (res > 0)
579             min = cur + 1;
580         else
581             return &targets_info[cur];
582     }
583 
584     return NULL;
585 }
586 
587 static HRESULT compile_shader(const char *preproc_shader, const char *target, const char *entrypoint,
588         ID3DBlob **shader_blob, ID3DBlob **error_messages)
589 {
590     struct bwriter_shader *shader;
591     char *messages = NULL;
592     HRESULT hr;
593     DWORD *res, size, major, minor;
594     ID3DBlob *buffer;
595     char *pos;
596     enum shader_type shader_type;
597     const struct target_info *info;
598 
599     TRACE("Preprocessed shader source: %s\n", debugstr_a(preproc_shader));
600 
601     TRACE("Checking compilation target %s\n", debugstr_a(target));
602     info = get_target_info(target);
603     if (!info)
604     {
605         FIXME("Unknown compilation target %s\n", debugstr_a(target));
606         return D3DERR_INVALIDCALL;
607     }
608     else
609     {
610         if (!info->support)
611         {
612             FIXME("Compilation target %s not yet supported\n", debugstr_a(target));
613             return D3DERR_INVALIDCALL;
614         }
615         else
616         {
617             shader_type = info->type;
618             major = info->sm_major;
619             minor = info->sm_minor;
620         }
621     }
622 
623     shader = parse_hlsl_shader(preproc_shader, shader_type, major, minor, entrypoint, &messages);
624 
625     if (messages)
626     {
627         TRACE("Compiler messages:\n");
628         TRACE("%s\n", debugstr_a(messages));
629 
630         TRACE("Shader source:\n");
631         TRACE("%s\n", debugstr_a(preproc_shader));
632 
633         if (error_messages)
634         {
635             const char *preproc_messages = *error_messages ? ID3D10Blob_GetBufferPointer(*error_messages) : NULL;
636 
637             size = strlen(messages) + (preproc_messages ? strlen(preproc_messages) : 0) + 1;
638             hr = D3DCreateBlob(size, &buffer);
639             if (FAILED(hr))
640             {
641                 HeapFree(GetProcessHeap(), 0, messages);
642                 if (shader) SlDeleteShader(shader);
643                 return hr;
644             }
645             pos = ID3D10Blob_GetBufferPointer(buffer);
646             if (preproc_messages)
647             {
648                 memcpy(pos, preproc_messages, strlen(preproc_messages) + 1);
649                 pos += strlen(preproc_messages);
650             }
651             memcpy(pos, messages, strlen(messages) + 1);
652 
653             if (*error_messages) ID3D10Blob_Release(*error_messages);
654             *error_messages = buffer;
655         }
656         HeapFree(GetProcessHeap(), 0, messages);
657     }
658 
659     if (!shader)
660     {
661         ERR("HLSL shader parsing failed.\n");
662         return D3DXERR_INVALIDDATA;
663     }
664 
665     hr = SlWriteBytecode(shader, 9, &res, &size);
666     SlDeleteShader(shader);
667     if (FAILED(hr))
668     {
669         ERR("SlWriteBytecode failed with error 0x%08x.\n", hr);
670         return D3DXERR_INVALIDDATA;
671     }
672 
673     if (shader_blob)
674     {
675         hr = D3DCreateBlob(size, &buffer);
676         if (FAILED(hr))
677         {
678             HeapFree(GetProcessHeap(), 0, res);
679             return hr;
680         }
681         memcpy(ID3D10Blob_GetBufferPointer(buffer), res, size);
682         *shader_blob = buffer;
683     }
684 
685     HeapFree(GetProcessHeap(), 0, res);
686 
687     return S_OK;
688 }
689 
690 HRESULT WINAPI D3DCompile2(const void *data, SIZE_T data_size, const char *filename,
691         const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint,
692         const char *target, UINT sflags, UINT eflags, UINT secondary_flags,
693         const void *secondary_data, SIZE_T secondary_data_size, ID3DBlob **shader,
694         ID3DBlob **error_messages)
695 {
696     HRESULT hr;
697 
698     TRACE("data %p, data_size %lu, filename %s, defines %p, include %p, entrypoint %s, "
699             "target %s, sflags %#x, eflags %#x, secondary_flags %#x, secondary_data %p, "
700             "secondary_data_size %lu, shader %p, error_messages %p.\n",
701             data, data_size, debugstr_a(filename), defines, include, debugstr_a(entrypoint),
702             debugstr_a(target), sflags, eflags, secondary_flags, secondary_data,
703             secondary_data_size, shader, error_messages);
704 
705     if (secondary_data)
706         FIXME("secondary data not implemented yet\n");
707 
708     if (shader) *shader = NULL;
709     if (error_messages) *error_messages = NULL;
710 
711     EnterCriticalSection(&wpp_mutex);
712 
713     hr = preprocess_shader(data, data_size, filename, defines, include, error_messages);
714     if (SUCCEEDED(hr))
715         hr = compile_shader(wpp_output, target, entrypoint, shader, error_messages);
716 
717     HeapFree(GetProcessHeap(), 0, wpp_output);
718     LeaveCriticalSection(&wpp_mutex);
719     return hr;
720 }
721 
722 HRESULT WINAPI D3DCompile(const void *data, SIZE_T data_size, const char *filename,
723         const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint,
724         const char *target, UINT sflags, UINT eflags, ID3DBlob **shader, ID3DBlob **error_messages)
725 {
726     TRACE("data %p, data_size %lu, filename %s, defines %p, include %p, entrypoint %s, "
727             "target %s, sflags %#x, eflags %#x, shader %p, error_messages %p.\n",
728             data, data_size, debugstr_a(filename), defines, include, debugstr_a(entrypoint),
729             debugstr_a(target), sflags, eflags, shader, error_messages);
730 
731     return D3DCompile2(data, data_size, filename, defines, include, entrypoint, target, sflags,
732             eflags, 0, NULL, 0, shader, error_messages);
733 }
734 
735 HRESULT WINAPI D3DPreprocess(const void *data, SIZE_T size, const char *filename,
736         const D3D_SHADER_MACRO *defines, ID3DInclude *include,
737         ID3DBlob **shader, ID3DBlob **error_messages)
738 {
739     HRESULT hr;
740     ID3DBlob *buffer;
741 
742     TRACE("data %p, size %lu, filename %s, defines %p, include %p, shader %p, error_messages %p\n",
743           data, size, debugstr_a(filename), defines, include, shader, error_messages);
744 
745     if (!data)
746         return E_INVALIDARG;
747 
748     EnterCriticalSection(&wpp_mutex);
749 
750     if (shader) *shader = NULL;
751     if (error_messages) *error_messages = NULL;
752 
753     hr = preprocess_shader(data, size, filename, defines, include, error_messages);
754 
755     if (SUCCEEDED(hr))
756     {
757         if (shader)
758         {
759             hr = D3DCreateBlob(wpp_output_size, &buffer);
760             if (FAILED(hr))
761                 goto cleanup;
762             CopyMemory(ID3D10Blob_GetBufferPointer(buffer), wpp_output, wpp_output_size);
763             *shader = buffer;
764         }
765         else
766             hr = E_INVALIDARG;
767     }
768 
769 cleanup:
770     HeapFree(GetProcessHeap(), 0, wpp_output);
771     LeaveCriticalSection(&wpp_mutex);
772     return hr;
773 }
774 
775 HRESULT WINAPI D3DDisassemble(const void *data, SIZE_T size, UINT flags, const char *comments, ID3DBlob **disassembly)
776 {
777     FIXME("data %p, size %lu, flags %#x, comments %p, disassembly %p stub!\n",
778             data, size, flags, comments, disassembly);
779     return E_NOTIMPL;
780 }
781 
782 HRESULT WINAPI D3DCompileFromFile(const WCHAR *filename, const D3D_SHADER_MACRO *defines, ID3DInclude *includes,
783         const char *entrypoint, const char *target, UINT flags1, UINT flags2, ID3DBlob **code, ID3DBlob **errors)
784 {
785     FIXME("filename %s, defines %p, includes %p, entrypoint %s, target %s, flags1 %x, flags2 %x, code %p, errors %p\n",
786             debugstr_w(filename), defines, includes, debugstr_a(entrypoint), debugstr_a(target), flags1, flags2, code, errors);
787 
788     return E_NOTIMPL;
789 }
790