summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/msm5205.cpp
blob: 1a3498cdf899c36cc23b3dfb77d5c9576f89c796 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
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
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
305
306
307
308
309
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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
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
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/*
 *   streaming ADPCM driver
 *   by Aaron Giles
 *
 *   Library to transcode from an ADPCM source to raw PCM.
 *   Written by Buffoni Mirko in 08/06/97
 *   References: various sources and documents.
 *
 *   HJB 08/31/98
 *   modified to use an automatically selected oversampling factor
 *   for the current sample rate
 *
 *   01/06/99
 *    separate MSM5205 emulator form adpcm.c and some fix
 *
 *   07/29/12
 *    added basic support for the MSM6585
 */

#include "emu.h"
#include "msm5205.h"

/*

    MSM 5205 ADPCM chip:

    Data is streamed from a CPU by means of a clock generated on the chip.

    Holding the rate selector lines (S1 and S2) both high places the MSM5205 in an undocumented
    mode which disables the sampling clock generator and makes VCK an input line.

    A reset signal is set high or low to determine whether playback (and interrupts) are occurring.

  MSM6585: is an upgraded MSM5205 voice synth IC.
   Improvements:
    More precise internal DA converter
    Built in low-pass filter
    Expanded sampling frequency

   Differences between MSM6585 & MSM5205:

                              MSM6585          MSM5205
    Master clock frequency    640kHz           384kHz
    Sampling frequency        4k/8k/16k/32kHz  4k/6k/8kHz
    ADPCM bit length          4-bit            3-bit/4-bit
    Data capture timing       5µsec            15.6µsec
    DA converter              12-bit           10-bit
    Low-pass filter           -40dB/oct        N/A
    Overflow prevent circuit  Included         N/A

    Data capture follows VCK falling edge on MSM5205 (VCK rising edge on MSM6585)

   TODO:
   - lowpass filter for MSM6585

 */

DEFINE_DEVICE_TYPE(MSM5205, msm5205_device, "msm5205", "MSM5205")
DEFINE_DEVICE_TYPE(MSM6585, msm6585_device, "msm6585", "MSM6585")


msm5205_device::msm5205_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: msm5205_device(mconfig, MSM5205, tag, owner, clock)
{
}

msm5205_device::msm5205_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
	: device_t(mconfig, type, tag, owner, clock),
		device_sound_interface(mconfig, *this),
		m_s1(false),
		m_s2(false),
		m_bitwidth(4),
		m_vck_cb(*this),
		m_vck_legacy_cb(*this)
{
}


msm6585_device::msm6585_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
	: msm5205_device(mconfig, MSM6585, tag, owner, clock)
{
}

//-------------------------------------------------
//  set_prescaler_selector - configuration helper
//-------------------------------------------------

void msm5205_device::set_prescaler_selector(device_t &device, int select)
{
	msm5205_device &msm = downcast<msm5205_device &>(device);
	msm.m_s1 = BIT(select, 1);
	msm.m_s2 = BIT(select, 0);
	msm.m_bitwidth = (select & 4) ? 4 : 3;
}

//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void msm5205_device::device_start()
{
	m_vck_cb.resolve_safe();
	m_vck_legacy_cb.resolve();

	/* compute the difference tables */
	compute_tables();

	/* stream system initialize */
	m_stream = machine().sound().stream_alloc(*this, 0, 1, clock());
	m_vck_timer = timer_alloc(TIMER_VCK);
	m_capture_timer = timer_alloc(TIMER_ADPCM_CAPTURE);

	/* register for save states */
	save_item(NAME(m_data));
	save_item(NAME(m_vck));
	save_item(NAME(m_reset));
	save_item(NAME(m_s1));
	save_item(NAME(m_s2));
	save_item(NAME(m_bitwidth));
	save_item(NAME(m_signal));
	save_item(NAME(m_step));
}

//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void msm5205_device::device_reset()
{
	/* initialize work */
	m_data    = 0;
	m_vck     = 0;
	m_reset   = 0;
	m_signal  = 0;
	m_step    = 0;
}


/*
 * ADPCM lookup table
 */

/* step size index shift table */
static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };

/*
 *   Compute the difference table
 */

