compiler.c 23.7 KB
Newer Older
1
/*
2
 * Copyright 2009 Matteo Bruni
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * Copyright 2010 Matteo Bruni 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
 */

20
#define COBJMACROS
21 22 23 24 25 26
#include "config.h"
#include "wine/port.h"
#include "wine/debug.h"
#include "wine/unicode.h"

#include "d3dcompiler_private.h"
27
#include "wine/wpp.h"
28 29 30

WINE_DEFAULT_DEBUG_CHANNEL(d3dcompiler);

31 32 33 34 35 36 37 38 39 40 41
#define D3DXERR_INVALIDDATA                      0x88760b59

#define BUFFER_INITIAL_CAPACITY 256

struct mem_file_desc
{
    const char *buffer;
    unsigned int size;
    unsigned int pos;
};

42 43
static struct mem_file_desc current_shader;
static ID3DInclude *current_include;
44
static const char *initial_filename;
45 46 47 48 49 50 51 52 53

#define INCLUDES_INITIAL_CAPACITY 4

struct loaded_include
{
    const char *name;
    const char *data;
};

54 55 56
static struct loaded_include *includes;
static int includes_capacity, includes_size;
static const char *parent_include;
57

58 59
static char *wpp_output;
static int wpp_output_capacity, wpp_output_size;
60

61 62
static char *wpp_messages;
static int wpp_messages_capacity, wpp_messages_size;
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

/* Mutex used to guarantee a single invocation
   of the D3DXAssembleShader function (or its variants) at a time.
   This is needed as wpp isn't thread-safe */
static CRITICAL_SECTION wpp_mutex;
static CRITICAL_SECTION_DEBUG wpp_mutex_debug =
{
    0, 0, &wpp_mutex,
    { &wpp_mutex_debug.ProcessLocksList,
      &wpp_mutex_debug.ProcessLocksList },
      0, 0, { (DWORD_PTR)(__FILE__ ": wpp_mutex") }
};
static CRITICAL_SECTION wpp_mutex = { &wpp_mutex_debug, -1, 0, 0, 0, 0 };

/* Preprocessor error reporting functions */
static void wpp_write_message(const char *fmt, va_list args)
{
    char* newbuffer;
    int rc, newsize;

    if(wpp_messages_capacity == 0)
    {
        wpp_messages = HeapAlloc(GetProcessHeap(), 0, MESSAGEBUFFER_INITIAL_SIZE);
        if(wpp_messages == NULL)
            return;
88

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
        wpp_messages_capacity = MESSAGEBUFFER_INITIAL_SIZE;
    }

    while(1)
    {
        rc = vsnprintf(wpp_messages + wpp_messages_size,
                       wpp_messages_capacity - wpp_messages_size, fmt, args);

        if (rc < 0 ||                                           /* C89 */
            rc >= wpp_messages_capacity - wpp_messages_size) {  /* C99 */
            /* Resize the buffer */
            newsize = wpp_messages_capacity * 2;
            newbuffer = HeapReAlloc(GetProcessHeap(), 0, wpp_messages, newsize);
            if(newbuffer == NULL)
            {
                ERR("Error reallocating memory for parser messages\n");
                return;
            }
            wpp_messages = newbuffer;
            wpp_messages_capacity = newsize;
        }
        else
        {
            wpp_messages_size += rc;
            return;
        }
    }
}

static void PRINTF_ATTR(1,2) wpp_write_message_var(const char *fmt, ...)
{
    va_list args;

    va_start(args, fmt);
    wpp_write_message(fmt, args);
    va_end(args);
}

static void wpp_error(const char *file, int line, int col, const char *near,
                      const char *msg, va_list ap)
{
    wpp_write_message_var("%s:%d:%d: %s: ", file ? file : "'main file'",
                          line, col, "Error");
    wpp_write_message(msg, ap);
    wpp_write_message_var("\n");
}

static void wpp_warning(const char *file, int line, int col, const char *near,
                        const char *msg, va_list ap)
{
    wpp_write_message_var("%s:%d:%d: %s: ", file ? file : "'main file'",
                          line, col, "Warning");
    wpp_write_message(msg, ap);
    wpp_write_message_var("\n");
}

