Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gwn_vertex_format.c « src « gawain « intern - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 122de12ec6ef8a7330a689fa6f2cb2370ae9fe48 (plain)
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

// Gawain vertex format
//
// This code is part of the Gawain library, with modifications
// specific to integration with Blender.
//
// Copyright 2016 Mike Erwin
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
// the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

#include "gwn_vertex_format.h"
#include "gwn_vertex_format_private.h"
#include <stddef.h>
#include <string.h>

#define PACK_DEBUG 0

#if PACK_DEBUG
  #include <stdio.h>
#endif

void GWN_vertformat_clear(Gwn_VertFormat* format)
	{
#if TRUST_NO_ONE
	memset(format, 0, sizeof(Gwn_VertFormat));
#else
	format->attr_len = 0;
	format->packed = false;
	format->name_offset = 0;
	format->name_len = 0;

	for (unsigned i = 0; i < GWN_VERT_ATTR_MAX_LEN; i++)
		format->attribs[i].name_len = 0;
#endif
	}

void GWN_vertformat_copy(Gwn_VertFormat* dest, const Gwn_VertFormat* src)
	{
	// copy regular struct fields
	memcpy(dest, src, sizeof(Gwn_VertFormat));

	for (unsigned i = 0; i < dest->attr_len; i++)
		for (unsigned j = 0; j < dest->attribs[i].name_len; j++)
			dest->attribs[i].name[j] = (char *)dest + (src->attribs[i].name[j] - ((char *)src));
	}

static GLenum convert_comp_type_to_gl(Gwn_VertCompType type)
	{
	static const GLenum table[] = {
		[GWN_COMP_I8] = GL_BYTE,
		[GWN_COMP_U8] = GL_UNSIGNED_BYTE,
		[GWN_COMP_I16] = GL_SHORT,
		[GWN_COMP_U16] = GL_UNSIGNED_SHORT,
		[GWN_COMP_I32] = GL_INT,
		[GWN_COMP_U32] = GL_UNSIGNED_INT,

		[GWN_COMP_F32] = GL_FLOAT,

		[GWN_COMP_I10] = GL_INT_2_10_10_10_REV
		};
	return table[type];
	}

static unsigned comp_sz(Gwn_VertCompType type)
	{
#if TRUST_NO_ONE
	assert(type <= GWN_COMP_F32); // other types have irregular sizes (not bytes)
#endif

	const GLubyte sizes[] = {1,1,2,2,4,4,4};
	return sizes[type];
	}

static unsigned attrib_sz(const Gwn_VertAttr *a)
	{
	if (a->comp_type == GWN_COMP_I10)
		return 4; // always packed as 10_10_10_2

	return a->comp_len * comp_sz(a->comp_type);
	}

static unsigned attrib_align(const Gwn_VertAttr *a)
	{
	if (a->comp_type == GWN_COMP_I10)
		return 4; // always packed as 10_10_10_2

	unsigned c = comp_sz(a->comp_type);
	if (a->comp_len == 3 && c <= 2)
		return 4 * c; // AMD HW can't fetch these well, so pad it out (other vendors too?)
	else
		return c; // most fetches are ok if components are naturally aligned
	}

unsigned vertex_buffer_size(const Gwn_VertFormat* format, unsigned vertex_len)
	{
#if TRUST_NO_ONE
	assert(format->packed && format->stride > 0);
#endif

	return format->stride * vertex_len;
	}

static const char* copy_attrib_name(Gwn_VertFormat* format, const char* name)
	{
	// strncpy does 110% of what we need; let's do exactly 100%
	char* name_copy = format->names + format->name_offset;
	unsigned available = GWN_VERT_ATTR_NAMES_BUF_LEN - format->name_offset;
	bool terminated = false;

	for (unsigned i = 0; i < available; ++i)
		{
		const char c = name[i];
		name_copy[i] = c;
		if (c == '\0')
			{
			terminated = true;
			format->name_offset += (i + 1);
			break;
			}
		}

#if TRUST_NO_ONE
	assert(terminated);
	assert(format->name_offset <= GWN_VERT_ATTR_NAMES_BUF_LEN);
#else
	(void)terminated;
#endif

	return name_copy;
	}