void msm5205_device::compute_tables()
{
	/* nibble to bit map */
	static const int nbl2bit[16][4] =
	{
		{ 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
		{ 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
		{-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
		{-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
	};

	int step, nib;

	/* loop over all possible steps */
	for (step = 0; step <= 48; step++)
	{
		/* compute the step value */
		int stepval = floor (16.0 * pow (11.0 / 10.0, (double)step));

		/* loop over all nibbles and compute the difference */
		for (nib = 0; nib < 16; nib++)
		{
			m_diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
				(stepval   * nbl2bit[nib][1] +
					stepval/2 * nbl2bit[nib][2] +
					stepval/4 * nbl2bit[nib][3] +
					stepval/8);
		}
	}
}


//-------------------------------------------------
//  device_timer - called whenever a device timer
//  fires
//-------------------------------------------------

void msm5205_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
		case TIMER_VCK:
			m_vck = !m_vck;
			m_vck_cb(m_vck);
			if (!m_vck)
				m_capture_timer->adjust(attotime::from_nsec(15600));
			break;

		case TIMER_ADPCM_CAPTURE:
			update_adpcm();
			break;
	}
}

// timer callback at VCK low edge on MSM5205 (at rising edge on MSM6585)
void msm5205_device::update_adpcm()
{
	int val;
	int new_signal;

	// callback user handler and latch next data
	if (!m_vck_legacy_cb.isnull())
		m_vck_legacy_cb(1);

	// reset check at last hiedge of VCK
	if (m_reset)
	{
		new_signal = 0;
		m_step = 0;
	}
	else
	{
		/* update signal */
		/* !! MSM5205 has internal 12bit decoding, signal width is 0 to 8191 !! */
		val = m_data;
		new_signal = m_signal + m_diff_lookup[m_step * 16 + (val & 15)];

		if (new_signal > 2047) new_signal = 2047;
		else if (new_signal < -2048) new_signal = -2048;

		m_step += index_shift[val & 7];

		if (m_step > 48) m_step = 48;
		else if (m_step < 0) m_step = 0;
	}

	/* update when signal changed */
	if( m_signal != new_signal)
	{
		m_stream->update();
		m_signal = new_signal;
	}
}



/*
 *    Handle an update of the VCK status of a chip (1 is reset ON, 0 is reset OFF)
 *    This function can use selector = MSM5205_SEX only
 */
WRITE_LINE_MEMBER(msm5205_device::vclk_w)
{
	if (get_prescaler() != 0)
		logerror("Error: vclk_w() called but VCK selected master mode\n");
	else
	{
		if (m_vck && !state)
			m_capture_timer->adjust(attotime::from_nsec(15600));
		m_vck = state;
	}
}

/*
 *    Handle an update of the reset status of a chip (1 is reset ON, 0 is reset OFF)
 */

WRITE_LINE_MEMBER(msm5205_device::reset_w)
{
	m_reset = state;
}

/*
 *    Handle an update of the data to the chip
 */

void msm5205_device::data_w(int data)
{
	if (m_bitwidth == 4)
		m_data = data & 0x0f;
	else
		m_data = (data & 0x07) << 1; /* unknown */
}

WRITE8_MEMBER(msm5205_device::data_w)
{
	data_w(data);
}

int msm5205_device::get_prescaler() const
{
	if (m_s1)
		return m_s2 ? 0 : 64;
	else
		return m_s2 ? 48 : 96;
}

int msm6585_device::get_prescaler() const
{
	return (m_s1 ? 20 : 40) * (m_s2 ? 1 : 4);
}

/*
 *    Handle a change of the selector
 */

void msm5205_device::playmode_w(int select)
{
	int bitwidth = (select & 4) ? 4 : 3;

	if ((select & 3) != ((m_s1 << 1) | m_s2))
	{
		m_stream->update();

		m_s1 = BIT(select, 1);
		m_s2 = BIT(select, 0);

		/* timer set */
		notify_clock_changed();
	}

	if (m_bitwidth != bitwidth)
	{
		m_stream->update();
		m_bitwidth = bitwidth;
	}
}

WRITE_LINE_MEMBER(msm5205_device::s1_w)
{
	if (m_s1 != bool(state))
	{
		m_stream->update();
		m_s1 = state;
		notify_clock_changed();
	}
}

WRITE_LINE_MEMBER(msm5205_device::s2_w)
{
	if (m_s2 != bool(state))
	{
		m_stream->update();
		m_s2 = state;
		notify_clock_changed();
	}
}


//-------------------------------------------------
//  device_clock_changed - called when the
//  device clock is altered in any way
//-------------------------------------------------

void msm5205_device::device_clock_changed()
{
	int prescaler = get_prescaler();
	if (prescaler != 0)
	{
		logerror("/%d prescaler selected\n", prescaler);

		attotime half_period = clocks_to_attotime(prescaler / 2);
		m_vck_timer->adjust(half_period, 0, half_period);
	}
	else
	{
		logerror("VCK slave mode selected\n");
		m_vck_timer->adjust(attotime::never);
	}
}


//-------------------------------------------------
//  sound_stream_update - handle a stream update
//-------------------------------------------------

void msm5205_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
	stream_sample_t *buffer = outputs[0];

	/* if this voice is active */
	if(m_signal)
	{
		short val = m_signal * 16;
		while (samples)
		{
			*buffer++ = val;
			samples--;
		}
	}
	else
		memset(buffer, 0, samples * sizeof(*buffer));
}


//-------------------------------------------------
//  device_timer - called whenever a device timer
//  fires
//-------------------------------------------------

void msm6585_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
	switch (id)
	{
		case TIMER_VCK:
			m_vck = !m_vck;
			m_vck_cb(m_vck);
			if (m_vck)
				m_capture_timer->adjust(attotime::from_usec(3));
			break;

		case TIMER_ADPCM_CAPTURE:
			update_adpcm();
			break;
	}
}


//-------------------------------------------------
//  sound_stream_update - handle a stream update
//-------------------------------------------------