145
static char *wpp_lookup_mem(const char *filename, int type, const char *parent_name,
146 147 148 149 150 151
                            char **include_path, int include_path_count)
{
    /* Here we return always ok. We will maybe fail on the next wpp_open_mem */
    char *path;
    int i;

152 153
    TRACE("Looking for include %s.\n", debugstr_a(filename));

154
    parent_include = NULL;
155
    if (strcmp(parent_name, initial_filename))
156 157 158 159 160 161 162 163 164 165 166
    {
        for(i = 0; i < includes_size; i++)
        {
            if(!strcmp(parent_name, includes[i].name))
            {
                parent_include = includes[i].data;
                break;
            }
        }
        if(parent_include == NULL)
        {
167
            ERR("Parent include %s missing.\n", debugstr_a(parent_name));
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
            return NULL;
        }
    }

    path = malloc(strlen(filename) + 1);
    if(path)
        memcpy(path, filename, strlen(filename) + 1);
    return path;
}

static void *wpp_open_mem(const char *filename, int type)
{
    struct mem_file_desc *desc;
    HRESULT hr;

183 184
    TRACE("Opening include %s.\n", debugstr_a(filename));

185
    if(!strcmp(filename, initial_filename))
186 187 188 189 190 191 192 193 194
    {
        current_shader.pos = 0;
        return &current_shader;
    }

    if(current_include == NULL) return NULL;
    desc = HeapAlloc(GetProcessHeap(), 0, sizeof(*desc));
    if(!desc)
        return NULL;
195

196 197
    if (FAILED(hr = ID3DInclude_Open(current_include, type ? D3D_INCLUDE_LOCAL : D3D_INCLUDE_SYSTEM,
            filename, parent_include, (const void **)&desc->buffer, &desc->size)))
198 199 200 201 202 203 204 205 206
    {
        HeapFree(GetProcessHeap(), 0, desc);
        return NULL;
    }

    if(includes_capacity == includes_size)
    {
        if(includes_capacity == 0)
        {
207
            includes = HeapAlloc(GetProcessHeap(), 0, INCLUDES_INITIAL_CAPACITY * sizeof(*includes));
208 209 210 211 212
            if(includes == NULL)
            {
                ERR("Error allocating memory for the loaded includes structure\n");
                goto error;
            }
213
            includes_capacity = INCLUDES_INITIAL_CAPACITY * sizeof(*includes);
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
        }
        else
        {
            int newcapacity = includes_capacity * 2;
            struct loaded_include *newincludes =
                HeapReAlloc(GetProcessHeap(), 0, includes, newcapacity);
            if(newincludes == NULL)
            {
                ERR("Error reallocating memory for the loaded includes structure\n");
                goto error;
            }
            includes = newincludes;
            includes_capacity = newcapacity;
        }
    }
    includes[includes_size].name = filename;
    includes[includes_size++].data = desc->buffer;

    desc->pos = 0;
    return desc;

error:
    ID3DInclude_Close(current_include, desc->buffer);
    HeapFree(GetProcessHeap(), 0, desc);
    return NULL;
}

static void wpp_close_mem(void *file)
{
    struct mem_file_desc *desc = file;

    if(desc != &current_shader)
    {
        if(current_include)
            ID3DInclude_Close(current_include, desc->buffer);
        else
            ERR("current_include == NULL, desc == %p, buffer = %s\n",
                desc, desc->buffer);

        HeapFree(GetProcessHeap(), 0, desc);
    }
}

static int wpp_read_mem(void *file, char *buffer, unsigned int len)
{
    struct mem_file_desc *desc = file;

    len = min(len, desc->size - desc->pos);
    memcpy(buffer, desc->buffer + desc->pos, len);
    desc->pos += len;
    return len;
}

static void wpp_write_mem(const char *buffer, unsigned int len)
{
    char *new_wpp_output;

    if(wpp_output_capacity == 0)
    {
        wpp_output = HeapAlloc(GetProcessHeap(), 0, BUFFER_INITIAL_CAPACITY);
        if(!wpp_output)
            return;
276

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
        wpp_output_capacity = BUFFER_INITIAL_CAPACITY;
    }
    if(len > wpp_output_capacity - wpp_output_size)
    {
        while(len > wpp_output_capacity - wpp_output_size)
        {
            wpp_output_capacity *= 2;
        }
        new_wpp_output = HeapReAlloc(GetProcessHeap(), 0, wpp_output,
                                     wpp_output_capacity);
        if(!new_wpp_output)
        {
            ERR("Error allocating memory\n");
            return;
        }
        wpp_output = new_wpp_output;
    }
    memcpy(wpp_output + wpp_output_size, buffer, len);
    wpp_output_size += len;
}