unsigned GWN_vertformat_attr_add(Gwn_VertFormat* format, const char* name, Gwn_VertCompType comp_type, unsigned comp_len, Gwn_VertFetchMode fetch_mode)
	{
#if TRUST_NO_ONE
	assert(format->name_len < GWN_VERT_ATTR_MAX_LEN); // there's room for more
	assert(format->attr_len < GWN_VERT_ATTR_MAX_LEN); // there's room for more
	assert(!format->packed); // packed means frozen/locked
	assert((comp_len >= 1 && comp_len <= 4) || comp_len == 8 || comp_len == 12 || comp_len == 16);
	switch (comp_type)
		{
		case GWN_COMP_F32:
			// float type can only kept as float
			assert(fetch_mode == GWN_FETCH_FLOAT);
			break;
		case GWN_COMP_I10:
			// 10_10_10 format intended for normals (xyz) or colors (rgb)
			// extra component packed.w can be manually set to { -2, -1, 0, 1 }
			assert(comp_len == 3 || comp_len == 4);
			assert(fetch_mode == GWN_FETCH_INT_TO_FLOAT_UNIT); // not strictly required, may relax later
			break;
		default:
			// integer types can be kept as int or converted/normalized to float
			assert(fetch_mode != GWN_FETCH_FLOAT);
			// only support float matrices (see Batch_update_program_bindings)
			assert(comp_len != 8 && comp_len != 12 && comp_len != 16);
		}
#endif
	format->name_len++; // multiname support

	const unsigned attrib_id = format->attr_len++;
	Gwn_VertAttr* attrib = format->attribs + attrib_id;

	attrib->name[attrib->name_len++] = copy_attrib_name(format, name);
	attrib->comp_type = comp_type;
	attrib->gl_comp_type = convert_comp_type_to_gl(comp_type);
	attrib->comp_len = (comp_type == GWN_COMP_I10) ? 4 : comp_len; // system needs 10_10_10_2 to be 4 or BGRA
	attrib->sz = attrib_sz(attrib);
	attrib->offset = 0; // offsets & stride are calculated later (during pack)
	attrib->fetch_mode = fetch_mode;

	return attrib_id;
	}

void GWN_vertformat_alias_add(Gwn_VertFormat* format, const char* alias)
	{
	Gwn_VertAttr* attrib = format->attribs + (format->attr_len - 1);
#if TRUST_NO_ONE
	assert(format->name_len < GWN_VERT_ATTR_MAX_LEN); // there's room for more
	assert(attrib->name_len < GWN_VERT_ATTR_MAX_NAMES);
#endif
	format->name_len++; // multiname support
	attrib->name[attrib->name_len++] = copy_attrib_name(format, alias);
	}

unsigned padding(unsigned offset, unsigned alignment)
	{
	const unsigned mod = offset % alignment;
	return (mod == 0) ? 0 : (alignment - mod);
	}

#if PACK_DEBUG
static void show_pack(unsigned a_idx, unsigned sz, unsigned pad)
	{
	const char c = 'A' + a_idx;
	for (unsigned i = 0; i < pad; ++i)
		putchar('-');
	for (unsigned i = 0; i < sz; ++i)
		putchar(c);
	}
#endif

void VertexFormat_pack(Gwn_VertFormat* format)
	{
	// for now, attributes are packed in the order they were added,
	// making sure each attrib is naturally aligned (add padding where necessary)

	// later we can implement more efficient packing w/ reordering
	// (keep attrib ID order, adjust their offsets to reorder in buffer)

	// TODO:
	// realloc just enough to hold the final combo string. And just enough to
	// hold used attribs, not all 16.

	Gwn_VertAttr* a0 = format->attribs + 0;
	a0->offset = 0;
	unsigned offset = a0->sz;

#if PACK_DEBUG
	show_pack(0, a0->sz, 0);
#endif

	for (unsigned a_idx = 1; a_idx < format->attr_len; ++a_idx)
		{
		Gwn_VertAttr* a = format->attribs + a_idx;
		unsigned mid_padding = padding(offset, attrib_align(a));
		offset += mid_padding;
		a->offset = offset;
		offset += a->sz;

#if PACK_DEBUG
		show_pack(a_idx, a->sz, mid_padding);
#endif
		}

	unsigned end_padding = padding(offset, attrib_align(a0));

#if PACK_DEBUG
	show_pack(0, 0, end_padding);
	putchar('\n');
#endif

	format->stride = offset + end_padding;
	format->packed = true;
	}


// OpenGL ES packs in a different order as desktop GL but component conversion is the same.
// Of the code here, only struct Gwn_PackedNormal needs to change.

#define SIGNED_INT_10_MAX  511
#define SIGNED_INT_10_MIN -512

static int clampi(int x, int min_allowed, int max_allowed)
	{
#if TRUST_NO_ONE
	assert(min_allowed <= max_allowed);
#endif

	if (x < min_allowed)
		return min_allowed;
	else if (x > max_allowed)
		return max_allowed;
	else
		return x;
	}

static int quantize(float x)
	{
	int qx = x * 511.0f;
	return clampi(qx, SIGNED_INT_10_MIN, SIGNED_INT_10_MAX);
	}

static int convert_i16(short x)
	{
	// 16-bit signed --> 10-bit signed
	return x >> 6;
	// TODO: round?
	}

Gwn_PackedNormal GWN_normal_convert_i10_v3(const float data[3])
	{
	Gwn_PackedNormal n = { .x = quantize(data[0]), .y = quantize(data[1]), .z = quantize(data[2]) };
	return n;
	}

Gwn_PackedNormal GWN_normal_convert_i10_s3(const short data[3])
	{
	Gwn_PackedNormal n = { .x = convert_i16(data[0]), .y = convert_i16(data[1]), .z = convert_i16(data[2]) };
	return n;
	}