void msm6585_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
	// should this be different?
	msm5205_device::sound_stream_update(stream, inputs, outputs,samples);
}
) ) { return "linear"; } else if (0 == strcmp(_glsl, "flat") ) { return "nointerpolation"; } return _glsl; // noperspective } const char* getUniformTypeName(UniformType::Enum _enum) { return s_uniformTypeName[_enum]; } UniformType::Enum nameToUniformTypeEnum(const char* _name) { for (uint32_t ii = 0; ii < UniformType::Count; ++ii) { if (NULL != s_uniformTypeName[ii] && 0 == strcmp(_name, s_uniformTypeName[ii]) ) { return UniformType::Enum(ii); } } return UniformType::Count; } int32_t writef(bx::WriterI* _writer, const char* _format, ...) { va_list argList; va_start(argList, _format); char temp[2048]; char* out = temp; int32_t max = sizeof(temp); int32_t len = bx::vsnprintf(out, max, _format, argList); if (len > max) { out = (char*)alloca(len); len = bx::vsnprintf(out, len, _format, argList); } len = bx::write(_writer, out, len); va_end(argList); return len; } class Bin2cWriter : public bx::CrtFileWriter { public: Bin2cWriter(const char* _name) : m_name(_name) { } virtual ~Bin2cWriter() { } virtual int32_t close() BX_OVERRIDE { generate(); return bx::CrtFileWriter::close(); } virtual int32_t write(const void* _data, int32_t _size) BX_OVERRIDE { const char* data = (const char*)_data; m_buffer.insert(m_buffer.end(), data, data+_size); return _size; } private: void generate() { #define HEX_DUMP_WIDTH 16 #define HEX_DUMP_SPACE_WIDTH 96 #define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s" const uint8_t* data = &m_buffer[0]; uint32_t size = (uint32_t)m_buffer.size(); outf("static const uint8_t %s[%d] =\n{\n", m_name.c_str(), size); if (NULL != data) { char hex[HEX_DUMP_SPACE_WIDTH+1]; char ascii[HEX_DUMP_WIDTH+1]; uint32_t hexPos = 0; uint32_t asciiPos = 0; for (uint32_t ii = 0; ii < size; ++ii) { bx::snprintf(&hex[hexPos], sizeof(hex)-hexPos, "0x%02x, ", data[asciiPos]); hexPos += 6; ascii[asciiPos] = isprint(data[asciiPos]) && data[asciiPos] != '\\' ? data[asciiPos] : '.'; asciiPos++; if (HEX_DUMP_WIDTH == asciiPos) { ascii[asciiPos] = '\0'; outf("\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii); data += asciiPos; hexPos = 0; asciiPos = 0; } } if (0 != asciiPos) { ascii[asciiPos] = '\0'; outf("\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii); } } outf("};\n"); #undef HEX_DUMP_WIDTH #undef HEX_DUMP_SPACE_WIDTH #undef HEX_DUMP_FORMAT } int32_t outf(const char* _format, ...) { va_list argList; va_start(argList, _format); char temp[2048]; char* out = temp; int32_t max = sizeof(temp); int32_t len = bx::vsnprintf(out, max, _format, argList); if (len > max) { out = (char*)alloca(len); len = bx::vsnprintf(out, len, _format, argList); } int32_t size = bx::CrtFileWriter::write(out, len); va_end(argList); return size; } std::string m_filePath; std::string m_name; typedef std::vector<uint8_t> Buffer; Buffer m_buffer; }; struct Varying { std::string m_precision; std::string m_interpolation; std::string m_name; std::string m_type; std::string m_init; std::string m_semantics; }; typedef std::unordered_map<std::string, Varying> VaryingMap; class File { public: File(const char* _filePath) : m_data(NULL) { FILE* file = fopen(_filePath, "r"); if (NULL != file) { m_size = fsize(file); m_data = new char[m_size+1]; m_size = (uint32_t)fread(m_data, 1, m_size, file); m_data[m_size] = '\0'; fclose(file); } } ~File() { delete [] m_data; } const char* getData() const { return m_data; } uint32_t getSize() const { return m_size; } private: char* m_data; uint32_t m_size; }; void strins(char* _str, const char* _insert) { size_t len = strlen(_insert); memmove(&_str[len], _str, strlen(_str) ); memcpy(_str, _insert, len); } void strreplace(char* _str, const char* _find, const char* _replace) { const size_t len = strlen(_find); char* replace = (char*)alloca(len+1); bx::strlcpy(replace, _replace, len+1); for (size_t ii = strlen(replace); ii < len; ++ii) { replace[ii] = ' '; } replace[len] = '\0'; BX_CHECK(len >= strlen(_replace), ""); for (char* ptr = strstr(_str, _find); NULL != ptr; ptr = strstr(ptr + len, _find) ) { memcpy(ptr, replace, len); } } class LineReader { public: LineReader(const char* _str) : m_str(_str) , m_pos(0) , m_size( (uint32_t)strlen(_str) ) { } std::string getLine() { const char* str = &m_str[m_pos]; skipLine(); const char* eol = &m_str[m_pos]; std::string tmp; tmp.assign(str, eol-str); return tmp; } bool isEof() const { return m_str[m_pos] == '\0'; } void skipLine() { const char* str = &m_str[m_pos]; const char* nl = bx::strnl(str); m_pos += (uint32_t)(nl - str); } const char* m_str; uint32_t m_pos; uint32_t m_size; }; void printCode(const char* _code, int32_t _line, int32_t _start, int32_t _end) { fprintf(stderr, "Code:\n---\n"); LineReader lr(_code); for (int32_t line = 1; !lr.isEof() && line < _end; ++line) { if (line >= _start) { fprintf(stderr, "%s%3d: %s", _line == line ? ">>> " : " ", line, lr.getLine().c_str() ); } else { lr.skipLine(); } } fprintf(stderr, "---\n"); } void writeFile(const char* _filePath, const void* _data, int32_t _size) { bx::CrtFileWriter out; if (0 == out.open(_filePath) ) { out.write(_data, _size); out.close(); } } struct Preprocessor { Preprocessor(const char* _filePath, bool _gles, const char* _includeDir = NULL) : m_tagptr(m_tags) , m_scratchPos(0) , m_fgetsPos(0) { m_tagptr->tag = FPPTAG_USERDATA; m_tagptr->data = this; m_tagptr++; m_tagptr->tag = FPPTAG_DEPENDS; m_tagptr->data = (void*)fppDepends; m_tagptr++; m_tagptr->tag = FPPTAG_INPUT; m_tagptr->data = (void*)fppInput; m_tagptr++; m_tagptr->tag = FPPTAG_OUTPUT; m_tagptr->data = (void*)fppOutput; m_tagptr++; m_tagptr->tag = FPPTAG_ERROR; m_tagptr->data = (void*)fppError; m_tagptr++; m_tagptr->tag = FPPTAG_IGNOREVERSION; m_tagptr->data = (void*)0; m_tagptr++; m_tagptr->tag = FPPTAG_LINE; m_tagptr->data = (void*)0; m_tagptr++; m_tagptr->tag = FPPTAG_INPUT_NAME; m_tagptr->data = scratch(_filePath); m_tagptr++; if (NULL != _includeDir) { addInclude(_includeDir); } if (!_gles) { m_default = "#define lowp\n#define mediump\n#define highp\n"; } } void setDefine(const char* _define) { m_tagptr->tag = FPPTAG_DEFINE; m_tagptr->data = scratch(_define); m_tagptr++; } void setDefaultDefine(const char* _name) { char temp[1024]; bx::snprintf(temp, BX_COUNTOF(temp) , "#ifndef %s\n" "# define %s 0\n" "#endif // %s\n" "\n" , _name , _name , _name ); m_default += temp; } void writef(const char* _format, ...) { va_list argList; va_start(argList, _format); bx::stringPrintfVargs(m_default, _format, argList); va_end(argList); } void addInclude(const char* _includeDir) { char* start = scratch(_includeDir); for (char* split = strchr(start, ';'); NULL != split; split = strchr(start, ';')) { *split = '\0'; m_tagptr->tag = FPPTAG_INCLUDE_DIR; m_tagptr->data = start; m_tagptr++; start = split + 1; } m_tagptr->tag = FPPTAG_INCLUDE_DIR; m_tagptr->data = start; m_tagptr++; } void addDependency(const char* _fileName) { m_depends += " \\\n "; m_depends += _fileName; } bool run(const char* _input) { m_fgetsPos = 0; m_preprocessed.clear(); m_input = m_default; m_input += "\n\n"; size_t len = strlen(_input)+1; char* temp = new char[len]; bx::eolLF(temp, len, _input); m_input += temp; delete [] temp; fppTag* tagptr = m_tagptr; tagptr->tag = FPPTAG_END; tagptr->data = 0; tagptr++; int result = fppPreProcess(m_tags); return 0 == result; } char* fgets(char* _buffer, int _size) { int ii = 0; for (char ch = m_input[m_fgetsPos]; m_fgetsPos < m_input.size() && ii < _size-1; ch = m_input[++m_fgetsPos]) { _buffer[ii++] = ch; if (ch == '\n' || ii == _size) { _buffer[ii] = '\0'; m_fgetsPos++; return _buffer; } } return NULL; } static void fppDepends(char* _fileName, void* _userData) { Preprocessor* thisClass = (Preprocessor*)_userData; thisClass->addDependency(_fileName); } static char* fppInput(char* _buffer, int _size, void* _userData) { Preprocessor* thisClass = (Preprocessor*)_userData; return thisClass->fgets(_buffer, _size); } static void fppOutput(int _ch, void* _userData) { Preprocessor* thisClass = (Preprocessor*)_userData; thisClass->m_preprocessed += _ch; } static void fppError(void* /*_userData*/, char* _format, va_list _vargs) { vfprintf(stderr, _format, _vargs); } char* scratch(const char* _str) { char* result = &m_scratch[m_scratchPos]; strcpy(result, _str); m_scratchPos += (uint32_t)strlen(_str)+1; return result; } fppTag m_tags[MAX_TAGS]; fppTag* m_tagptr; std::string m_depends; std::string m_default; std::string m_input; std::string m_preprocessed; char m_scratch[16<<10]; uint32_t m_scratchPos; uint32_t m_fgetsPos; }; typedef std::vector<std::string> InOut; uint32_t parseInOut(InOut& _inout, const char* _str, const char* _eol) { uint32_t hash = 0; _str = bx::strws(_str); if (_str < _eol) { const char* delim; do { delim = strpbrk(_str, " ,"); if (NULL != delim) { delim = delim > _eol ? _eol : delim; std::string token; token.assign(_str, delim-_str); _inout.push_back(token); _str = bx::strws(delim + 1); } } while (delim < _eol && _str < _eol && NULL != delim); std::sort(_inout.begin(), _inout.end() ); bx::HashMurmur2A murmur; murmur.begin(); for (InOut::const_iterator it = _inout.begin(), itEnd = _inout.end(); it != itEnd; ++it) { murmur.add(it->c_str(), (uint32_t)it->size() ); } hash = murmur.end(); } return hash; } void addFragData(Preprocessor& _preprocessor, char* _data, uint32_t _idx, bool _comma) { char find[32]; bx::snprintf(find, sizeof(find), "gl_FragData[%d]", _idx); char replace[32]; bx::snprintf(replace, sizeof(replace), "gl_FragData_%d_", _idx); strreplace(_data, find, replace); _preprocessor.writef( " \\\n\t%sout vec4 gl_FragData_%d_ : SV_TARGET%d" , _comma ? ", " : " " , _idx , _idx ); } void voidFragData(char* _data, uint32_t _idx) { char find[32]; bx::snprintf(find, sizeof(find), "gl_FragData[%d]", _idx); strreplace(_data, find, "bgfx_VoidFrag"); } // c - compute // d - domain // f - fragment // g - geometry // h - hull // v - vertex // // OpenGL #version Features Direct3D Features Shader Model // 2.1 120 vf 9.0 vf 2.0 // 3.0 130 // 3.1 140 // 3.2 150 vgf // 3.3 330 10.0 vgf 4.0 // 4.0 400 vhdgf // 4.1 410 // 4.2 420 11.0 vhdgf+c 5.0 // 4.3 430 vhdgf+c // 4.4 440 void help(const char* _error = NULL) { if (NULL != _error) { fprintf(stderr, "Error:\n%s\n\n", _error); } fprintf(stderr , "shaderc, bgfx shader compiler tool\n" "Copyright 2011-2015 Branimir Karadzic. All rights reserved.\n" "License: http://www.opensource.org/licenses/BSD-2-Clause\n\n" ); fprintf(stderr , "Usage: shaderc -f <in> -o <out> --type <v/f> --platform <platform>\n" "\n" "Options:\n" " -f <file path> Input file path.\n" " -i <include path> Include path (for multiple paths use semicolon).\n" " -o <file path> Output file path.\n" " --bin2c <file path> Generate C header file.\n" " --depends <file path> Generate makefile style depends file.\n" " --platform <platform> Target platform.\n" " android\n" " asm.js\n" " ios\n" " linux\n" " nacl\n" " osx\n" " windows\n" " --preprocess Preprocess only.\n" " --raw Do not process shader. No preprocessor, and no glsl-optimizer (GLSL only).\n" " --type <type> Shader type (vertex, fragment)\n" " --varyingdef <file path> Path to varying.def.sc file.\n" " --verbose Verbose.\n" "\n" "Options (DX9 and DX11 only):\n" "\n" " --debug Debug information.\n" " --disasm Disassemble compiled shader.\n" " -p, --profile <profile> Shader model (f.e. ps_3_0).\n" " -O <level> Optimization level (0, 1, 2, 3).\n" " --Werror Treat warnings as errors.\n" "\n" "For additional information, see https://github.com/bkaradzic/bgfx\n" ); } int main(int _argc, const char* _argv[]) { bx::CommandLine cmdLine(_argc, _argv); if (cmdLine.hasArg('h', "help") ) { help(); return EXIT_FAILURE; } g_verbose = cmdLine.hasArg("verbose"); const char* filePath = cmdLine.findOption('f'); if (NULL == filePath) { help("Shader file name must be specified."); return EXIT_FAILURE; } const char* outFilePath = cmdLine.findOption('o'); if (NULL == outFilePath) { help("Output file name must be specified."); return EXIT_FAILURE; } const char* type = cmdLine.findOption('\0', "type"); if (NULL == type) { help("Must specify shader type."); return EXIT_FAILURE; } const char* platform = cmdLine.findOption('\0', "platform"); if (NULL == platform) { help("Must specify platform."); return EXIT_FAILURE; } bool raw = cmdLine.hasArg('\0', "raw"); uint32_t glsl = 0; uint32_t essl = 0; uint32_t hlsl = 2; uint32_t d3d = 11; const char* profile = cmdLine.findOption('p', "profile"); if (NULL != profile) { if (0 == strncmp(&profile[1], "s_4_0_level", 11) ) { hlsl = 2; } else if (0 == strncmp(&profile[1], "s_3", 3) ) { hlsl = 3; d3d = 9; } else if (0 == strncmp(&profile[1], "s_4", 3) ) { hlsl = 4; } else if (0 == strncmp(&profile[1], "s_5", 3) ) { hlsl = 5; } else { glsl = atoi(profile); } } else { essl = 2; } const char* bin2c = NULL; if (cmdLine.hasArg("bin2c") ) { bin2c = cmdLine.findOption("bin2c"); if (NULL == bin2c) { bin2c = bx::baseName(outFilePath); uint32_t len = (uint32_t)strlen(bin2c); char* temp = (char*)alloca(len+1); for (char *out = temp; *bin2c != '\0';) { char ch = *bin2c++; if (isalnum(ch) ) { *out++ = ch; } else { *out++ = '_'; } } temp[len] = '\0'; bin2c = temp; } } bool depends = cmdLine.hasArg("depends"); bool preprocessOnly = cmdLine.hasArg("preprocess"); const char* includeDir = cmdLine.findOption('i'); Preprocessor preprocessor(filePath, 0 != essl, includeDir); std::string dir; { const char* base = bx::baseName(filePath); if (base != filePath) { dir.assign(filePath, base-filePath); preprocessor.addInclude(dir.c_str() ); } } preprocessor.setDefaultDefine("BX_PLATFORM_ANDROID"); preprocessor.setDefaultDefine("BX_PLATFORM_EMSCRIPTEN"); preprocessor.setDefaultDefine("BX_PLATFORM_IOS"); preprocessor.setDefaultDefine("BX_PLATFORM_LINUX"); preprocessor.setDefaultDefine("BX_PLATFORM_NACL"); preprocessor.setDefaultDefine("BX_PLATFORM_OSX"); preprocessor.setDefaultDefine("BX_PLATFORM_WINDOWS"); preprocessor.setDefaultDefine("BX_PLATFORM_XBOX360"); // preprocessor.setDefaultDefine("BGFX_SHADER_LANGUAGE_ESSL"); preprocessor.setDefaultDefine("BGFX_SHADER_LANGUAGE_GLSL"); preprocessor.setDefaultDefine("BGFX_SHADER_LANGUAGE_HLSL"); preprocessor.setDefaultDefine("BGFX_SHADER_TYPE_COMPUTE"); preprocessor.setDefaultDefine("BGFX_SHADER_TYPE_FRAGMENT"); preprocessor.setDefaultDefine("BGFX_SHADER_TYPE_VERTEX"); char glslDefine[128]; bx::snprintf(glslDefine, BX_COUNTOF(glslDefine), "BGFX_SHADER_LANGUAGE_GLSL=%d", glsl); if (0 == bx::stricmp(platform, "android") ) { preprocessor.setDefine("BX_PLATFORM_ANDROID=1"); preprocessor.setDefine("BGFX_SHADER_LANGUAGE_GLSL=1"); } else if (0 == bx::stricmp(platform, "asm.js") ) { preprocessor.setDefine("BX_PLATFORM_EMSCRIPTEN=1"); preprocessor.setDefine("BGFX_SHADER_LANGUAGE_GLSL=1"); } else if (0 == bx::stricmp(platform, "ios") ) { preprocessor.setDefine("BX_PLATFORM_IOS=1"); preprocessor.setDefine("BGFX_SHADER_LANGUAGE_GLSL=1"); } else if (0 == bx::stricmp(platform, "linux") ) { preprocessor.setDefine("BX_PLATFORM_LINUX=1"); preprocessor.setDefine(glslDefine); } else if (0 == bx::stricmp(platform, "nacl") ) { preprocessor.setDefine("BX_PLATFORM_NACL=1"); preprocessor.setDefine("BGFX_SHADER_LANGUAGE_GLSL=1"); } else if (0 == bx::stricmp(platform, "osx") ) { preprocessor.setDefine("BX_PLATFORM_OSX=1"); preprocessor.setDefine(glslDefine); } else if (0 == bx::stricmp(platform, "windows") ) { preprocessor.setDefine("BX_PLATFORM_WINDOWS=1"); char temp[256]; bx::snprintf(temp, sizeof(temp), "BGFX_SHADER_LANGUAGE_HLSL=%d", hlsl); preprocessor.setDefine(temp); } else if (0 == bx::stricmp(platform, "xbox360") ) { preprocessor.setDefine("BX_PLATFORM_XBOX360=1"); preprocessor.setDefine("BGFX_SHADER_LANGUAGE_HLSL=3"); } else { fprintf(stderr, "Unknown platform %s?!", platform); return EXIT_FAILURE; } preprocessor.setDefine("M_PI=3.1415926535897932384626433832795"); char shaderType = tolower(type[0]); switch (shaderType) { case 'c': preprocessor.setDefine("BGFX_SHADER_TYPE_COMPUTE=1"); break; case 'f': preprocessor.setDefine("BGFX_SHADER_TYPE_FRAGMENT=1"); break; case 'v': preprocessor.setDefine("BGFX_SHADER_TYPE_VERTEX=1"); break; default: fprintf(stderr, "Unknown type: %s?!", type); return EXIT_FAILURE; } bool compiled = false; FILE* file = fopen(filePath, "r"); if (NULL == file) { fprintf(stderr, "Unable to open file '%s'.\n", filePath); } else { VaryingMap varyingMap; std::string defaultVarying = dir + "varying.def.sc"; const char* varyingdef = cmdLine.findOption("varyingdef", defaultVarying.c_str() ); File attribdef(varyingdef); const char* parse = attribdef.getData(); if (NULL != parse && *parse != '\0') { preprocessor.addDependency(varyingdef); } while (NULL != parse && *parse != '\0') { parse = bx::strws(parse); const char* eol = strchr(parse, ';'); if (NULL == eol) { eol = bx::streol(parse); } if (NULL != eol) { const char* precision = NULL; const char* interpolation = NULL; const char* type = parse; if (0 == strncmp(type, "lowp", 4) || 0 == strncmp(type, "mediump", 7) || 0 == strncmp(type, "highp", 5) ) { precision = type; type = parse = bx::strws(bx::strword(parse) ); } if (0 == strncmp(type, "flat", 4) || 0 == strncmp(type, "smooth", 6) || 0 == strncmp(type, "noperspective", 13) ) { interpolation = type; type = parse = bx::strws(bx::strword(parse) ); } const char* name = parse = bx::strws(bx::strword(parse) ); const char* column = parse = bx::strws(bx::strword(parse) ); const char* semantics = parse = bx::strws(bx::strnws (parse) ); const char* assign = parse = bx::strws(bx::strword(parse) ); const char* init = parse = bx::strws(bx::strnws (parse) ); if (type < eol && name < eol && column < eol && ':' == *column && semantics < eol) { Varying var; if (NULL != precision) { var.m_precision.assign(precision, bx::strword(precision)-precision); } if (NULL != interpolation) { var.m_interpolation.assign(interpolation, bx::strword(interpolation)-interpolation); } var.m_type.assign(type, bx::strword(type)-type); var.m_name.assign(name, bx::strword(name)-name); var.m_semantics.assign(semantics, bx::strword(semantics)-semantics); if (d3d == 9 && var.m_semantics == "BITANGENT") { var.m_semantics = "BINORMAL"; } if (assign < eol && '=' == *assign && init < eol) { var.m_init.assign(init, eol-init); } varyingMap.insert(std::make_pair(var.m_name, var) ); } parse = bx::strws(bx::strnl(eol) ); } } InOut shaderInputs; InOut shaderOutputs; uint32_t inputHash = 0; uint32_t outputHash = 0; char* data; char* input; { const size_t padding = 1024; uint32_t size = (uint32_t)fsize(file); data = new char[size+padding+1]; size = (uint32_t)fread(data, 1, size, file); // Compiler generates "error X3000: syntax error: unexpected end of file" // if input doesn't have empty line at EOF. data[size] = '\n'; memset(&data[size+1], 0, padding); fclose(file); input = const_cast<char*>(bx::strws(data) ); while (input[0] == '$') { const char* str = bx::strws(input+1); const char* eol = bx::streol(str); const char* nl = bx::strnl(eol); input = const_cast<char*>(nl); if (0 == strncmp(str, "input", 5) ) { str += 5; const char* comment = strstr(str, "//"); eol = NULL != comment && comment < eol ? comment : eol; inputHash = parseInOut(shaderInputs, str, eol); } else if (0 == strncmp(str, "output", 6) ) { str += 6; const char* comment = strstr(str, "//"); eol = NULL != comment && comment < eol ? comment : eol; outputHash = parseInOut(shaderOutputs, str, eol); } else if (0 == strncmp(str, "raw", 3) ) { raw = true; str += 3; } input = const_cast<char*>(bx::strws(input) ); } if (!raw) { // To avoid commented code being recognized as used feature, // first preprocess pass is used to strip all comments before // substituting code. preprocessor.run(input); delete [] data; size = (uint32_t)preprocessor.m_preprocessed.size(); data = new char[size+padding+1]; memcpy(data, preprocessor.m_preprocessed.c_str(), size); memset(&data[size], 0, padding+1); input = data; } } if (raw) { bx::CrtFileWriter* writer = NULL; if (NULL != bin2c) { writer = new Bin2cWriter(bin2c); } else { writer = new bx::CrtFileWriter; } if (0 != writer->open(outFilePath) ) { fprintf(stderr, "Unable to open output file '%s'.", outFilePath); return EXIT_FAILURE; } uint32_t inputHash = 0; uint32_t outputHash = 0; if ('f' == shaderType) { bx::write(writer, BGFX_CHUNK_MAGIC_FSH); bx::write(writer, inputHash); } else if ('v' == shaderType) { bx::write(writer, BGFX_CHUNK_MAGIC_VSH); bx::write(writer, outputHash); } else { bx::write(writer, BGFX_CHUNK_MAGIC_CSH); bx::write(writer, outputHash); } if (0 != glsl) { bx::write(writer, uint16_t(0) ); uint32_t shaderSize = (uint32_t)strlen(input); bx::write(writer, shaderSize); bx::write(writer, input, shaderSize); bx::write(writer, uint8_t(0) ); compiled = true; } else { if (d3d > 9) { compiled = compileHLSLShaderDx11(cmdLine, input, writer); } else { compiled = compileHLSLShaderDx9(cmdLine, input, writer); } } writer->close(); delete writer; } else if ('c' == shaderType) // Compute { char* entry = strstr(input, "void main()"); if (NULL == entry) { fprintf(stderr, "Shader entry point 'void main()' is not found.\n"); } else { if (0 != glsl) { } else { preprocessor.writef( "#define lowp\n" "#define mediump\n" "#define highp\n" "#define ivec2 int2\n" "#define ivec3 int3\n" "#define ivec4 int4\n" "#define uvec2 uint2\n" "#define uvec3 uint3\n" "#define uvec4 uint4\n" "#define vec2 float2\n" "#define vec3 float3\n" "#define vec4 float4\n" "#define mat2 float2x2\n" "#define mat3 float3x3\n" "#define mat4 float4x4\n" ); entry[4] = '_'; preprocessor.writef("#define void_main()"); preprocessor.writef(" \\\n\tvoid main("); uint32_t arg = 0; const bool hasLocalInvocationID = NULL != strstr(input, "gl_LocalInvocationID"); const bool hasLocalInvocationIndex = NULL != strstr(input, "gl_LocalInvocationIndex"); const bool hasGlobalInvocationID = NULL != strstr(input, "gl_GlobalInvocationID"); const bool hasWorkGroupID = NULL != strstr(input, "gl_WorkGroupID"); if (hasLocalInvocationID) { preprocessor.writef( " \\\n\t%sint3 gl_LocalInvocationID : SV_GroupThreadID" , arg++ > 0 ? ", " : " " ); } if (hasLocalInvocationIndex) { preprocessor.writef( " \\\n\t%sint gl_LocalInvocationIndex : SV_GroupIndex" , arg++ > 0 ? ", " : " " ); } if (hasGlobalInvocationID) { preprocessor.writef( " \\\n\t%sint3 gl_GlobalInvocationID : SV_DispatchThreadID" , arg++ > 0 ? ", " : " " ); } if (hasWorkGroupID) { preprocessor.writef( " \\\n\t%sint3 gl_WorkGroupID : SV_GroupID" , arg++ > 0 ? ", " : " " ); } preprocessor.writef( " \\\n\t)\n" ); } if (preprocessor.run(input) ) { BX_TRACE("Input file: %s", filePath); BX_TRACE("Output file: %s", outFilePath); if (preprocessOnly) { bx::CrtFileWriter writer; if (0 != writer.open(outFilePath) ) { fprintf(stderr, "Unable to open output file '%s'.", outFilePath); return EXIT_FAILURE; } writer.write(preprocessor.m_preprocessed.c_str(), (int32_t)preprocessor.m_preprocessed.size() ); writer.close(); return EXIT_SUCCESS; } { bx::CrtFileWriter* writer = NULL; if (NULL != bin2c) { writer = new Bin2cWriter(bin2c); } else { writer = new bx::CrtFileWriter; } if (0 != writer->open(outFilePath) ) { fprintf(stderr, "Unable to open output file '%s'.", outFilePath); return EXIT_FAILURE; } bx::write(writer, BGFX_CHUNK_MAGIC_CSH); bx::write(writer, outputHash); if (0 != glsl) { std::string code; if (essl) { bx::stringPrintf(code, "#version 310 es\n"); } else { bx::stringPrintf(code, "#version %d\n", glsl == 0 ? 430 : glsl); } code += preprocessor.m_preprocessed; #if 1 bx::write(writer, uint16_t(0) ); uint32_t shaderSize = (uint32_t)code.size(); bx::write(writer, shaderSize); bx::write(writer, code.c_str(), shaderSize); bx::write(writer, uint8_t(0) ); compiled = true; #else compiled = compileGLSLShader(cmdLine, essl, code, writer); #endif // 0 } else { if (d3d > 9) { compiled = compileHLSLShaderDx11(cmdLine, preprocessor.m_preprocessed, writer); } else { compiled = compileHLSLShaderDx9(cmdLine, preprocessor.m_preprocessed, writer); } } writer->close(); delete writer; } if (compiled) { if (depends) { std::string ofp = outFilePath; ofp += ".d"; bx::CrtFileWriter writer; if (0 == writer.open(ofp.c_str() ) ) { writef(&writer, "%s : %s\n", outFilePath, preprocessor.m_depends.c_str() ); writer.close(); } } } } } } else // Vertex/Fragment { char* entry = strstr(input, "void main()"); if (NULL == entry) { fprintf(stderr, "Shader entry point 'void main()' is not found.\n"); } else { if (0 != glsl) { if (120 == glsl || essl) { preprocessor.writef( "#define ivec2 vec2\n" "#define ivec3 vec3\n" "#define ivec4 vec4\n" ); } if (0 == essl) { // bgfx shadow2D/Proj behave like EXT_shadow_samplers // not as GLSL language 1.2 specs shadow2D/Proj. preprocessor.writef( "#define shadow2D(_sampler, _coord) bgfxShadow2D(_sampler, _coord).x\n" "#define shadow2DProj(_sampler, _coord) bgfxShadow2DProj(_sampler, _coord).x\n" ); } for (InOut::const_iterator it = shaderInputs.begin(), itEnd = shaderInputs.end(); it != itEnd; ++it) { VaryingMap::const_iterator varyingIt = varyingMap.find(*it); if (varyingIt != varyingMap.end() ) { const Varying& var = varyingIt->second; const char* name = var.m_name.c_str(); if (0 == strncmp(name, "a_", 2) || 0 == strncmp(name, "i_", 2) ) { preprocessor.writef("attribute %s %s %s %s;\n" , var.m_precision.c_str() , var.m_interpolation.c_str() , var.m_type.c_str() , name ); } else { preprocessor.writef("%s varying %s %s %s;\n" , var.m_interpolation.c_str() , var.m_precision.c_str() , var.m_type.c_str() , name ); } } } for (InOut::const_iterator it = shaderOutputs.begin(), itEnd = shaderOutputs.end(); it != itEnd; ++it) { VaryingMap::const_iterator varyingIt = varyingMap.find(*it); if (varyingIt != varyingMap.end() ) { const Varying& var = varyingIt->second; preprocessor.writef("%s varying %s %s;\n" , var.m_interpolation.c_str() , var.m_type.c_str() , var.m_name.c_str() ); } } } else { preprocessor.writef( "#define lowp\n" "#define mediump\n" "#define highp\n" "#define ivec2 int2\n" "#define ivec3 int3\n" "#define ivec4 int4\n" "#define uvec2 uint2\n" "#define uvec3 uint3\n" "#define uvec4 uint4\n" "#define vec2 float2\n" "#define vec3 float3\n" "#define vec4 float4\n" "#define mat2 float2x2\n" "#define mat3 float3x3\n" "#define mat4 float4x4\n" ); if (hlsl < 4) { preprocessor.writef( "#define flat\n" "#define smooth\n" "#define noperspective\n" ); } entry[4] = '_'; if ('f' == shaderType) { const char* brace = strstr(entry, "{"); if (NULL != brace) { strins(const_cast<char*>(brace+1), "\nvec4 bgfx_VoidFrag;\n"); } const bool hasFragCoord = NULL != strstr(input, "gl_FragCoord") || hlsl > 3; const bool hasFragDepth = NULL != strstr(input, "gl_FragDepth"); const bool hasFrontFacing = NULL != strstr(input, "gl_FrontFacing"); const bool hasPrimitiveId = NULL != strstr(input, "gl_PrimitiveID"); bool hasFragData[8] = {}; uint32_t numFragData = 0; for (uint32_t ii = 0; ii < BX_COUNTOF(hasFragData); ++ii) { char temp[32]; bx::snprintf(temp, BX_COUNTOF(temp), "gl_FragData[%d]", ii); hasFragData[ii] = NULL != strstr(input, temp); numFragData += hasFragData[ii]; } if (0 == numFragData) { // GL errors when both gl_FragColor and gl_FragData is used. // This will trigger the same error with HLSL compiler too. preprocessor.writef("#define gl_FragColor gl_FragData_0_\n"); } preprocessor.writef("#define void_main()"); preprocessor.writef(" \\\n\tvoid main("); uint32_t arg = 0; if (hasFragCoord) { preprocessor.writef(" \\\n\tvec4 gl_FragCoord : SV_POSITION"); ++arg; } for (InOut::const_iterator it = shaderInputs.begin(), itEnd = shaderInputs.end(); it != itEnd; ++it) { VaryingMap::const_iterator varyingIt = varyingMap.find(*it); if (varyingIt != varyingMap.end() ) { const Varying& var = varyingIt->second; preprocessor.writef(" \\\n\t%s%s %s %s : %s" , arg++ > 0 ? ", " : " " , interpolationDx11(var.m_interpolation.c_str() ) , var.m_type.c_str() , var.m_name.c_str() , var.m_semantics.c_str() ); } } addFragData(preprocessor, input, 0, arg++ > 0); const uint32_t maxRT = d3d > 9 ? BX_COUNTOF(hasFragData) : 4; for (uint32_t ii = 1; ii < BX_COUNTOF(hasFragData); ++ii) { if (ii < maxRT) { if (hasFragData[ii]) { addFragData(preprocessor, input, ii, arg++ > 0); } } else { voidFragData(input, ii); } } if (hasFragDepth) { preprocessor.writef( " \\\n\t%sout float gl_FragDepth : SV_DEPTH" , arg++ > 0 ? ", " : " " ); } if (hasFrontFacing && hlsl >= 3) { preprocessor.writef( " \\\n\t%sfloat __vface : VFACE" , arg++ > 0 ? ", " : " " ); } if (hasPrimitiveId) { if (d3d > 9) { preprocessor.writef( " \\\n\t%suint gl_PrimitiveID : SV_PrimitiveID" , arg++ > 0 ? ", " : " " ); } else { fprintf(stderr, "PrimitiveID builtin is not supported by this D3D9 HLSL.\n"); return EXIT_FAILURE; } } preprocessor.writef( " \\\n\t)\n" ); if (hasFrontFacing) { if (hlsl >= 3) { preprocessor.writef( "#define gl_FrontFacing (__vface <= 0.0)\n" ); } else { preprocessor.writef( "#define gl_FrontFacing false\n" ); } } } else if ('v' == shaderType) { const char* brace = strstr(entry, "{"); if (NULL != brace) { const char* end = bx::strmb(brace, '{', '}'); if (NULL != end) { strins(const_cast<char*>(end), "__RETURN__;\n"); } } preprocessor.writef( "struct Output\n" "{\n" "\tvec4 gl_Position : SV_POSITION;\n" "#define gl_Position _varying_.gl_Position\n" ); for (InOut::const_iterator it = shaderOutputs.begin(), itEnd = shaderOutputs.end(); it != itEnd; ++it) { VaryingMap::const_iterator varyingIt = varyingMap.find(*it); if (varyingIt != varyingMap.end() ) { const Varying& var = varyingIt->second; preprocessor.writef("\t%s %s : %s;\n", var.m_type.c_str(), var.m_name.c_str(), var.m_semantics.c_str() ); preprocessor.writef("#define %s _varying_.%s\n", var.m_name.c_str(), var.m_name.c_str() ); } } preprocessor.writef( "};\n" ); preprocessor.writef("#define void_main() \\\n"); preprocessor.writef("Output main("); bool first = true; for (InOut::const_iterator it = shaderInputs.begin(), itEnd = shaderInputs.end(); it != itEnd; ++it) { VaryingMap::const_iterator varyingIt = varyingMap.find(*it); if (varyingIt != varyingMap.end() ) { const Varying& var = varyingIt->second; preprocessor.writef("%s%s %s : %s\\\n", first ? "" : "\t, ", var.m_type.c_str(), var.m_name.c_str(), var.m_semantics.c_str() ); first = false; } } preprocessor.writef( ") \\\n" "{ \\\n" "\tOutput _varying_;" ); for (InOut::const_iterator it = shaderOutputs.begin(), itEnd = shaderOutputs.end(); it != itEnd; ++it) { VaryingMap::const_iterator varyingIt = varyingMap.find(*it); if (varyingIt != varyingMap.end() ) { const Varying& var = varyingIt->second; preprocessor.writef(" \\\n\t%s = %s;", var.m_name.c_str(), var.m_init.c_str() ); } } preprocessor.writef( "\n#define __RETURN__ \\\n" "\t} \\\n" "\treturn _varying_" ); } } if (preprocessor.run(input) ) { BX_TRACE("Input file: %s", filePath); BX_TRACE("Output file: %s", outFilePath); if (preprocessOnly) { bx::CrtFileWriter writer; if (0 != writer.open(outFilePath) ) { fprintf(stderr, "Unable to open output file '%s'.", outFilePath); return EXIT_FAILURE; } if (0 != glsl) { const char* profile = cmdLine.findOption('p', "profile"); if (NULL == profile) { writef(&writer , "#ifdef GL_ES\n" "precision highp float;\n" "#endif // GL_ES\n\n" ); } } writer.write(preprocessor.m_preprocessed.c_str(), (int32_t)preprocessor.m_preprocessed.size() ); writer.close(); return EXIT_SUCCESS; } { bx::CrtFileWriter* writer = NULL; if (NULL != bin2c) { writer = new Bin2cWriter(bin2c); } else { writer = new bx::CrtFileWriter; } if (0 != writer->open(outFilePath) ) { fprintf(stderr, "Unable to open output file '%s'.", outFilePath); return EXIT_FAILURE; } if ('f' == shaderType) { bx::write(writer, BGFX_CHUNK_MAGIC_FSH); bx::write(writer, inputHash); } else if ('v' == shaderType) { bx::write(writer, BGFX_CHUNK_MAGIC_VSH); bx::write(writer, outputHash); } else { bx::write(writer, BGFX_CHUNK_MAGIC_CSH); bx::write(writer, outputHash); } if (0 != glsl) { std::string code; bool hasTextureLod = NULL != bx::findIdentifierMatch(input, s_ARB_shader_texture_lod /*EXT_shader_texture_lod*/); if (0 == essl) { bx::stringPrintf(code, "#version %s\n", profile); bx::stringPrintf(code , "#define bgfxShadow2D shadow2D\n" "#define bgfxShadow2DProj shadow2DProj\n" ); if (hasTextureLod && 130 > glsl) { bx::stringPrintf(code , "#extension GL_ARB_shader_texture_lod : enable\n" ); } } else { // Pretend that all extensions are available. // This will be stripped later. if (hasTextureLod) { bx::stringPrintf(code , "#extension GL_EXT_shader_texture_lod : enable\n" "#define texture2DLod texture2DLodEXT\n" "#define texture2DProjLod texture2DProjLodEXT\n" "#define textureCubeLod textureCubeLodEXT\n" // "#define texture2DGrad texture2DGradEXT\n" // "#define texture2DProjGrad texture2DProjGradEXT\n" // "#define textureCubeGrad textureCubeGradEXT\n" ); } if (NULL != bx::findIdentifierMatch(input, s_OES_standard_derivatives) ) { bx::stringPrintf(code, "#extension GL_OES_standard_derivatives : enable\n"); } if (NULL != bx::findIdentifierMatch(input, s_OES_texture_3D) ) { bx::stringPrintf(code, "#extension GL_OES_texture_3D : enable\n"); } if (NULL != bx::findIdentifierMatch(input, s_EXT_shadow_samplers) ) { bx::stringPrintf(code , "#extension GL_EXT_shadow_samplers : enable\n" "#define shadow2D shadow2DEXT\n" "#define shadow2DProj shadow2DProjEXT\n" ); } if (NULL != bx::findIdentifierMatch(input, "gl_FragDepth") ) { bx::stringPrintf(code , "#extension GL_EXT_frag_depth : enable\n" "#define gl_FragDepth gl_FragDepthEXT\n" ); } } code += preprocessor.m_preprocessed; compiled = compileGLSLShader(cmdLine, essl, code, writer); } else { if (d3d > 9) { compiled = compileHLSLShaderDx11(cmdLine, preprocessor.m_preprocessed, writer); } else { compiled = compileHLSLShaderDx9(cmdLine, preprocessor.m_preprocessed, writer); } } writer->close(); delete writer; } if (compiled) { if (depends) { std::string ofp = outFilePath; ofp += ".d"; bx::CrtFileWriter writer; if (0 == writer.open(ofp.c_str() ) ) { writef(&writer, "%s : %s\n", outFilePath, preprocessor.m_depends.c_str() ); writer.close(); } } } } } } delete [] data; } if (compiled) { return EXIT_SUCCESS; } remove(outFilePath); fprintf(stderr, "Failed to build shader.\n"); return EXIT_FAILURE; }