static int wpp_close_output(void)
{
    char *new_wpp_output = HeapReAlloc(GetProcessHeap(), 0, wpp_output,
                                       wpp_output_size + 1);
    if(!new_wpp_output) return 0;
    wpp_output = new_wpp_output;
    wpp_output[wpp_output_size]='\0';
305
    wpp_output_size++;
306 307 308
    return 1;
}

309
static HRESULT preprocess_shader(const void *data, SIZE_T data_size, const char *filename,
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
        const D3D_SHADER_MACRO *defines, ID3DInclude *include, ID3DBlob **error_messages)
{
    int ret;
    HRESULT hr = S_OK;
    const D3D_SHADER_MACRO *def = defines;

    static const struct wpp_callbacks wpp_callbacks =
    {
        wpp_lookup_mem,
        wpp_open_mem,
        wpp_close_mem,
        wpp_read_mem,
        wpp_write_mem,
        wpp_error,
        wpp_warning,
    };

    if (def != NULL)
    {
        while (def->Name != NULL)
        {
            wpp_add_define(def->Name, def->Definition);
            def++;
        }
    }
    current_include = include;
    includes_size = 0;

    wpp_output_size = wpp_output_capacity = 0;
    wpp_output = NULL;

    wpp_set_callbacks(&wpp_callbacks);
    wpp_messages_size = wpp_messages_capacity = 0;
    wpp_messages = NULL;
    current_shader.buffer = data;
    current_shader.size = data_size;
346
    initial_filename = filename ? filename : "";
347

348
    ret = wpp_parse(initial_filename, NULL);
349 350 351 352 353 354 355 356 357 358
    if (!wpp_close_output())
        ret = 1;
    if (ret)
    {
        TRACE("Error during shader preprocessing\n");
        if (wpp_messages)
        {
            int size;
            ID3DBlob *buffer;

359
            TRACE("Preprocessor messages:\n%s\n", debugstr_a(wpp_messages));
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389

            if (error_messages)
            {
                size = strlen(wpp_messages) + 1;
                hr = D3DCreateBlob(size, &buffer);
                if (FAILED(hr))
                    goto cleanup;
                CopyMemory(ID3D10Blob_GetBufferPointer(buffer), wpp_messages, size);
                *error_messages = buffer;
            }
        }
        if (data)
            TRACE("Shader source:\n%s\n", debugstr_an(data, data_size));
        hr = E_FAIL;
    }

cleanup:
    /* Remove the previously added defines */
    if (defines != NULL)
    {
        while (defines->Name != NULL)
        {
            wpp_del_define(defines->Name);
            defines++;
        }
    }
    HeapFree(GetProcessHeap(), 0, wpp_messages);
    return hr;
}

390
static HRESULT assemble_shader(const char *preproc_shader,
391
        ID3DBlob **shader_blob, ID3DBlob **error_messages)
