dmsynth: Dump data passed to Download method.
[wine] / dlls / d3dcompiler_43 / compiler.c
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 #define COBJMACROS
21 #include "config.h"
22 #include "wine/port.h"
23 #include "wine/debug.h"
24 #include "wine/unicode.h"
25
26 #include "d3dcompiler_private.h"
27 #include "wine/wpp.h"
28
29 WINE_DEFAULT_DEBUG_CHANNEL(d3dcompiler);
30
31 #define D3DXERR_INVALIDDATA                      0x88760b59
32
33 #define BUFFER_INITIAL_CAPACITY 256
34
35 struct mem_file_desc
36 {
37     const char *buffer;
38     unsigned int size;
39     unsigned int pos;
40 };
41
42 static struct mem_file_desc current_shader;
43 static ID3DInclude *current_include;
44 static const char *initial_filename;
45
46 #define INCLUDES_INITIAL_CAPACITY 4
47
48 struct loaded_include
49 {
50     const char *name;
51     const char *data;
52 };
53
54 static struct loaded_include *includes;
55 static int includes_capacity, includes_size;
56 static const char *parent_include;
57
58 static char *wpp_output;
59 static int wpp_output_capacity, wpp_output_size;
60
61 static char *wpp_messages;
62 static int wpp_messages_capacity, wpp_messages_size;
63
64 /* Mutex used to guarantee a single invocation
65    of the D3DXAssembleShader function (or its variants) at a time.
66    This is needed as wpp isn't thread-safe */
67 static CRITICAL_SECTION wpp_mutex;
68 static CRITICAL_SECTION_DEBUG wpp_mutex_debug =
69 {
70     0, 0, &wpp_mutex,
71     { &wpp_mutex_debug.ProcessLocksList,
72       &wpp_mutex_debug.ProcessLocksList },
73       0, 0, { (DWORD_PTR)(__FILE__ ": wpp_mutex") }
74 };
75 static CRITICAL_SECTION wpp_mutex = { &wpp_mutex_debug, -1, 0, 0, 0, 0 };
76
77 /* Preprocessor error reporting functions */
78 static void wpp_write_message(const char *fmt, va_list args)
79 {
80     char* newbuffer;
81     int rc, newsize;
82
83     if(wpp_messages_capacity == 0)
84     {
85         wpp_messages = HeapAlloc(GetProcessHeap(), 0, MESSAGEBUFFER_INITIAL_SIZE);
86         if(wpp_messages == NULL)
87         {
88             ERR("Error allocating memory for parser messages\n");
89             return;
90         }
91         wpp_messages_capacity = MESSAGEBUFFER_INITIAL_SIZE;
92     }
93
94     while(1)
95     {
96         rc = vsnprintf(wpp_messages + wpp_messages_size,
97                        wpp_messages_capacity - wpp_messages_size, fmt, args);
98
99         if (rc < 0 ||                                           /* C89 */
100             rc >= wpp_messages_capacity - wpp_messages_size) {  /* C99 */
101             /* Resize the buffer */
102             newsize = wpp_messages_capacity * 2;
103             newbuffer = HeapReAlloc(GetProcessHeap(), 0, wpp_messages, newsize);
104             if(newbuffer == NULL)
105             {
106                 ERR("Error reallocating memory for parser messages\n");
107                 return;
108             }
109             wpp_messages = newbuffer;
110             wpp_messages_capacity = newsize;
111         }
112         else
113         {
114             wpp_messages_size += rc;
115             return;
116         }
117     }
118 }
119
120 static void PRINTF_ATTR(1,2) wpp_write_message_var(const char *fmt, ...)
121 {
122     va_list args;
123
124     va_start(args, fmt);
125     wpp_write_message(fmt, args);
126     va_end(args);
127 }
128
129 static void wpp_error(const char *file, int line, int col, const char *near,
130                       const char *msg, va_list ap)
131 {
132     wpp_write_message_var("%s:%d:%d: %s: ", file ? file : "'main file'",
133                           line, col, "Error");
134     wpp_write_message(msg, ap);
135     wpp_write_message_var("\n");
136 }
137
138 static void wpp_warning(const char *file, int line, int col, const char *near,
139                         const char *msg, va_list ap)
140 {
141     wpp_write_message_var("%s:%d:%d: %s: ", file ? file : "'main file'",
142                           line, col, "Warning");
143     wpp_write_message(msg, ap);
144     wpp_write_message_var("\n");
145 }
146
147 static char *wpp_lookup_mem(const char *filename, int type, const char *parent_name,
148                             char **include_path, int include_path_count)
149 {
150     /* Here we return always ok. We will maybe fail on the next wpp_open_mem */
151     char *path;
152     int i;
153
154     parent_include = NULL;
155     if(parent_name[0] != '\0')
156     {
157         for(i = 0; i < includes_size; i++)
158         {
159             if(!strcmp(parent_name, includes[i].name))
160             {
161                 parent_include = includes[i].data;
162                 break;
163             }
164         }
165         if(parent_include == NULL)
166         {
167             ERR("Parent include file missing\n");
168             return NULL;
169         }
170     }
171
172     path = malloc(strlen(filename) + 1);
173     if(path)
174         memcpy(path, filename, strlen(filename) + 1);
175     return path;
176 }
177
178 static void *wpp_open_mem(const char *filename, int type)
179 {
180     struct mem_file_desc *desc;
181     HRESULT hr;
182
183     if(!strcmp(filename, initial_filename))
184     {
185         current_shader.pos = 0;
186         return &current_shader;
187     }
188
189     if(current_include == NULL) return NULL;
190     desc = HeapAlloc(GetProcessHeap(), 0, sizeof(*desc));
191     if(!desc)
192     {
193         ERR("Error allocating memory\n");
194         return NULL;
195     }
196     hr = ID3DInclude_Open(current_include,
197                           type ? D3D_INCLUDE_LOCAL : D3D_INCLUDE_SYSTEM,
198                           filename, parent_include, (LPCVOID *)&desc->buffer,
199                           &desc->size);
200     if(FAILED(hr))
201     {
202         HeapFree(GetProcessHeap(), 0, desc);
203         return NULL;
204     }
205
206     if(includes_capacity == includes_size)
207     {
208         if(includes_capacity == 0)
209         {
210             includes = HeapAlloc(GetProcessHeap(), 0, INCLUDES_INITIAL_CAPACITY * sizeof(*includes));
211             if(includes == NULL)
212             {
213                 ERR("Error allocating memory for the loaded includes structure\n");
214                 goto error;
215             }
216             includes_capacity = INCLUDES_INITIAL_CAPACITY * sizeof(*includes);
217         }
218         else
219         {
220             int newcapacity = includes_capacity * 2;
221             struct loaded_include *newincludes =
222                 HeapReAlloc(GetProcessHeap(), 0, includes, newcapacity);
223             if(newincludes == NULL)
224             {
225                 ERR("Error reallocating memory for the loaded includes structure\n");
226                 goto error;
227             }
228             includes = newincludes;
229             includes_capacity = newcapacity;
230         }
231     }
232     includes[includes_size].name = filename;
233     includes[includes_size++].data = desc->buffer;
234
235     desc->pos = 0;
236     return desc;
237
238 error:
239     ID3DInclude_Close(current_include, desc->buffer);
240     HeapFree(GetProcessHeap(), 0, desc);
241     return NULL;
242 }
243
244 static void wpp_close_mem(void *file)
245 {
246     struct mem_file_desc *desc = file;
247
248     if(desc != &current_shader)
249     {
250         if(current_include)
251             ID3DInclude_Close(current_include, desc->buffer);
252         else
253             ERR("current_include == NULL, desc == %p, buffer = %s\n",
254                 desc, desc->buffer);
255
256         HeapFree(GetProcessHeap(), 0, desc);
257     }
258 }
259
260 static int wpp_read_mem(void *file, char *buffer, unsigned int len)
261 {
262     struct mem_file_desc *desc = file;
263
264     len = min(len, desc->size - desc->pos);
265     memcpy(buffer, desc->buffer + desc->pos, len);
266     desc->pos += len;
267     return len;
268 }
269
270 static void wpp_write_mem(const char *buffer, unsigned int len)
271 {
272     char *new_wpp_output;
273
274     if(wpp_output_capacity == 0)
275     {
276         wpp_output = HeapAlloc(GetProcessHeap(), 0, BUFFER_INITIAL_CAPACITY);
277         if(!wpp_output)
278         {
279             ERR("Error allocating memory\n");
280             return;
281         }
282         wpp_output_capacity = BUFFER_INITIAL_CAPACITY;
283     }
284     if(len > wpp_output_capacity - wpp_output_size)
285     {
286         while(len > wpp_output_capacity - wpp_output_size)
287         {
288             wpp_output_capacity *= 2;
289         }
290         new_wpp_output = HeapReAlloc(GetProcessHeap(), 0, wpp_output,
291                                      wpp_output_capacity);
292         if(!new_wpp_output)
293         {
294             ERR("Error allocating memory\n");
295             return;
296         }
297         wpp_output = new_wpp_output;
298     }
299     memcpy(wpp_output + wpp_output_size, buffer, len);
300     wpp_output_size += len;
301 }
302
303 static int wpp_close_output(void)
304 {
305     char *new_wpp_output = HeapReAlloc(GetProcessHeap(), 0, wpp_output,
306                                        wpp_output_size + 1);
307     if(!new_wpp_output) return 0;
308     wpp_output = new_wpp_output;
309     wpp_output[wpp_output_size]='\0';
310     wpp_output_size++;
311     return 1;
312 }
313
314 static HRESULT preprocess_shader(const void *data, SIZE_T data_size, const char *filename,
315         const D3D_SHADER_MACRO *defines, ID3DInclude *include, ID3DBlob **error_messages)
316 {
317     int ret;
318     HRESULT hr = S_OK;
319     const D3D_SHADER_MACRO *def = defines;
320
321     static const struct wpp_callbacks wpp_callbacks =
322     {
323         wpp_lookup_mem,
324         wpp_open_mem,
325         wpp_close_mem,
326         wpp_read_mem,
327         wpp_write_mem,
328         wpp_error,
329         wpp_warning,
330     };
331
332     if (def != NULL)
333     {
334         while (def->Name != NULL)
335         {
336             wpp_add_define(def->Name, def->Definition);
337             def++;
338         }
339     }
340     current_include = include;
341     includes_size = 0;
342
343     wpp_output_size = wpp_output_capacity = 0;
344     wpp_output = NULL;
345
346     wpp_set_callbacks(&wpp_callbacks);
347     wpp_messages_size = wpp_messages_capacity = 0;
348     wpp_messages = NULL;
349     current_shader.buffer = data;
350     current_shader.size = data_size;
351     initial_filename = filename ? filename : "";
352
353     ret = wpp_parse(initial_filename, NULL);
354     if (!wpp_close_output())
355         ret = 1;
356     if (ret)
357     {
358         TRACE("Error during shader preprocessing\n");
359         if (wpp_messages)
360         {
361             int size;
362             ID3DBlob *buffer;
363
364             TRACE("Preprocessor messages:\n%s\n", debugstr_a(wpp_messages));
365
366             if (error_messages)
367             {
368                 size = strlen(wpp_messages) + 1;
369                 hr = D3DCreateBlob(size, &buffer);
370                 if (FAILED(hr))
371                     goto cleanup;
372                 CopyMemory(ID3D10Blob_GetBufferPointer(buffer), wpp_messages, size);
373                 *error_messages = buffer;
374             }
375         }
376         if (data)
377             TRACE("Shader source:\n%s\n", debugstr_an(data, data_size));
378         hr = E_FAIL;
379     }
380
381 cleanup:
382     /* Remove the previously added defines */
383     if (defines != NULL)
384     {
385         while (defines->Name != NULL)
386         {
387             wpp_del_define(defines->Name);
388             defines++;
389         }
390     }
391     HeapFree(GetProcessHeap(), 0, wpp_messages);
392     return hr;
393 }
394
395 static HRESULT assemble_shader(const char *preproc_shader,
396         ID3DBlob **shader_blob, ID3DBlob **error_messages)
397 {
398     struct bwriter_shader *shader;
399     char *messages = NULL;
400     HRESULT hr;
401     DWORD *res, size;
402     ID3DBlob *buffer;
403     char *pos;
404
405     shader = SlAssembleShader(preproc_shader, &messages);
406
407     if (messages)
408     {
409         TRACE("Assembler messages:\n");
410         TRACE("%s\n", debugstr_a(messages));
411
412         TRACE("Shader source:\n");
413         TRACE("%s\n", debugstr_a(preproc_shader));
414
415         if (error_messages)
416         {
417             const char *preproc_messages = *error_messages ? ID3D10Blob_GetBufferPointer(*error_messages) : NULL;
418
419             size = strlen(messages) + (preproc_messages ? strlen(preproc_messages) : 0) + 1;
420             hr = D3DCreateBlob(size, &buffer);
421             if (FAILED(hr))
422             {
423                 HeapFree(GetProcessHeap(), 0, messages);
424                 if (shader) SlDeleteShader(shader);
425                 return hr;
426             }
427             pos = ID3D10Blob_GetBufferPointer(buffer);
428             if (preproc_messages)
429             {
430                 CopyMemory(pos, preproc_messages, strlen(preproc_messages) + 1);
431                 pos += strlen(preproc_messages);
432             }
433             CopyMemory(pos, messages, strlen(messages) + 1);
434
435             if (*error_messages) ID3D10Blob_Release(*error_messages);
436             *error_messages = buffer;
437         }
438         HeapFree(GetProcessHeap(), 0, messages);
439     }
440
441     if (shader == NULL)
442     {
443         ERR("Asm reading failed\n");
444         return D3DXERR_INVALIDDATA;
445     }
446
447     hr = SlWriteBytecode(shader, 9, &res, &size);
448     SlDeleteShader(shader);
449     if (FAILED(hr))
450     {
451         ERR("SlWriteBytecode failed with 0x%08x\n", hr);
452         return D3DXERR_INVALIDDATA;
453     }
454
455     if (shader_blob)
456     {
457         hr = D3DCreateBlob(size, &buffer);
458         if (FAILED(hr))
459         {
460             HeapFree(GetProcessHeap(), 0, res);
461             return hr;
462         }
463         CopyMemory(ID3D10Blob_GetBufferPointer(buffer), res, size);
464         *shader_blob = buffer;
465     }
466
467     HeapFree(GetProcessHeap(), 0, res);
468
469     return S_OK;
470 }
471
472 HRESULT WINAPI D3DAssemble(const void *data, SIZE_T datasize, const char *filename,
473         const D3D_SHADER_MACRO *defines, ID3DInclude *include, UINT flags,
474         ID3DBlob **shader, ID3DBlob **error_messages)
475 {
476     HRESULT hr;
477
478     TRACE("data %p, datasize %lu, filename %s, defines %p, include %p, sflags %#x,\n"
479             "shader %p, error_messages %p\n",
480             data, datasize, debugstr_a(filename), defines, include, flags, shader, error_messages);
481
482     EnterCriticalSection(&wpp_mutex);
483
484     /* TODO: flags */
485     if (flags) FIXME("flags %x\n", flags);
486
487     if (shader) *shader = NULL;
488     if (error_messages) *error_messages = NULL;
489
490     hr = preprocess_shader(data, datasize, filename, defines, include, error_messages);
491     if (SUCCEEDED(hr))
492         hr = assemble_shader(wpp_output, shader, error_messages);
493
494     HeapFree(GetProcessHeap(), 0, wpp_output);
495     LeaveCriticalSection(&wpp_mutex);
496     return hr;
497 }
498
499 static HRESULT compile_shader(const char *preproc_shader, const char *target, const char *entrypoint,
500         ID3DBlob **shader_blob, ID3DBlob **error_messages)
501 {
502     struct bwriter_shader *shader;
503     char *messages = NULL;
504     HRESULT hr;
505     DWORD *res, size, major, minor;
506     ID3DBlob *buffer;
507     char *pos;
508     enum shader_type shader_type;
509
510     TRACE("Preprocessed shader source: %s\n", debugstr_a(preproc_shader));
511
512     TRACE("Parsing compilation target %s.\n", debugstr_a(target));
513     if (strlen(target) != 6 || target[1] != 's' || target[2] != '_' || target[4] != '_')
514     {
515         FIXME("Unknown compilation target %s.\n", debugstr_a(target));
516         return D3DERR_INVALIDCALL;
517     }
518
519     if (target[0] == 'v')
520         shader_type = ST_VERTEX;
521     else if (target[0] == 'p')
522         shader_type = ST_PIXEL;
523     else
524     {
525         FIXME("Unsupported shader target type %s.\n", debugstr_a(target));
526         return D3DERR_INVALIDCALL;
527     }
528
529     major = target[3] - '0';
530     if (major == 0 || major > 5)
531     {
532         FIXME("Unsupported shader target major version %d.\n", major);
533         return D3DERR_INVALIDCALL;
534     }
535     minor = target[5] - '0';
536     if (minor > 1 || (minor == 1 && (shader_type != ST_VERTEX || major > 1)))
537     {
538         FIXME("Unsupported shader target minor version %d.\n", minor);
539         return D3DERR_INVALIDCALL;
540     }
541     shader = parse_hlsl_shader(preproc_shader, shader_type, major, minor, entrypoint, &messages);
542
543     if (messages)
544     {
545         TRACE("Compiler messages:\n");
546         TRACE("%s\n", debugstr_a(messages));
547
548         TRACE("Shader source:\n");
549         TRACE("%s\n", debugstr_a(preproc_shader));
550
551         if (error_messages)
552         {
553             const char *preproc_messages = *error_messages ? ID3D10Blob_GetBufferPointer(*error_messages) : NULL;
554
555             size = strlen(messages) + (preproc_messages ? strlen(preproc_messages) : 0) + 1;
556             hr = D3DCreateBlob(size, &buffer);
557             if (FAILED(hr))
558             {
559                 HeapFree(GetProcessHeap(), 0, messages);
560                 if (shader) SlDeleteShader(shader);
561                 return hr;
562             }
563             pos = ID3D10Blob_GetBufferPointer(buffer);
564             if (preproc_messages)
565             {
566                 memcpy(pos, preproc_messages, strlen(preproc_messages) + 1);
567                 pos += strlen(preproc_messages);
568             }
569             memcpy(pos, messages, strlen(messages) + 1);
570
571             if (*error_messages) ID3D10Blob_Release(*error_messages);
572             *error_messages = buffer;
573         }
574         HeapFree(GetProcessHeap(), 0, messages);
575     }
576
577     if (!shader)
578     {
579         ERR("HLSL shader parsing failed.\n");
580         return D3DXERR_INVALIDDATA;
581     }
582
583     hr = SlWriteBytecode(shader, 9, &res, &size);
584     SlDeleteShader(shader);
585     if (FAILED(hr))
586     {
587         ERR("SlWriteBytecode failed with error 0x%08x.\n", hr);
588         return D3DXERR_INVALIDDATA;
589     }
590
591     if (shader_blob)
592     {
593         hr = D3DCreateBlob(size, &buffer);
594         if (FAILED(hr))
595         {
596             HeapFree(GetProcessHeap(), 0, res);
597             return hr;
598         }
599         memcpy(ID3D10Blob_GetBufferPointer(buffer), res, size);
600         *shader_blob = buffer;
601     }
602
603     HeapFree(GetProcessHeap(), 0, res);
604
605     return S_OK;
606 }
607
608 HRESULT WINAPI D3DCompile(const void *data, SIZE_T data_size, const char *filename,
609         const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint,
610         const char *target, UINT sflags, UINT eflags, ID3DBlob **shader, ID3DBlob **error_messages)
611 {
612     HRESULT hr;
613
614     FIXME("data %p, data_size %lu, filename %s, defines %p, include %p, entrypoint %s,\n"
615             "target %s, sflags %#x, eflags %#x, shader %p, error_messages %p stub!\n",
616             data, data_size, debugstr_a(filename), defines, include, debugstr_a(entrypoint),
617             debugstr_a(target), sflags, eflags, shader, error_messages);
618
619     if (shader) *shader = NULL;
620     if (error_messages) *error_messages = NULL;
621
622     EnterCriticalSection(&wpp_mutex);
623
624     hr = preprocess_shader(data, data_size, filename, defines, include, error_messages);
625     if (SUCCEEDED(hr))
626         hr = compile_shader(wpp_output, target, entrypoint, shader, error_messages);
627
628     HeapFree(GetProcessHeap(), 0, wpp_output);
629     LeaveCriticalSection(&wpp_mutex);
630     return hr;
631 }
632
633 HRESULT WINAPI D3DPreprocess(const void *data, SIZE_T size, const char *filename,
634         const D3D_SHADER_MACRO *defines, ID3DInclude *include,
635         ID3DBlob **shader, ID3DBlob **error_messages)
636 {
637     HRESULT hr;
638     ID3DBlob *buffer;
639
640     if (!data)
641         return E_INVALIDARG;
642
643     EnterCriticalSection(&wpp_mutex);
644
645     if (shader) *shader = NULL;
646     if (error_messages) *error_messages = NULL;
647
648     hr = preprocess_shader(data, size, filename, defines, include, error_messages);
649
650     if (SUCCEEDED(hr))
651     {
652         if (shader)
653         {
654             hr = D3DCreateBlob(wpp_output_size, &buffer);
655             if (FAILED(hr))
656                 goto cleanup;
657             CopyMemory(ID3D10Blob_GetBufferPointer(buffer), wpp_output, wpp_output_size);
658             *shader = buffer;
659         }
660         else
661             hr = E_INVALIDARG;
662     }
663
664 cleanup:
665     HeapFree(GetProcessHeap(), 0, wpp_output);
666     LeaveCriticalSection(&wpp_mutex);
667     return hr;
668 }
669
670 HRESULT WINAPI D3DDisassemble(const void *data, SIZE_T size, UINT flags, const char *comments, ID3DBlob **disassembly)
671 {
672     FIXME("data %p, size %lu, flags %#x, comments %p, disassembly %p stub!\n",
673             data, size, flags, comments, disassembly);
674     return E_NOTIMPL;
675 }