392 393 394 395
{
    struct bwriter_shader *shader;
    char *messages = NULL;
    HRESULT hr;
396 397
    DWORD *res, size;
    ID3DBlob *buffer;
398 399
    char *pos;

400
    shader = SlAssembleShader(preproc_shader, &messages);
401

402
    if (messages)
403
    {
404
        TRACE("Assembler messages:\n");
405
        TRACE("%s\n", debugstr_a(messages));
406 407

        TRACE("Shader source:\n");
408
        TRACE("%s\n", debugstr_a(preproc_shader));
409

410
        if (error_messages)
411
        {
412 413 414
            const char *preproc_messages = *error_messages ? ID3D10Blob_GetBufferPointer(*error_messages) : NULL;

            size = strlen(messages) + (preproc_messages ? strlen(preproc_messages) : 0) + 1;
415
            hr = D3DCreateBlob(size, &buffer);
416
            if (FAILED(hr))
417 418
            {
                HeapFree(GetProcessHeap(), 0, messages);
419
                if (shader) SlDeleteShader(shader);
420 421 422
                return hr;
            }
            pos = ID3D10Blob_GetBufferPointer(buffer);
423
            if (preproc_messages)
424
            {
425 426
                CopyMemory(pos, preproc_messages, strlen(preproc_messages) + 1);
                pos += strlen(preproc_messages);
427
            }
428
            CopyMemory(pos, messages, strlen(messages) + 1);
429

430
            if (*error_messages) ID3D10Blob_Release(*error_messages);
431
            *error_messages = buffer;
432 433 434 435
        }
        HeapFree(GetProcessHeap(), 0, messages);
    }

436
    if (shader == NULL)
437 438 439 440 441
    {
        ERR("Asm reading failed\n");
        return D3DXERR_INVALIDDATA;
    }

442
    hr = SlWriteBytecode(shader, 9, &res, &size);
443
    SlDeleteShader(shader);
444
    if (FAILED(hr))
445 446 447 448 449
    {
        ERR("SlWriteBytecode failed with 0x%08x\n", hr);
        return D3DXERR_INVALIDDATA;
    }

450
    if (shader_blob)
451 452
    {
        hr = D3DCreateBlob(size, &buffer);
453
        if (FAILED(hr))
454 455 456 457 458
        {
            HeapFree(GetProcessHeap(), 0, res);
            return hr;
        }
        CopyMemory(ID3D10Blob_GetBufferPointer(buffer), res, size);
459
        *shader_blob = buffer;
460 461 462 463 464 465 466
    }

    HeapFree(GetProcessHeap(), 0, res);

    return S_OK;
}

467 468 469 470
HRESULT WINAPI D3DAssemble(const void *data, SIZE_T datasize, const char *filename,
        const D3D_SHADER_MACRO *defines, ID3DInclude *include, UINT flags,
        ID3DBlob **shader, ID3DBlob **error_messages)
{
471 472
    HRESULT hr;

473 474 475 476
    TRACE("data %p, datasize %lu, filename %s, defines %p, include %p, sflags %#x,\n"
            "shader %p, error_messages %p\n",
            data, datasize, debugstr_a(filename), defines, include, flags, shader, error_messages);

477 478 479
    EnterCriticalSection(&wpp_mutex);

    /* TODO: flags */
480
    if (flags) FIXME("flags %x\n", flags);
481

482 483
    if (shader) *shader = NULL;
    if (error_messages) *error_messages = NULL;
484

485
    hr = preprocess_shader(data, datasize, filename, defines, include, error_messages);
486
    if (SUCCEEDED(hr))
487
        hr = assemble_shader(wpp_output, shader, error_messages);
488 489 490 491

    HeapFree(GetProcessHeap(), 0, wpp_output);
    LeaveCriticalSection(&wpp_mutex);
    return hr;
492
}
493

494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
struct target_info {
    const char *name;
    enum shader_type type;
    DWORD sm_major;
    DWORD sm_minor;
    DWORD level_major;
    DWORD level_minor;
    BOOL sw;
    BOOL support;
};

/* Must be kept sorted for binary search */
static const struct target_info targets_info[] = {
    { "cs_4_0",            ST_UNKNOWN, 4, 0, 0, 0, FALSE, FALSE },
    { "cs_4_1",            ST_UNKNOWN, 4, 1, 0, 0, FALSE, FALSE },
    { "cs_5_0",            ST_UNKNOWN, 5, 0, 0, 0, FALSE, FALSE },
    { "ds_5_0",            ST_UNKNOWN, 5, 0, 0, 0, FALSE, FALSE },
    { "fx_2_0",            ST_UNKNOWN, 2, 0, 0, 0, FALSE, FALSE },
    { "fx_4_0",            ST_UNKNOWN, 4, 0, 0, 0, FALSE, FALSE },
    { "fx_4_1",            ST_UNKNOWN, 4, 1, 0, 0, FALSE, FALSE },
    { "fx_5_0",            ST_UNKNOWN, 5, 0, 0, 0, FALSE, FALSE },
    { "gs_4_0",            ST_UNKNOWN, 4, 0, 0, 0, FALSE, FALSE },
    { "gs_4_1",            ST_UNKNOWN, 4, 1, 0, 0, FALSE, FALSE },
    { "gs_5_0",            ST_UNKNOWN, 5, 0, 0, 0, FALSE, FALSE },
    { "hs_5_0",            ST_UNKNOWN, 5, 0, 0, 0, FALSE, FALSE },
    { "ps.1.0",            ST_PIXEL,   1, 0, 0, 0, FALSE, TRUE  },
    { "ps.1.1",            ST_PIXEL,   1, 1, 0, 0, FALSE, FALSE },
    { "ps.1.2",            ST_PIXEL,   1, 2, 0, 0, FALSE, FALSE },
    { "ps.1.3",            ST_PIXEL,   1, 3, 0, 0, FALSE, FALSE },
    { "ps.1.4",            ST_PIXEL,   1, 4, 0, 0, FALSE, FALSE },
    { "ps.2.0",            ST_PIXEL,   2, 0, 0, 0, FALSE, TRUE  },
    { "ps.2.a",            ST_PIXEL,   2, 1, 0, 0, FALSE, FALSE },
    { "ps.2.b",            ST_PIXEL,   2, 2, 0, 0, FALSE, FALSE },
    { "ps.2.sw",           ST_PIXEL,   2, 0, 0, 0, TRUE,  FALSE },
    { "ps.3.0",            ST_PIXEL,   3, 0, 0, 0, FALSE, TRUE  },
    { "ps_1_0",            ST_PIXEL,   1, 0, 0, 0, FALSE, TRUE  },
    { "ps_1_1",            ST_PIXEL,   1, 1, 0, 0, FALSE, FALSE },
    { "ps_1_2",            ST_PIXEL,   1, 2, 0, 0, FALSE, FALSE },
    { "ps_1_3",            ST_PIXEL,   1, 3, 0, 0, FALSE, FALSE },
    { "ps_1_4",            ST_PIXEL,   1, 4, 0, 0, FALSE, FALSE },
    { "ps_2_0",            ST_PIXEL,   2, 0, 0, 0, FALSE, TRUE  },
    { "ps_2_a",            ST_PIXEL,   2, 1, 0, 0, FALSE, FALSE },
    { "ps_2_b",            ST_PIXEL,   2, 2, 0, 0, FALSE, FALSE },
    { "ps_2_sw",           ST_PIXEL,   2, 0, 0, 0, TRUE,  FALSE },
    { "ps_3_0",            ST_PIXEL,   3, 0, 0, 0, FALSE, TRUE  },
    { "ps_3_sw",           ST_PIXEL,   3, 0, 0, 0, TRUE,  FALSE },
    { "ps_4_0",            ST_PIXEL,   4, 0, 0, 0, FALSE, TRUE  },
    { "ps_4_0_level_9_0",  ST_PIXEL,   4, 0, 9, 0, FALSE, FALSE },
    { "ps_4_0_level_9_1",  ST_PIXEL,   4, 0, 9, 1, FALSE, FALSE },
    { "ps_4_0_level_9_3",  ST_PIXEL,   4, 0, 9, 3, FALSE, FALSE },
    { "ps_4_1",            ST_PIXEL,   4, 1, 0, 0, FALSE, TRUE  },
    { "ps_5_0",            ST_PIXEL,   5, 0, 0, 0, FALSE, TRUE  },
    { "tx_1_0",            ST_UNKNOWN, 1, 0, 0, 0, FALSE, FALSE },
    { "vs.1.0",            ST_VERTEX,  1, 0, 0, 0, FALSE, TRUE  },
    { "vs.1.1",            ST_VERTEX,  1, 1, 0, 0, FALSE, TRUE  },
    { "vs.2.0",            ST_VERTEX,  2, 0, 0, 0, FALSE, TRUE  },
    { "vs.2.a",            ST_VERTEX,  2, 1, 0, 0, FALSE, FALSE },
    { "vs.2.sw",           ST_VERTEX,  2, 0, 0, 0, TRUE,  FALSE },
    { "vs.3.0",            ST_VERTEX,  3, 0, 0, 0, FALSE, TRUE  },
    { "vs.3.sw",           ST_VERTEX,  3, 0, 0, 0, TRUE,  FALSE },
    { "vs_1_0",            ST_VERTEX,  1, 0, 0, 0, FALSE, TRUE  },
    { "vs_1_1",            ST_VERTEX,  1, 1, 0, 0, FALSE, TRUE  },
    { "vs_2_0",            ST_VERTEX,  2, 0, 0, 0, FALSE, TRUE  },
    { "vs_2_a",            ST_VERTEX,  2, 1, 0, 0, FALSE, FALSE },
    { "vs_2_sw",           ST_VERTEX,  2, 0, 0, 0, TRUE,  FALSE },
    { "vs_3_0",            ST_VERTEX,  3, 0, 0, 0, FALSE, TRUE  },
    { "vs_3_sw",           ST_VERTEX,  3, 0, 0, 0, TRUE,  FALSE },
    { "vs_4_0",            ST_VERTEX,  4, 0, 0, 0, FALSE, TRUE  },
    { "vs_4_0_level_9_0",  ST_VERTEX,  4, 0, 9, 0, FALSE, FALSE },
    { "vs_4_0_level_9_1",  ST_VERTEX,  4, 0, 9, 1, FALSE, FALSE },
    { "vs_4_0_level_9_3",  ST_VERTEX,  4, 0, 9, 3, FALSE, FALSE },
    { "vs_4_1",            ST_VERTEX,  4, 1, 0, 0, FALSE, TRUE  },
    { "vs_5_0",            ST_VERTEX,  5, 0, 0, 0, FALSE, TRUE  },
};

static const struct target_info * get_target_info(const char *target)
{
    LONG min = 0;
    LONG max = sizeof(targets_info) / sizeof(targets_info[0]) - 1;
    LONG cur;
    int res;

    while (min <= max)
    {
        cur = (min + max) / 2;
        res = strcmp(target, targets_info[cur].name);
        if (res < 0)
            max = cur - 1;
        else if (res > 0)
            min = cur + 1;
        else
            return &targets_info[cur];
    }

    return NULL;
}

591 592 593 594 595 596
static HRESULT compile_shader(const char *preproc_shader, const char *target, const char *entrypoint,
        ID3DBlob **shader_blob, ID3DBlob **error_messages)
{
    struct bwriter_shader *shader;
    char *messages = NULL;
    HRESULT hr;
597
    DWORD *res, size, major, minor;
598 599
    ID3DBlob *buffer;
    char *pos;
600
    enum shader_type shader_type;
601
    const struct target_info *info;
602

603 604
    TRACE("Preprocessed shader source: %s\n", debugstr_a(preproc_shader));

605 606 607
    TRACE("Checking compilation target %s\n", debugstr_a(target));
    info = get_target_info(target);
    if (!info)
608
    {
609
        FIXME("Unknown compilation target %s\n", debugstr_a(target));
610 611 612 613
        return D3DERR_INVALIDCALL;
    }
    else
    {
614 615 616 617 618 619 620 621 622 623 624
        if (!info->support)
        {
            FIXME("Compilation target %s not yet supported\n", debugstr_a(target));
            return D3DERR_INVALIDCALL;
        }
        else
        {
            shader_type = info->type;
            major = info->sm_major;
            minor = info->sm_minor;
        }
625 626 627
    }

    shader = parse_hlsl_shader(preproc_shader, shader_type, major, minor, entrypoint, &messages);
628 629 630 631

    if (messages)
    {
        TRACE("Compiler messages:\n");
632
        TRACE("%s\n", debugstr_a(messages));
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693

        TRACE("Shader source:\n");
        TRACE("%s\n", debugstr_a(preproc_shader));

        if (error_messages)
        {
            const char *preproc_messages = *error_messages ? ID3D10Blob_GetBufferPointer(*error_messages) : NULL;

            size = strlen(messages) + (preproc_messages ? strlen(preproc_messages) : 0) + 1;
            hr = D3DCreateBlob(size, &buffer);
            if (FAILED(hr))
            {
                HeapFree(GetProcessHeap(), 0, messages);
                if (shader) SlDeleteShader(shader);
                return hr;
            }
            pos = ID3D10Blob_GetBufferPointer(buffer);
            if (preproc_messages)
            {
                memcpy(pos, preproc_messages, strlen(preproc_messages) + 1);
                pos += strlen(preproc_messages);
            }
            memcpy(pos, messages, strlen(messages) + 1);

            if (*error_messages) ID3D10Blob_Release(*error_messages);
            *error_messages = buffer;
        }
        HeapFree(GetProcessHeap(), 0, messages);
    }

    if (!shader)
    {
        ERR("HLSL shader parsing failed.\n");
        return D3DXERR_INVALIDDATA;
    }

    hr = SlWriteBytecode(shader, 9, &res, &size);
    SlDeleteShader(shader);
    if (FAILED(hr))
    {
        ERR("SlWriteBytecode failed with error 0x%08x.\n", hr);
        return D3DXERR_INVALIDDATA;
    }

    if (shader_blob)
    {
        hr = D3DCreateBlob(size, &buffer);
        if (FAILED(hr))
        {
            HeapFree(GetProcessHeap(), 0, res);
            return hr;
        }
        memcpy(ID3D10Blob_GetBufferPointer(buffer), res, size);
        *shader_blob = buffer;
    }

    HeapFree(GetProcessHeap(), 0, res);

    return S_OK;
}

694 695 696 697
HRESULT WINAPI D3DCompile(const void *data, SIZE_T data_size, const char *filename,
        const D3D_SHADER_MACRO *defines, ID3DInclude *include, const char *entrypoint,
        const char *target, UINT sflags, UINT eflags, ID3DBlob **shader, ID3DBlob **error_messages)
{
698 699
    HRESULT hr;

700 701
    TRACE("data %p, data_size %lu, filename %s, defines %p, include %p, entrypoint %s,\n"
            "target %s, sflags %#x, eflags %#x, shader %p, error_messages %p\n",
702 703 704
            data, data_size, debugstr_a(filename), defines, include, debugstr_a(entrypoint),
            debugstr_a(target), sflags, eflags, shader, error_messages);

705 706 707 708
    if (shader) *shader = NULL;
    if (error_messages) *error_messages = NULL;

    EnterCriticalSection(&wpp_mutex);
709

710
    hr = preprocess_shader(data, data_size, filename, defines, include, error_messages);
711 712
    if (SUCCEEDED(hr))
        hr = compile_shader(wpp_output, target, entrypoint, shader, error_messages);
713

714 715 716
    HeapFree(GetProcessHeap(), 0, wpp_output);
    LeaveCriticalSection(&wpp_mutex);
    return hr;
717
}
718 719 720 721 722 723 724 725

HRESULT WINAPI D3DPreprocess(const void *data, SIZE_T size, const char *filename,
        const D3D_SHADER_MACRO *defines, ID3DInclude *include,
        ID3DBlob **shader, ID3DBlob **error_messages)
{
    HRESULT hr;
    ID3DBlob *buffer;

726 727 728
    TRACE("data %p, size %lu, filename %s, defines %p, include %p, shader %p, error_messages %p\n",
          data, size, debugstr_a(filename), defines, include, shader, error_messages);

729 730 731 732 733 734 735 736
    if (!data)
        return E_INVALIDARG;

    EnterCriticalSection(&wpp_mutex);

    if (shader) *shader = NULL;
    if (error_messages) *error_messages = NULL;

737
    hr = preprocess_shader(data, size, filename, defines, include, error_messages);
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757

    if (SUCCEEDED(hr))
    {
        if (shader)
        {
            hr = D3DCreateBlob(wpp_output_size, &buffer);
            if (FAILED(hr))
                goto cleanup;
            CopyMemory(ID3D10Blob_GetBufferPointer(buffer), wpp_output, wpp_output_size);
            *shader = buffer;
        }
        else
            hr = E_INVALIDARG;
    }

cleanup:
    HeapFree(GetProcessHeap(), 0, wpp_output);
    LeaveCriticalSection(&wpp_mutex);
    return hr;
}
758 759 760 761 762 763 764

HRESULT WINAPI D3DDisassemble(const void *data, SIZE_T size, UINT flags, const char *comments, ID3DBlob **disassembly)
{
    FIXME("data %p, size %lu, flags %#x, comments %p, disassembly %p stub!\n",
            data, size, flags, comments, disassembly);
    return E_NOTIMPL;
}