1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

thirdparty: update all sokol and fontstash headers with their upstream versions (#16940)

This commit is contained in:
Delyan Angelov 2023-01-11 11:29:38 +02:00 committed by GitHub
parent d1306ffcf5
commit e854051c1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 3937 additions and 1692 deletions

6
examples/sokol/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
02_cubes_glsl/cube_glsl.h
03_march_tracing_glsl/rt_glsl.h
04_multi_shader_glsl/rt_glsl_march.h
04_multi_shader_glsl/rt_glsl_puppy.h
05_instancing_glsl/rt_glsl_instancing.h
06_obj_viewer/gouraud.h

View File

@ -1,5 +1,5 @@
// stb_truetype.h - v1.24 - public domain
// authored from 2009-2020 by Sean Barrett / RAD Game Tools
// stb_truetype.h - v1.26 - public domain
// authored from 2009-2021 by Sean Barrett / RAD Game Tools
//
// =======================================================================
//
@ -58,6 +58,8 @@
//
// VERSION HISTORY
//
// 1.26 (2021-08-28) fix broken rasterizer
// 1.25 (2021-07-11) many fixes
// 1.24 (2020-02-05) fix warning
// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
@ -270,8 +272,8 @@
//// SAMPLE PROGRAMS
////
//
// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
//
// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless.
// See "tests/truetype_demo_win32.c" for a complete version.
#if 0
#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
#include "stb_truetype.h"
@ -297,6 +299,8 @@ void my_stbtt_initfont(void)
void my_stbtt_print(float x, float y, char *text)
{
// assume orthographic projection with units = screen pixels, origin at top left
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, ftex);
glBegin(GL_QUADS);
@ -304,10 +308,10 @@ void my_stbtt_print(float x, float y, char *text)
if (*text >= 32 && *text < 128) {
stbtt_aligned_quad q;
stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0);
glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0);
glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1);
glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1);
}
++text;
}
@ -853,6 +857,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s
STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
// frees the data allocated above
STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl);
STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg);
STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg);
// fills svg with the character's SVG data.
@ -1539,12 +1544,12 @@ STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codep
search += 2;
{
stbtt_uint16 offset, start;
stbtt_uint16 offset, start, last;
stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);
STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
if (unicode_codepoint < start)
last = ttUSHORT(data + endCount + 2*item);
if (unicode_codepoint < start || unicode_codepoint > last)
return 0;
offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
@ -1871,7 +1876,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s
if (comp_verts) STBTT_free(comp_verts, info->userdata);
return 0;
}
if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
if (vertices) STBTT_free(vertices, info->userdata);
vertices = tmp;
@ -2134,7 +2139,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st
subrs = stbtt__cid_get_glyph_subrs(info, glyph_index);
has_subrs = 1;
}
// fallthrough
// FALLTHROUGH
case 0x1D: // callgsubr
if (sp < 1) return STBTT__CSERR("call(g|)subr stack");
v = (int) s[--sp];
@ -2239,7 +2244,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st
} break;
default:
if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254))
if (b0 != 255 && b0 != 28 && b0 < 32)
return STBTT__CSERR("reserved operator");
// push immediate
@ -2351,7 +2356,7 @@ STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningent
return length;
}
static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
{
stbtt_uint8 *data = info->data + info->kern;
stbtt_uint32 needle, straw;
@ -2381,243 +2386,225 @@ static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph
return 0;
}
static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)
static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)
{
stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
switch(coverageFormat) {
case 1: {
stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
switch (coverageFormat) {
case 1: {
stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
// Binary search.
stbtt_int32 l=0, r=glyphCount-1, m;
int straw, needle=glyph;
while (l <= r) {
stbtt_uint8 *glyphArray = coverageTable + 4;
stbtt_uint16 glyphID;
m = (l + r) >> 1;
glyphID = ttUSHORT(glyphArray + 2 * m);
straw = glyphID;
if (needle < straw)
r = m - 1;
else if (needle > straw)
l = m + 1;
else {
return m;
}
// Binary search.
stbtt_int32 l=0, r=glyphCount-1, m;
int straw, needle=glyph;
while (l <= r) {
stbtt_uint8 *glyphArray = coverageTable + 4;
stbtt_uint16 glyphID;
m = (l + r) >> 1;
glyphID = ttUSHORT(glyphArray + 2 * m);
straw = glyphID;
if (needle < straw)
r = m - 1;
else if (needle > straw)
l = m + 1;
else {
return m;
}
} break;
}
break;
}
case 2: {
stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
stbtt_uint8 *rangeArray = coverageTable + 4;
case 2: {
stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
stbtt_uint8 *rangeArray = coverageTable + 4;
// Binary search.
stbtt_int32 l=0, r=rangeCount-1, m;
int strawStart, strawEnd, needle=glyph;
while (l <= r) {
stbtt_uint8 *rangeRecord;
m = (l + r) >> 1;
rangeRecord = rangeArray + 6 * m;
strawStart = ttUSHORT(rangeRecord);
strawEnd = ttUSHORT(rangeRecord + 2);
if (needle < strawStart)
r = m - 1;
else if (needle > strawEnd)
l = m + 1;
else {
stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
return startCoverageIndex + glyph - strawStart;
}
// Binary search.
stbtt_int32 l=0, r=rangeCount-1, m;
int strawStart, strawEnd, needle=glyph;
while (l <= r) {
stbtt_uint8 *rangeRecord;
m = (l + r) >> 1;
rangeRecord = rangeArray + 6 * m;
strawStart = ttUSHORT(rangeRecord);
strawEnd = ttUSHORT(rangeRecord + 2);
if (needle < strawStart)
r = m - 1;
else if (needle > strawEnd)
l = m + 1;
else {
stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
return startCoverageIndex + glyph - strawStart;
}
} break;
}
break;
}
default: {
// There are no other cases.
STBTT_assert(0);
} break;
}
default: return -1; // unsupported
}
return -1;
return -1;
}
static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)
{
stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
switch(classDefFormat)
{
case 1: {
stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
switch (classDefFormat)
{
case 1: {
stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
break;
}
classDefTable = classDef1ValueArray + 2 * glyphCount;
} break;
case 2: {
stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
stbtt_uint8 *classRangeRecords = classDefTable + 4;
case 2: {
stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
stbtt_uint8 *classRangeRecords = classDefTable + 4;
// Binary search.
stbtt_int32 l=0, r=classRangeCount-1, m;
int strawStart, strawEnd, needle=glyph;
while (l <= r) {
stbtt_uint8 *classRangeRecord;
m = (l + r) >> 1;
classRangeRecord = classRangeRecords + 6 * m;
strawStart = ttUSHORT(classRangeRecord);
strawEnd = ttUSHORT(classRangeRecord + 2);
if (needle < strawStart)
r = m - 1;
else if (needle > strawEnd)
l = m + 1;
else
return (stbtt_int32)ttUSHORT(classRangeRecord + 4);
}
break;
}
// Binary search.
stbtt_int32 l=0, r=classRangeCount-1, m;
int strawStart, strawEnd, needle=glyph;
while (l <= r) {
stbtt_uint8 *classRangeRecord;
m = (l + r) >> 1;
classRangeRecord = classRangeRecords + 6 * m;
strawStart = ttUSHORT(classRangeRecord);
strawEnd = ttUSHORT(classRangeRecord + 2);
if (needle < strawStart)
r = m - 1;
else if (needle > strawEnd)
l = m + 1;
else
return (stbtt_int32)ttUSHORT(classRangeRecord + 4);
}
default:
return -1; // Unsupported definition type, return an error.
}
classDefTable = classRangeRecords + 6 * classRangeCount;
} break;
default: {
// There are no other cases.
STBTT_assert(0);
} break;
}
return -1;
// "All glyphs not assigned to a class fall into class 0". (OpenType spec)
return 0;
}
// Define to STBTT_assert(x) if you want to break on unimplemented formats.
#define STBTT_GPOS_TODO_assert(x)
static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
{
stbtt_uint16 lookupListOffset;
stbtt_uint8 *lookupList;
stbtt_uint16 lookupCount;
stbtt_uint8 *data;
stbtt_int32 i;
stbtt_uint16 lookupListOffset;
stbtt_uint8 *lookupList;
stbtt_uint16 lookupCount;
stbtt_uint8 *data;
stbtt_int32 i, sti;
if (!info->gpos) return 0;
if (!info->gpos) return 0;
data = info->data + info->gpos;
data = info->data + info->gpos;
if (ttUSHORT(data+0) != 1) return 0; // Major version 1
if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
if (ttUSHORT(data+0) != 1) return 0; // Major version 1
if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
lookupListOffset = ttUSHORT(data+8);
lookupList = data + lookupListOffset;
lookupCount = ttUSHORT(lookupList);
lookupListOffset = ttUSHORT(data+8);
lookupList = data + lookupListOffset;
lookupCount = ttUSHORT(lookupList);
for (i=0; i<lookupCount; ++i) {
stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
stbtt_uint8 *lookupTable = lookupList + lookupOffset;
for (i=0; i<lookupCount; ++i) {
stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
stbtt_uint8 *lookupTable = lookupList + lookupOffset;
stbtt_uint16 lookupType = ttUSHORT(lookupTable);
stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
stbtt_uint8 *subTableOffsets = lookupTable + 6;
switch(lookupType) {
case 2: { // Pair Adjustment Positioning Subtable
stbtt_int32 sti;
for (sti=0; sti<subTableCount; sti++) {
stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
stbtt_uint8 *table = lookupTable + subtableOffset;
stbtt_uint16 posFormat = ttUSHORT(table);
stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
if (coverageIndex == -1) continue;
stbtt_uint16 lookupType = ttUSHORT(lookupTable);
stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
stbtt_uint8 *subTableOffsets = lookupTable + 6;
if (lookupType != 2) // Pair Adjustment Positioning Subtable
continue;
switch (posFormat) {
case 1: {
stbtt_int32 l, r, m;
int straw, needle;
stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
stbtt_int32 valueRecordPairSizeInBytes = 2;
stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
stbtt_uint8 *pairValueTable = table + pairPosOffset;
stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
stbtt_uint8 *pairValueArray = pairValueTable + 2;
// TODO: Support more formats.
STBTT_GPOS_TODO_assert(valueFormat1 == 4);
if (valueFormat1 != 4) return 0;
STBTT_GPOS_TODO_assert(valueFormat2 == 0);
if (valueFormat2 != 0) return 0;
for (sti=0; sti<subTableCount; sti++) {
stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
stbtt_uint8 *table = lookupTable + subtableOffset;
stbtt_uint16 posFormat = ttUSHORT(table);
stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
if (coverageIndex == -1) continue;
STBTT_assert(coverageIndex < pairSetCount);
STBTT__NOTUSED(pairSetCount);
switch (posFormat) {
case 1: {
stbtt_int32 l, r, m;
int straw, needle;
stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?
stbtt_int32 valueRecordPairSizeInBytes = 2;
stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
stbtt_uint8 *pairValueTable = table + pairPosOffset;
stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
stbtt_uint8 *pairValueArray = pairValueTable + 2;
needle=glyph2;
r=pairValueCount-1;
l=0;
if (coverageIndex >= pairSetCount) return 0;
// Binary search.
while (l <= r) {
stbtt_uint16 secondGlyph;
stbtt_uint8 *pairValue;
m = (l + r) >> 1;
pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
secondGlyph = ttUSHORT(pairValue);
straw = secondGlyph;
if (needle < straw)
r = m - 1;
else if (needle > straw)
l = m + 1;
else {
stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
return xAdvance;
}
}
} break;
needle=glyph2;
r=pairValueCount-1;
l=0;
case 2: {
stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
// Binary search.
while (l <= r) {
stbtt_uint16 secondGlyph;
stbtt_uint8 *pairValue;
m = (l + r) >> 1;
pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
secondGlyph = ttUSHORT(pairValue);
straw = secondGlyph;
if (needle < straw)
r = m - 1;
else if (needle > straw)
l = m + 1;
else {
stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
return xAdvance;
}
}
} else
return 0;
break;
}
stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);
stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);
int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);
int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);
case 2: {
stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?
stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);
stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);
int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);
int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);
stbtt_uint16 class1Count = ttUSHORT(table + 12);
stbtt_uint16 class2Count = ttUSHORT(table + 14);
STBTT_assert(glyph1class < class1Count);
STBTT_assert(glyph2class < class2Count);
stbtt_uint16 class1Count = ttUSHORT(table + 12);
stbtt_uint16 class2Count = ttUSHORT(table + 14);
stbtt_uint8 *class1Records, *class2Records;
stbtt_int16 xAdvance;
// TODO: Support more formats.
STBTT_GPOS_TODO_assert(valueFormat1 == 4);
if (valueFormat1 != 4) return 0;
STBTT_GPOS_TODO_assert(valueFormat2 == 0);
if (valueFormat2 != 0) return 0;
if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed
if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed
if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) {
stbtt_uint8 *class1Records = table + 16;
stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count);
stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class);
return xAdvance;
}
} break;
default: {
// There are no other cases.
STBTT_assert(0);
break;
};
}
}
break;
};
class1Records = table + 16;
class2Records = class1Records + 2 * (glyph1class * class2Count);
xAdvance = ttSHORT(class2Records + 2 * glyph2class);
return xAdvance;
} else
return 0;
break;
}
default:
// TODO: Implement other stuff.
break;
}
}
return 0; // Unsupported position format
}
}
}
return 0;
return 0;
}
STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2)
@ -3075,6 +3062,23 @@ static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edg
}
}
static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width)
{
STBTT_assert(top_width >= 0);
STBTT_assert(bottom_width >= 0);
return (top_width + bottom_width) / 2.0f * height;
}
static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1)
{
return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0);
}
static float stbtt__sized_triangle_area(float height, float width)
{
return height * width / 2;
}
static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
{
float y_bottom = y_top+1;
@ -3129,13 +3133,13 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
float height;
// simple case, only spans one pixel
int x = (int) x_top;
height = sy1 - sy0;
height = (sy1 - sy0) * e->direction;
STBTT_assert(x >= 0 && x < len);
scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height;
scanline_fill[x] += e->direction * height; // everything right of this pixel is filled
scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);
scanline_fill[x] += height; // everything right of this pixel is filled
} else {
int x,x1,x2;
float y_crossing, step, sign, area;
float y_crossing, y_final, step, sign, area;
// covers 2+ pixels
if (x_top > x_bottom) {
// flip scanline vertically; signed area is the same
@ -3148,29 +3152,79 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
dy = -dy;
t = x0, x0 = xb, xb = t;
}
STBTT_assert(dy >= 0);
STBTT_assert(dx >= 0);
x1 = (int) x_top;
x2 = (int) x_bottom;
// compute intersection with y axis at x1+1
y_crossing = (x1+1 - x0) * dy + y_top;
y_crossing = y_top + dy * (x1+1 - x0);
// compute intersection with y axis at x2
y_final = y_top + dy * (x2 - x0);
// x1 x_top x2 x_bottom
// y_top +------|-----+------------+------------+--------|---+------------+
// | | | | | |
// | | | | | |
// sy0 | Txxxxx|............|............|............|............|
// y_crossing | *xxxxx.......|............|............|............|
// | | xxxxx..|............|............|............|
// | | /- xx*xxxx........|............|............|
// | | dy < | xxxxxx..|............|............|
// y_final | | \- | xx*xxx.........|............|
// sy1 | | | | xxxxxB...|............|
// | | | | | |
// | | | | | |
// y_bottom +------------+------------+------------+------------+------------+
//
// goal is to measure the area covered by '.' in each pixel
// if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
// @TODO: maybe test against sy1 rather than y_bottom?
if (y_crossing > y_bottom)
y_crossing = y_bottom;
sign = e->direction;
// area of the rectangle covered from y0..y_crossing
area = sign * (y_crossing-sy0);
// area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2);
step = sign * dy;
// area of the rectangle covered from sy0..y_crossing
area = sign * (y_crossing-sy0);
// area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)
scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top);
// check if final y_crossing is blown up; no test case for this
if (y_final > y_bottom) {
y_final = y_bottom;
dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom
}
// in second pixel, area covered by line segment found in first pixel
// is always a rectangle 1 wide * the height of that line segment; this
// is exactly what the variable 'area' stores. it also gets a contribution
// from the line segment within it. the THIRD pixel will get the first
// pixel's rectangle contribution, the second pixel's rectangle contribution,
// and its own contribution. the 'own contribution' is the same in every pixel except
// the leftmost and rightmost, a trapezoid that slides down in each pixel.
// the second pixel's contribution to the third pixel will be the
// rectangle 1 wide times the height change in the second pixel, which is dy.
step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x,
// which multiplied by 1-pixel-width is how much pixel area changes for each step in x
// so the area advances by 'step' every time
for (x = x1+1; x < x2; ++x) {
scanline[x] += area + step/2;
scanline[x] += area + step/2; // area of trapezoid is 1*step/2
area += step;
}
y_crossing += dy * (x2 - (x1+1));
STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down
STBTT_assert(sy1 > y_final-0.01f);
STBTT_assert(STBTT_fabs(area) <= 1.01f);
scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing);
// area covered in the last pixel is the rectangle from all the pixels to the left,
// plus the trapezoid filled by the line segment in this pixel all the way to the right edge
scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f);
// the rest of the line is filled based on the total height of the line segment in this pixel
scanline_fill[x2] += sign * (sy1-sy0);
}
} else {
@ -3178,6 +3232,9 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
// clipping logic. since this does not match the intended use
// of this library, we use a different, very slow brute
// force implementation
// note though that this does happen some of the time because
// x_top and x_bottom can be extrapolated at the top & bottom of
// the shape and actually lie outside the bounding box
int x;
for (x=0; x < len; ++x) {
// cases:
@ -4414,15 +4471,14 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
float y_frac;
int winding = 0;
orig[0] = x;
orig[1] = y;
// make sure y never passes through a vertex of the shape
y_frac = (float) STBTT_fmod(y, 1.0f);
if (y_frac < 0.01f)
y += 0.01f;
else if (y_frac > 0.99f)
y -= 0.01f;
orig[0] = x;
orig[1] = y;
// test a ray from (-infinity,y) to (x,y)
@ -4484,35 +4540,35 @@ static float stbtt__cuberoot( float x )
return (float) STBTT_pow( x,1.0f/3.0f);
}
// x^3 + c*x^2 + b*x + a = 0
// x^3 + a*x^2 + b*x + c = 0
static int stbtt__solve_cubic(float a, float b, float c, float* r)
{
float s = -a / 3;
float p = b - a*a / 3;
float q = a * (2*a*a - 9*b) / 27 + c;
float s = -a / 3;
float p = b - a*a / 3;
float q = a * (2*a*a - 9*b) / 27 + c;
float p3 = p*p*p;
float d = q*q + 4*p3 / 27;
if (d >= 0) {
float z = (float) STBTT_sqrt(d);
float u = (-q + z) / 2;
float v = (-q - z) / 2;
u = stbtt__cuberoot(u);
v = stbtt__cuberoot(v);
r[0] = s + u + v;
return 1;
} else {
float u = (float) STBTT_sqrt(-p/3);
float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative
float m = (float) STBTT_cos(v);
float d = q*q + 4*p3 / 27;
if (d >= 0) {
float z = (float) STBTT_sqrt(d);
float u = (-q + z) / 2;
float v = (-q - z) / 2;
u = stbtt__cuberoot(u);
v = stbtt__cuberoot(v);
r[0] = s + u + v;
return 1;
} else {
float u = (float) STBTT_sqrt(-p/3);
float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative
float m = (float) STBTT_cos(v);
float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f;
r[0] = s + u * 2 * m;
r[1] = s - u * (m + n);
r[2] = s - u * (m - n);
r[0] = s + u * 2 * m;
r[1] = s - u * (m + n);
r[2] = s - u * (m - n);
//STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe?
//STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f);
//STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f);
return 3;
return 3;
}
}
@ -4589,18 +4645,17 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
for (i=0; i < num_verts; ++i) {
float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
// check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve
float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
if (dist2 < min_dist*min_dist)
min_dist = (float) STBTT_sqrt(dist2);
if (verts[i].type == STBTT_vline) {
if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) {
float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y;
float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
if (dist2 < min_dist*min_dist)
min_dist = (float) STBTT_sqrt(dist2);
// coarse culling against bbox
//if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist &&
// sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist)
float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i];
dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i];
STBTT_assert(i != 0);
if (dist < min_dist) {
// check position along line
@ -4627,7 +4682,8 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
float ax = x1-x0, ay = y1-y0;
float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
float mx = x0 - sx, my = y0 - sy;
float res[3],px,py,t,it;
float res[3] = {0.f,0.f,0.f};
float px,py,t,it,dist2;
float a_inv = precompute[i];
if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula
float a = 3*(ax*bx + ay*by);
@ -4654,6 +4710,10 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc
float d = (mx*ax+my*ay) * a_inv;
num = stbtt__solve_cubic(b, c, d, res);
}
dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
if (dist2 < min_dist*min_dist)
min_dist = (float) STBTT_sqrt(dist2);
if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) {
t = res[0], it = 1.0f - t;
px = it*it*x0 + 2*t*it*x1 + t*t*x2;
@ -4913,6 +4973,12 @@ STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const
// FULL VERSION HISTORY
//
// 1.25 (2021-07-11) many fixes
// 1.24 (2020-02-05) fix warning
// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
// 1.21 (2019-02-25) fix warning
// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()
// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod
// 1.18 (2018-01-29) add missing function
// 1.17 (2017-07-23) make more arguments const; doc fix

File diff suppressed because it is too large Load Diff

View File

@ -17,9 +17,6 @@
SOKOL_DUMMY_BACKEND - use a dummy backend
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
SOKOL_LOG(msg) - your own logging function (default: puts(msg))
SOKOL_MALLOC(s) - your own malloc() implementation (default: malloc(s))
SOKOL_FREE(p) - your own free() implementation (default: free(p))
SOKOL_AUDIO_API_DECL- public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_AUDIO_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -)
@ -316,6 +313,8 @@
"Blob URLs": https://www.html5rocks.com/en/tutorials/workers/basics/
Also see: https://blog.paul.cx/post/a-wait-free-spsc-ringbuffer-for-the-web/
THE COREAUDIO BACKEND
=====================
The CoreAudio backend is selected on macOS and iOS (__APPLE__ is defined).
@ -365,6 +364,62 @@
header must be present (usually both are installed with some sort
of ALSA development package).
MEMORY ALLOCATION OVERRIDE
==========================
You can override the memory allocation functions at initialization time
like this:
void* my_alloc(size_t size, void* user_data) {
return malloc(size);
}
void my_free(void* ptr, void* user_data) {
free(ptr);
}
...
saudio_setup(&(saudio_desc){
// ...
.allocator = {
.alloc = my_alloc,
.free = my_free,
.user_data = ...,
}
});
...
If no overrides are provided, malloc and free will be used.
This only affects memory allocation calls done by sokol_audio.h
itself though, not any allocations in OS libraries.
Memory allocation will only happen on the same thread where saudio_setup()
was called, so you don't need to worry about thread-safety.
LOG FUNCTION OVERRIDE
=====================
You can override the log function at initialization time like this:
void my_log(const char* message, void* user_data) {
printf("saudio says: \s\n", message);
}
...
saudio_setup(&(saudio_desc){
// ...
.logger = {
.log_cb = my_log,
.user_data = ...,
}
});
...
If no overrides are provided, puts will be used on most platforms.
On Android, __android_log_write will be used instead.
LICENSE
=======
@ -392,6 +447,7 @@
distribution.
*/
#define SOKOL_AUDIO_INCLUDED (1)
#include <stddef.h> // size_t
#include <stdint.h>
#include <stdbool.h>
@ -412,15 +468,42 @@
extern "C" {
#endif
/*
saudio_allocator
Used in saudio_desc to provide custom memory-alloc and -free functions
to sokol_audio.h. If memory management should be overridden, both the
alloc and free function must be provided (e.g. it's not valid to
override one function but not the other).
*/
typedef struct saudio_allocator {
void* (*alloc)(size_t size, void* user_data);
void (*free)(void* ptr, void* user_data);
void* user_data;
} saudio_allocator;
/*
saudio_logger
Used in saudio_desc to provide custom log callbacks to sokol_audio.h.
Default behavior is SOKOL_LOG(message).
*/
typedef struct saudio_logger {
void (*log_cb)(const char* message, void* user_data);
void* user_data;
} saudio_logger;
typedef struct saudio_desc {
int sample_rate; /* requested sample rate */
int num_channels; /* number of channels, default: 1 (mono) */
int buffer_frames; /* number of frames in streaming buffer */
int packet_frames; /* number of frames in a packet */
int num_packets; /* number of packets in packet queue */
void (*stream_cb)(float* buffer, int num_frames, int num_channels); /* optional streaming callback (no user data) */
void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data); /*... and with user data */
void* user_data; /* optional user data argument for stream_userdata_cb */
int sample_rate; // requested sample rate
int num_channels; // number of channels, default: 1 (mono)
int buffer_frames; // number of frames in streaming buffer
int packet_frames; // number of frames in a packet
int num_packets; // number of packets in packet queue
void (*stream_cb)(float* buffer, int num_frames, int num_channels); // optional streaming callback (no user data)
void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data); //... and with user data
void* user_data; // optional user data argument for stream_userdata_cb
saudio_allocator allocator; // optional allocation override functions
saudio_logger logger; // optional log override functions
} saudio_desc;
/* setup sokol-audio */
@ -458,6 +541,12 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc);
/*=== IMPLEMENTATION =========================================================*/
#ifdef SOKOL_AUDIO_IMPL
#define SOKOL_AUDIO_IMPL_INCLUDED (1)
#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE)
#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use saudio_desc.allocator to override memory allocation functions"
#endif
#include <stdlib.h> // alloc, free
#include <string.h> // memset, memcpy
#include <stddef.h> // size_t
@ -466,24 +555,26 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc);
#endif
#ifndef SOKOL_DEBUG
#ifndef NDEBUG
#define SOKOL_DEBUG (1)
#define SOKOL_DEBUG
#endif
#endif
#ifndef SOKOL_ASSERT
#include <assert.h>
#define SOKOL_ASSERT(c) assert(c)
#endif
#ifndef SOKOL_MALLOC
#include <stdlib.h>
#define SOKOL_MALLOC(s) malloc(s)
#define SOKOL_FREE(p) free(p)
#endif
#ifndef SOKOL_LOG
#ifdef SOKOL_DEBUG
#include <stdio.h>
#define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); }
#else
#define SOKOL_LOG(s)
#if !defined(SOKOL_DEBUG)
#define SAUDIO_LOG(s)
#else
#define SAUDIO_LOG(s) _saudio_log(s)
#ifndef SOKOL_LOG
#if defined(__ANDROID__)
#include <android/log.h>
#define SOKOL_LOG(s) __android_log_write(ANDROID_LOG_INFO, "SOKOL_AUDIO", s)
#else
#include <stdio.h>
#define SOKOL_LOG(s) puts(s)
#endif
#endif
#endif
@ -670,6 +761,9 @@ typedef OSStatus _saudio_OSStatus;
#define _saudio_kAudioFormatFlagIsPacked (kAudioFormatFlagIsPacked)
#else
#ifdef __cplusplus
extern "C" {
#endif
// embedded AudioToolbox declarations
typedef uint32_t _saudio_AudioFormatID;
@ -745,6 +839,11 @@ extern _saudio_OSStatus AudioQueueAllocateBuffer(_saudio_AudioQueueRef inAQ, uin
extern _saudio_OSStatus AudioQueueEnqueueBuffer(_saudio_AudioQueueRef inAQ, _saudio_AudioQueueBufferRef inBuffer, uint32_t inNumPacketDescs, const _saudio_AudioStreamPacketDescription* inPacketDescs);
extern _saudio_OSStatus AudioQueueStart(_saudio_AudioQueueRef inAQ, const _saudio_AudioTimeStamp * inStartTime);
extern _saudio_OSStatus AudioQueueStop(_saudio_AudioQueueRef inAQ, bool inImmediate);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // SAUDIO_OSX_USE_SYSTEM_HEADERS
typedef struct {
@ -893,6 +992,51 @@ _SOKOL_PRIVATE void _saudio_stream_callback(float* buffer, int num_frames, int n
}
}
/*=== MEMORY HELPERS ========================================================*/
_SOKOL_PRIVATE void _saudio_clear(void* ptr, size_t size) {
SOKOL_ASSERT(ptr && (size > 0));
memset(ptr, 0, size);
}
_SOKOL_PRIVATE void* _saudio_malloc(size_t size) {
SOKOL_ASSERT(size > 0);
void* ptr;
if (_saudio.desc.allocator.alloc) {
ptr = _saudio.desc.allocator.alloc(size, _saudio.desc.allocator.user_data);
}
else {
ptr = malloc(size);
}
SOKOL_ASSERT(ptr);
return ptr;
}
_SOKOL_PRIVATE void* _saudio_malloc_clear(size_t size) {
void* ptr = _saudio_malloc(size);
_saudio_clear(ptr, size);
return ptr;
}
_SOKOL_PRIVATE void _saudio_free(void* ptr) {
if (_saudio.desc.allocator.free) {
_saudio.desc.allocator.free(ptr, _saudio.desc.allocator.user_data);
}
else {
free(ptr);
}
}
#if defined(SOKOL_DEBUG)
_SOKOL_PRIVATE void _saudio_log(const char* msg) {
SOKOL_ASSERT(msg);
if (_saudio.desc.logger.log_cb) {
_saudio.desc.logger.log_cb(msg, _saudio.desc.logger.user_data);
} else {
SOKOL_LOG(msg);
}
}
#endif
/*=== MUTEX IMPLEMENTATION ===================================================*/
#if defined(_SAUDIO_NOTHREADS)
@ -1003,8 +1147,7 @@ _SOKOL_PRIVATE void _saudio_fifo_init(_saudio_fifo_t* fifo, int packet_size, int
SOKOL_ASSERT((packet_size > 0) && (num_packets > 0));
fifo->packet_size = packet_size;
fifo->num_packets = num_packets;
fifo->base_ptr = (uint8_t*) SOKOL_MALLOC((size_t)(packet_size * num_packets));
SOKOL_ASSERT(fifo->base_ptr);
fifo->base_ptr = (uint8_t*) _saudio_malloc((size_t)(packet_size * num_packets));
fifo->cur_packet = -1;
fifo->cur_offset = 0;
_saudio_ring_init(&fifo->read_queue, num_packets);
@ -1022,7 +1165,7 @@ _SOKOL_PRIVATE void _saudio_fifo_init(_saudio_fifo_t* fifo, int packet_size, int
_SOKOL_PRIVATE void _saudio_fifo_shutdown(_saudio_fifo_t* fifo) {
SOKOL_ASSERT(fifo->base_ptr);
SOKOL_FREE(fifo->base_ptr);
_saudio_free(fifo->base_ptr);
fifo->base_ptr = 0;
fifo->valid = false;
_saudio_mutex_destroy(&fifo->mutex);
@ -1191,7 +1334,7 @@ _SOKOL_PRIVATE void _saudio_coreaudio_callback(void* user_data, _saudio_AudioQue
int num_bytes = (int) buffer->mAudioDataByteSize;
if (0 == _saudio_fifo_read(&_saudio.fifo, ptr, num_bytes)) {
/* not enough read data available, fill the entire buffer with silence */
memset(ptr, 0, (size_t)num_bytes);
_saudio_clear(ptr, (size_t)num_bytes);
}
}
AudioQueueEnqueueBuffer(queue, buffer, 0, NULL);
@ -1213,7 +1356,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
/* create an audio queue with fp32 samples */
_saudio_AudioStreamBasicDescription fmt;
memset(&fmt, 0, sizeof(fmt));
_saudio_clear(&fmt, sizeof(fmt));
fmt.mSampleRate = (double) _saudio.sample_rate;
fmt.mFormatID = _saudio_kAudioFormatLinearPCM;
fmt.mFormatFlags = _saudio_kLinearPCMFormatFlagIsFloat | _saudio_kAudioFormatFlagIsPacked;
@ -1223,16 +1366,16 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
fmt.mBytesPerPacket = fmt.mBytesPerFrame;
fmt.mBitsPerChannel = 32;
_saudio_OSStatus res = AudioQueueNewOutput(&fmt, _saudio_coreaudio_callback, 0, NULL, NULL, 0, &_saudio.backend.ca_audio_queue);
SOKOL_ASSERT((res == 0) && _saudio.backend.ca_audio_queue);
SOKOL_ASSERT((res == 0) && _saudio.backend.ca_audio_queue); (void)res;
/* create 2 audio buffers */
for (int i = 0; i < 2; i++) {
_saudio_AudioQueueBufferRef buf = NULL;
const uint32_t buf_byte_size = (uint32_t)_saudio.buffer_frames * fmt.mBytesPerFrame;
res = AudioQueueAllocateBuffer(_saudio.backend.ca_audio_queue, buf_byte_size, &buf);
SOKOL_ASSERT((res == 0) && buf);
SOKOL_ASSERT((res == 0) && buf); (void)res;
buf->mAudioDataByteSize = buf_byte_size;
memset(buf->mAudioData, 0, buf->mAudioDataByteSize);
_saudio_clear(buf->mAudioData, buf->mAudioDataByteSize);
AudioQueueEnqueueBuffer(_saudio.backend.ca_audio_queue, buf, 0, NULL);
}
@ -1241,7 +1384,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
/* ...and start playback */
res = AudioQueueStart(_saudio.backend.ca_audio_queue, NULL);
SOKOL_ASSERT(0 == res);
SOKOL_ASSERT(0 == res); (void)res;
return true;
}
@ -1284,7 +1427,7 @@ _SOKOL_PRIVATE void* _saudio_alsa_cb(void* param) {
else {
if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.buffer, _saudio.backend.buffer_byte_size)) {
/* not enough read data available, fill the entire buffer with silence */
memset(_saudio.backend.buffer, 0, (size_t)_saudio.backend.buffer_byte_size);
_saudio_clear(_saudio.backend.buffer, (size_t)_saudio.backend.buffer_byte_size);
}
}
}
@ -1296,7 +1439,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
int dir; uint32_t rate;
int rc = snd_pcm_open(&_saudio.backend.device, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) {
SOKOL_LOG("sokol_audio.h: snd_pcm_open() failed");
SAUDIO_LOG("sokol_audio.h: snd_pcm_open() failed");
return false;
}
@ -1309,26 +1452,26 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
snd_pcm_hw_params_any(_saudio.backend.device, params);
snd_pcm_hw_params_set_access(_saudio.backend.device, params, SND_PCM_ACCESS_RW_INTERLEAVED);
if (0 > snd_pcm_hw_params_set_format(_saudio.backend.device, params, SND_PCM_FORMAT_FLOAT_LE)) {
SOKOL_LOG("sokol_audio.h: float samples not supported");
SAUDIO_LOG("sokol_audio.h: float samples not supported");
goto error;
}
if (0 > snd_pcm_hw_params_set_buffer_size(_saudio.backend.device, params, (snd_pcm_uframes_t)_saudio.buffer_frames)) {
SOKOL_LOG("sokol_audio.h: requested buffer size not supported");
SAUDIO_LOG("sokol_audio.h: requested buffer size not supported");
goto error;
}
if (0 > snd_pcm_hw_params_set_channels(_saudio.backend.device, params, (uint32_t)_saudio.num_channels)) {
SOKOL_LOG("sokol_audio.h: requested channel count not supported");
SAUDIO_LOG("sokol_audio.h: requested channel count not supported");
goto error;
}
/* let ALSA pick a nearby sampling rate */
rate = (uint32_t) _saudio.sample_rate;
dir = 0;
if (0 > snd_pcm_hw_params_set_rate_near(_saudio.backend.device, params, &rate, &dir)) {
SOKOL_LOG("sokol_audio.h: snd_pcm_hw_params_set_rate_near() failed");
SAUDIO_LOG("sokol_audio.h: snd_pcm_hw_params_set_rate_near() failed");
goto error;
}
if (0 > snd_pcm_hw_params(_saudio.backend.device, params)) {
SOKOL_LOG("sokol_audio.h: snd_pcm_hw_params() failed");
SAUDIO_LOG("sokol_audio.h: snd_pcm_hw_params() failed");
goto error;
}
@ -1339,12 +1482,11 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
/* allocate the streaming buffer */
_saudio.backend.buffer_byte_size = _saudio.buffer_frames * _saudio.bytes_per_frame;
_saudio.backend.buffer_frames = _saudio.buffer_frames;
_saudio.backend.buffer = (float*) SOKOL_MALLOC((size_t)_saudio.backend.buffer_byte_size);
memset(_saudio.backend.buffer, 0, (size_t)_saudio.backend.buffer_byte_size);
_saudio.backend.buffer = (float*) _saudio_malloc_clear((size_t)_saudio.backend.buffer_byte_size);
/* create the buffer-streaming start thread */
if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_alsa_cb, 0)) {
SOKOL_LOG("sokol_audio.h: pthread_create() failed");
SAUDIO_LOG("sokol_audio.h: pthread_create() failed");
goto error;
}
@ -1363,7 +1505,7 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) {
pthread_join(_saudio.backend.thread, 0);
snd_pcm_drain(_saudio.backend.device);
snd_pcm_close(_saudio.backend.device);
SOKOL_FREE(_saudio.backend.buffer);
_saudio_free(_saudio.backend.buffer);
};
/*=== WASAPI BACKEND IMPLEMENTATION ==========================================*/
@ -1419,7 +1561,7 @@ _SOKOL_PRIVATE void _saudio_wasapi_fill_buffer(void) {
else {
if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.thread.src_buffer, _saudio.backend.thread.src_buffer_byte_size)) {
/* not enough read data available, fill the entire buffer with silence */
memset(_saudio.backend.thread.src_buffer, 0, (size_t)_saudio.backend.thread.src_buffer_byte_size);
_saudio_clear(_saudio.backend.thread.src_buffer, (size_t)_saudio.backend.thread.src_buffer_byte_size);
}
}
}
@ -1487,7 +1629,7 @@ _SOKOL_PRIVATE DWORD WINAPI _saudio_wasapi_thread_fn(LPVOID param) {
_SOKOL_PRIVATE void _saudio_wasapi_release(void) {
if (_saudio.backend.thread.src_buffer) {
SOKOL_FREE(_saudio.backend.thread.src_buffer);
_saudio_free(_saudio.backend.thread.src_buffer);
_saudio.backend.thread.src_buffer = 0;
}
if (_saudio.backend.render_client) {
@ -1537,17 +1679,17 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
#endif
_saudio.backend.thread.buffer_end_event = CreateEvent(0, FALSE, FALSE, 0);
if (0 == _saudio.backend.thread.buffer_end_event) {
SOKOL_LOG("sokol_audio wasapi: failed to create buffer_end_event");
SAUDIO_LOG("sokol_audio wasapi: failed to create buffer_end_event");
goto error;
}
#if defined(_SAUDIO_UWP)
_saudio.backend.interface_activation_mutex = CreateMutexA(NULL, FALSE, "interface_activation_mutex");
if (_saudio.backend.interface_activation_mutex == NULL) {
SOKOL_LOG("sokol_audio wasapi: failed to create interface activation mutex");
SAUDIO_LOG("sokol_audio wasapi: failed to create interface activation mutex");
goto error;
}
if (FAILED(StringFromIID(_SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_Devinterface_Audio_Render), &_saudio.backend.interface_activation_audio_interface_uid_string))) {
SOKOL_LOG("sokol_audio wasapi: failed to get default audio device ID string");
SAUDIO_LOG("sokol_audio wasapi: failed to get default audio device ID string");
goto error;
}
@ -1561,7 +1703,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
static IActivateAudioInterfaceCompletionHandler completion_handler_interface = { &completion_handler_interface_vtable };
if (FAILED(ActivateAudioInterfaceAsync(_saudio.backend.interface_activation_audio_interface_uid_string, _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioClient), NULL, &completion_handler_interface, &_saudio.backend.interface_activation_operation))) {
SOKOL_LOG("sokol_audio wasapi: failed to get default audio device ID string");
SAUDIO_LOG("sokol_audio wasapi: failed to get default audio device ID string");
goto error;
}
while (!(_saudio.backend.audio_client)) {
@ -1571,7 +1713,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
}
if (!(_saudio.backend.interface_activation_success)) {
SOKOL_LOG("sokol_audio wasapi: interface activation failed. Unable to get audio client");
SAUDIO_LOG("sokol_audio wasapi: interface activation failed. Unable to get audio client");
goto error;
}
@ -1581,14 +1723,14 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
_SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IMMDeviceEnumerator),
(void**)&_saudio.backend.device_enumerator)))
{
SOKOL_LOG("sokol_audio wasapi: failed to create device enumerator");
SAUDIO_LOG("sokol_audio wasapi: failed to create device enumerator");
goto error;
}
if (FAILED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(_saudio.backend.device_enumerator,
eRender, eConsole,
&_saudio.backend.device)))
{
SOKOL_LOG("sokol_audio wasapi: GetDefaultAudioEndPoint failed");
SAUDIO_LOG("sokol_audio wasapi: GetDefaultAudioEndPoint failed");
goto error;
}
if (FAILED(IMMDevice_Activate(_saudio.backend.device,
@ -1596,13 +1738,13 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
CLSCTX_ALL, 0,
(void**)&_saudio.backend.audio_client)))
{
SOKOL_LOG("sokol_audio wasapi: device activate failed");
SAUDIO_LOG("sokol_audio wasapi: device activate failed");
goto error;
}
#endif
WAVEFORMATEXTENSIBLE fmtex;
memset(&fmtex, 0, sizeof(fmtex));
_saudio_clear(&fmtex, sizeof(fmtex));
fmtex.Format.nChannels = (WORD)_saudio.num_channels;
fmtex.Format.nSamplesPerSec = (DWORD)_saudio.sample_rate;
fmtex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
@ -1625,22 +1767,22 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
AUDCLNT_STREAMFLAGS_EVENTCALLBACK|AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM|AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY,
dur, 0, (WAVEFORMATEX*)&fmtex, 0)))
{
SOKOL_LOG("sokol_audio wasapi: audio client initialize failed");
SAUDIO_LOG("sokol_audio wasapi: audio client initialize failed");
goto error;
}
if (FAILED(IAudioClient_GetBufferSize(_saudio.backend.audio_client, &_saudio.backend.thread.dst_buffer_frames))) {
SOKOL_LOG("sokol_audio wasapi: audio client get buffer size failed");
SAUDIO_LOG("sokol_audio wasapi: audio client get buffer size failed");
goto error;
}
if (FAILED(IAudioClient_GetService(_saudio.backend.audio_client,
_SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioRenderClient),
(void**)&_saudio.backend.render_client)))
{
SOKOL_LOG("sokol_audio wasapi: audio client GetService failed");
SAUDIO_LOG("sokol_audio wasapi: audio client GetService failed");
goto error;
}
if (FAILED(IAudioClient_SetEventHandle(_saudio.backend.audio_client, _saudio.backend.thread.buffer_end_event))) {
SOKOL_LOG("sokol_audio wasapi: audio client SetEventHandle failed");
SAUDIO_LOG("sokol_audio wasapi: audio client SetEventHandle failed");
goto error;
}
_saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float);
@ -1648,13 +1790,12 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
_saudio.backend.thread.src_buffer_byte_size = _saudio.backend.thread.src_buffer_frames * _saudio.bytes_per_frame;
/* allocate an intermediate buffer for sample format conversion */
_saudio.backend.thread.src_buffer = (float*) SOKOL_MALLOC((size_t)_saudio.backend.thread.src_buffer_byte_size);
SOKOL_ASSERT(_saudio.backend.thread.src_buffer);
_saudio.backend.thread.src_buffer = (float*) _saudio_malloc((size_t)_saudio.backend.thread.src_buffer_byte_size);
/* create streaming thread */
_saudio.backend.thread.thread_handle = CreateThread(NULL, 0, _saudio_wasapi_thread_fn, 0, 0, 0);
if (0 == _saudio.backend.thread.thread_handle) {
SOKOL_LOG("sokol_audio wasapi: CreateThread failed");
SAUDIO_LOG("sokol_audio wasapi: CreateThread failed");
goto error;
}
return true;
@ -1698,7 +1839,7 @@ EMSCRIPTEN_KEEPALIVE int _saudio_emsc_pull(int num_frames) {
const int num_bytes = num_frames * _saudio.bytes_per_frame;
if (0 == _saudio_fifo_read(&_saudio.fifo, _saudio.backend.buffer, num_bytes)) {
/* not enough read data available, fill the entire buffer with silence */
memset(_saudio.backend.buffer, 0, (size_t)num_bytes);
_saudio_clear(_saudio.backend.buffer, (size_t)num_bytes);
}
}
int res = (int) _saudio.backend.buffer;
@ -1723,12 +1864,6 @@ EM_JS(int, saudio_js_init, (int sample_rate, int num_channels, int buffer_size),
latencyHint: 'interactive',
});
}
else if (typeof webkitAudioContext !== 'undefined') {
Module._saudio_context = new webkitAudioContext({
sampleRate: sample_rate,
latencyHint: 'interactive',
});
}
else {
Module._saudio_context = null;
console.log('sokol_audio.h: no WebAudio support');
@ -1736,14 +1871,14 @@ EM_JS(int, saudio_js_init, (int sample_rate, int num_channels, int buffer_size),
if (Module._saudio_context) {
console.log('sokol_audio.h: sample rate ', Module._saudio_context.sampleRate);
Module._saudio_node = Module._saudio_context.createScriptProcessor(buffer_size, 0, num_channels);
Module._saudio_node.onaudioprocess = function pump_audio(event) {
var num_frames = event.outputBuffer.length;
var ptr = __saudio_emsc_pull(num_frames);
Module._saudio_node.onaudioprocess = (event) => {
const num_frames = event.outputBuffer.length;
const ptr = __saudio_emsc_pull(num_frames);
if (ptr) {
var num_channels = event.outputBuffer.numberOfChannels;
for (var chn = 0; chn < num_channels; chn++) {
var chan = event.outputBuffer.getChannelData(chn);
for (var i = 0; i < num_frames; i++) {
const num_channels = event.outputBuffer.numberOfChannels;
for (let chn = 0; chn < num_channels; chn++) {
const chan = event.outputBuffer.getChannelData(chn);
for (let i = 0; i < num_frames; i++) {
chan[i] = HEAPF32[(ptr>>2) + ((num_channels*i)+chn)]
}
}
@ -1752,7 +1887,7 @@ EM_JS(int, saudio_js_init, (int sample_rate, int num_channels, int buffer_size),
Module._saudio_node.connect(Module._saudio_context.destination);
// in some browsers, WebAudio needs to be activated on a user action
var resume_webaudio = function() {
const resume_webaudio = () => {
if (Module._saudio_context) {
if (Module._saudio_context.state === 'suspended') {
Module._saudio_context.resume();
@ -1771,11 +1906,13 @@ EM_JS(int, saudio_js_init, (int sample_rate, int num_channels, int buffer_size),
/* shutdown the WebAudioContext and ScriptProcessorNode */
EM_JS(void, saudio_js_shutdown, (void), {
if (Module._saudio_context !== null) {
\x2F\x2A\x2A @suppress {missingProperties} \x2A\x2F
const ctx = Module._saudio_context;
if (ctx !== null) {
if (Module._saudio_node) {
Module._saudio_node.disconnect();
}
Module._saudio_context.close();
ctx.close();
Module._saudio_context = null;
Module._saudio_node = null;
}
@ -1819,7 +1956,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
_saudio.sample_rate = saudio_js_sample_rate();
_saudio.buffer_frames = saudio_js_buffer_frames();
const size_t buf_size = (size_t) (_saudio.buffer_frames * _saudio.bytes_per_frame);
_saudio.backend.buffer = (uint8_t*) SOKOL_MALLOC(buf_size);
_saudio.backend.buffer = (uint8_t*) _saudio_malloc(buf_size);
return true;
}
else {
@ -1830,7 +1967,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
_SOKOL_PRIVATE void _saudio_backend_shutdown(void) {
saudio_js_shutdown();
if (_saudio.backend.buffer) {
SOKOL_FREE(_saudio.backend.buffer);
_saudio_free(_saudio.backend.buffer);
_saudio.backend.buffer = 0;
}
}
@ -1904,7 +2041,7 @@ _SOKOL_PRIVATE void _saudio_opensles_fill_buffer(void) {
const int src_buffer_byte_size = src_buffer_frames * _saudio.num_channels * (int)sizeof(float);
if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.src_buffer, src_buffer_byte_size)) {
/* not enough read data available, fill the entire buffer with silence */
memset(_saudio.backend.src_buffer, 0x0, (size_t)src_buffer_byte_size);
_saudio_clear(_saudio.backend.src_buffer, (size_t)src_buffer_byte_size);
}
}
}
@ -1919,6 +2056,7 @@ _SOKOL_PRIVATE void SLAPIENTRY _saudio_opensles_play_cb(SLPlayItf player, void *
}
_SOKOL_PRIVATE void* _saudio_opensles_thread_fn(void* param) {
_SOKOL_UNUSED(param);
while (!_saudio.backend.thread_stop) {
/* get next output buffer, advance, next buffer. */
int16_t* out_buffer = _saudio.backend.output_buffers[_saudio.backend.active_buffer];
@ -1959,9 +2097,9 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) {
}
for (int i = 0; i < SAUDIO_NUM_BUFFERS; i++) {
SOKOL_FREE(_saudio.backend.output_buffers[i]);
_saudio_free(_saudio.backend.output_buffers[i]);
}
SOKOL_FREE(_saudio.backend.src_buffer);
_saudio_free(_saudio.backend.src_buffer);
}
_SOKOL_PRIVATE bool _saudio_backend_init(void) {
@ -1969,29 +2107,25 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
for (int i = 0; i < SAUDIO_NUM_BUFFERS; ++i) {
const int buffer_size_bytes = (int)sizeof(int16_t) * _saudio.num_channels * _saudio.buffer_frames;
_saudio.backend.output_buffers[i] = (int16_t*) SOKOL_MALLOC((size_t)buffer_size_bytes);
SOKOL_ASSERT(_saudio.backend.output_buffers[i]);
memset(_saudio.backend.output_buffers[i], 0x0, (size_t)buffer_size_bytes);
_saudio.backend.output_buffers[i] = (int16_t*) _saudio_malloc_clear((size_t)buffer_size_bytes);
}
{
const int buffer_size_bytes = _saudio.bytes_per_frame * _saudio.buffer_frames;
_saudio.backend.src_buffer = (float*) SOKOL_MALLOC((size_t)buffer_size_bytes);
SOKOL_ASSERT(_saudio.backend.src_buffer);
memset(_saudio.backend.src_buffer, 0x0, (size_t)buffer_size_bytes);
_saudio.backend.src_buffer = (float*) _saudio_malloc_clear((size_t)buffer_size_bytes);
}
/* Create engine */
const SLEngineOption opts[] = { SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE };
const SLEngineOption opts[] = { { SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE } };
if (slCreateEngine(&_saudio.backend.engine_obj, 1, opts, 0, NULL, NULL ) != SL_RESULT_SUCCESS) {
SOKOL_LOG("sokol_audio opensles: slCreateEngine failed");
SAUDIO_LOG("sokol_audio opensles: slCreateEngine failed");
_saudio_backend_shutdown();
return false;
}
(*_saudio.backend.engine_obj)->Realize(_saudio.backend.engine_obj, SL_BOOLEAN_FALSE);
if ((*_saudio.backend.engine_obj)->GetInterface(_saudio.backend.engine_obj, SL_IID_ENGINE, &_saudio.backend.engine) != SL_RESULT_SUCCESS) {
SOKOL_LOG("sokol_audio opensles: GetInterface->Engine failed");
SAUDIO_LOG("sokol_audio opensles: GetInterface->Engine failed");
_saudio_backend_shutdown();
return false;
}
@ -2003,14 +2137,14 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
if( (*_saudio.backend.engine)->CreateOutputMix(_saudio.backend.engine, &_saudio.backend.output_mix_obj, 1, ids, req) != SL_RESULT_SUCCESS)
{
SOKOL_LOG("sokol_audio opensles: CreateOutputMix failed");
SAUDIO_LOG("sokol_audio opensles: CreateOutputMix failed");
_saudio_backend_shutdown();
return false;
}
(*_saudio.backend.output_mix_obj)->Realize(_saudio.backend.output_mix_obj, SL_BOOLEAN_FALSE);
if((*_saudio.backend.output_mix_obj)->GetInterface(_saudio.backend.output_mix_obj, SL_IID_VOLUME, &_saudio.backend.output_mix_vol) != SL_RESULT_SUCCESS) {
SOKOL_LOG("sokol_audio opensles: GetInterface->OutputMixVol failed");
SAUDIO_LOG("sokol_audio opensles: GetInterface->OutputMixVol failed");
}
}
@ -2091,7 +2225,8 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) {
SOKOL_ASSERT(!_saudio.valid);
SOKOL_ASSERT(desc);
memset(&_saudio, 0, sizeof(_saudio));
SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
_saudio_clear(&_saudio, sizeof(_saudio));
_saudio.desc = *desc;
_saudio.stream_cb = desc->stream_cb;
_saudio.stream_userdata_cb = desc->stream_userdata_cb;
@ -2108,7 +2243,7 @@ SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) {
the requested packet size
*/
if (0 != (_saudio.buffer_frames % _saudio.packet_frames)) {
SOKOL_LOG("sokol_audio.h: actual backend buffer size isn't multiple of requested packet size");
SAUDIO_LOG("sokol_audio.h: actual backend buffer size isn't multiple of requested packet size");
_saudio_backend_shutdown();
return;
}

File diff suppressed because it is too large Load Diff

View File

@ -28,12 +28,9 @@
...optionally provide the following macros to override defaults:
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
SOKOL_MALLOC(s) - your own malloc function (default: malloc(s))
SOKOL_FREE(p) - your own free function (default: free(p))
SOKOL_FONTSTASH_API_DECL - public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_FONTSTASH_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -)
SOKOL_LOG(msg) - your own logging function (default: puts(msg))
SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false))
Include the following headers before including sokol_fontstash.h:
@ -55,7 +52,10 @@
--- Create at least one fontstash context with sfons_create() (this replaces
glfonsCreate() from fontstash.h's example GL renderer:
FONScontext* ctx = sfons_create(atlas_width, atlas_height, FONS_ZERO_TOPLEFT);
FONScontext* ctx = sfons_create(&(sfons_desc_t){
.width = atlas_width,
.height = atlas_height,
});
Each FONScontext manages one font atlas texture which can hold rasterized
glyphs for multiple fonts.
@ -88,7 +88,7 @@
--- finally on application shutdown, call:
sfons_shutdown()
sfons_destroy(FONScontext* ctx)
before sgl_shutdown() and sg_shutdown()
@ -96,7 +96,7 @@
WHAT HAPPENS UNDER THE HOOD:
============================
sfons_create():
FONScontext* sfons_create(const sfons_desc_t* desc)
- creates a sokol-gfx shader compatible with sokol-gl
- creates an sgl_pipeline object with alpha-blending using
this shader
@ -122,14 +122,43 @@
all calls to fonsDrawText() will be merged into a single draw call
as long as all calls use the same FONScontext
sfons_flush():
sfons_flush(FONScontext* ctx):
- this will call sg_update_image() on the font atlas texture
if fontstash.h has added any rasterized glyphs since the last
frame
sfons_shutdown():
sfons_destroy(FONScontext* ctx):
- destroy the font atlas texture, sgl_pipeline and sg_shader objects
MEMORY ALLOCATION OVERRIDE
==========================
You can override the memory allocation functions at initialization time
like this:
void* my_alloc(size_t size, void* user_data) {
return malloc(size);
}
void my_free(void* ptr, void* user_data) {
free(ptr);
}
...
FONScontext* fons_context = sfons_create(&(sfons_desc_t){
...
.allocator = {
.alloc = my_alloc,
.free = my_free,
.user_data = ...,
}
});
...
If no overrides are provided, malloc and free will be used. Please
note that this doesn't affect any memory allocation performed
in fontstash.h (unfortunately those are hardwired to malloc/free).
LICENSE
=======
zlib/libpng license
@ -158,6 +187,7 @@
#define SOKOL_FONTSTASH_INCLUDED (1)
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h> // size_t
#if !defined(SOKOL_GFX_INCLUDED)
#error "Please include sokol_gfx.h before sokol_fontstash.h"
@ -179,7 +209,30 @@
extern "C" {
#endif
SOKOL_FONTSTASH_API_DECL FONScontext* sfons_create(int width, int height, int flags);
/*
sfonst_allocator_t
Used in sfons_desc_t to provide custom memory-alloc and -free functions
to sokol_fontstash.h. If memory management should be overridden, both the
alloc and free function must be provided (e.g. it's not valid to
override one function but not the other).
NOTE that this does not affect memory allocation calls inside
fontstash.h
*/
typedef struct sfons_allocator_t {
void* (*alloc)(size_t size, void* user_data);
void (*free)(void* ptr, void* user_data);
void* user_data;
} sfons_allocator_t;
typedef struct sfons_desc_t {
int width; // initial width of font atlas texture (default: 512, must be power of 2)
int height; // initial height of font atlas texture (default: 512, must be power of 2)
sfons_allocator_t allocator; // optional memory allocation overrides
} sfons_desc_t;
SOKOL_FONTSTASH_API_DECL FONScontext* sfons_create(const sfons_desc_t* desc);
SOKOL_FONTSTASH_API_DECL void sfons_destroy(FONScontext* ctx);
SOKOL_FONTSTASH_API_DECL void sfons_flush(FONScontext* ctx);
SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
@ -192,7 +245,13 @@ SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, ui
/*-- IMPLEMENTATION ----------------------------------------------------------*/
#ifdef SOKOL_FONTSTASH_IMPL
#define SOKOL_FONTSTASH_IMPL_INCLUDED (1)
#include <string.h> /* memset, memcpy */
#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE)
#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sfons_desc_t.allocator to override memory allocation functions"
#endif
#include <string.h> // memset, memcpy
#include <stdlib.h> // malloc, free
#if !defined(SOKOL_GL_INCLUDED)
#error "Please include sokol_gl.h before sokol_fontstash.h"
@ -206,26 +265,13 @@ SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, ui
#endif
#ifndef SOKOL_DEBUG
#ifndef NDEBUG
#define SOKOL_DEBUG (1)
#define SOKOL_DEBUG
#endif
#endif
#ifndef SOKOL_ASSERT
#include <assert.h>
#define SOKOL_ASSERT(c) assert(c)
#endif
#ifndef SOKOL_MALLOC
#include <stdlib.h>
#define SOKOL_MALLOC(s) malloc(s)
#define SOKOL_FREE(p) free(p)
#endif
#ifndef SOKOL_LOG
#ifdef SOKOL_DEBUG
#include <stdio.h>
#define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); }
#else
#define SOKOL_LOG(s)
#endif
#endif
#ifndef SOKOL_UNREACHABLE
#define SOKOL_UNREACHABLE SOKOL_ASSERT(false)
#endif
@ -1609,13 +1655,48 @@ static const char* _sfons_fs_source_dummy = "";
#endif
typedef struct _sfons_t {
sfons_desc_t desc;
sg_shader shd;
sgl_pipeline pip;
sg_image img;
int width, height;
int cur_width, cur_height;
bool img_dirty;
} _sfons_t;
static void _sfons_clear(void* ptr, size_t size) {
SOKOL_ASSERT(ptr && (size > 0));
memset(ptr, 0, size);
}
static void* _sfons_malloc(const sfons_allocator_t* allocator, size_t size) {
SOKOL_ASSERT(allocator && (size > 0));
void* ptr;
if (allocator->alloc) {
ptr = allocator->alloc(size, allocator->user_data);
}
else {
ptr = malloc(size);
}
SOKOL_ASSERT(ptr);
return ptr;
}
static void* _sfons_malloc_clear(const sfons_allocator_t* allocator, size_t size) {
void* ptr = _sfons_malloc(allocator, size);
_sfons_clear(ptr, size);
return ptr;
}
static void _sfons_free(const sfons_allocator_t* allocator, void* ptr) {
SOKOL_ASSERT(allocator);
if (allocator->free) {
allocator->free(ptr, allocator->user_data);
}
else {
free(ptr);
}
}
static int _sfons_render_create(void* user_ptr, int width, int height) {
SOKOL_ASSERT(user_ptr && (width > 8) && (height > 8));
_sfons_t* sfons = (_sfons_t*) user_ptr;
@ -1623,7 +1704,7 @@ static int _sfons_render_create(void* user_ptr, int width, int height) {
/* sokol-gl compatible shader which treats RED channel as alpha */
if (sfons->shd.id == SG_INVALID_ID) {
sg_shader_desc shd_desc;
memset(&shd_desc, 0, sizeof(shd_desc));
_sfons_clear(&shd_desc, sizeof(shd_desc));
shd_desc.attrs[0].name = "position";
shd_desc.attrs[1].name = "texcoord0";
shd_desc.attrs[2].name = "color0";
@ -1687,7 +1768,7 @@ static int _sfons_render_create(void* user_ptr, int width, int height) {
/* sokol-gl pipeline object */
if (sfons->pip.id == SG_INVALID_ID) {
sg_pipeline_desc pip_desc;
memset(&pip_desc, 0, sizeof(pip_desc));
_sfons_clear(&pip_desc, sizeof(pip_desc));
pip_desc.shader = sfons->shd;
pip_desc.colors[0].blend.enabled = true;
pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;
@ -1700,14 +1781,14 @@ static int _sfons_render_create(void* user_ptr, int width, int height) {
sg_destroy_image(sfons->img);
sfons->img.id = SG_INVALID_ID;
}
sfons->width = width;
sfons->height = height;
sfons->cur_width = width;
sfons->cur_height = height;
SOKOL_ASSERT(sfons->img.id == SG_INVALID_ID);
sg_image_desc img_desc;
memset(&img_desc, 0, sizeof(img_desc));
img_desc.width = sfons->width;
img_desc.height = sfons->height;
_sfons_clear(&img_desc, sizeof(img_desc));
img_desc.width = sfons->cur_width;
img_desc.height = sfons->cur_height;
img_desc.min_filter = SG_FILTER_LINEAR;
img_desc.mag_filter = SG_FILTER_LINEAR;
img_desc.usage = SG_USAGE_DYNAMIC;
@ -1759,42 +1840,43 @@ static void _sfons_render_delete(void* user_ptr) {
sg_destroy_shader(sfons->shd);
sfons->shd.id = SG_INVALID_ID;
}
SOKOL_FREE(sfons);
}
// NOTE clang analyzer will report a potential memory leak for the call
// to SOKOL_MALLOC in the sfons_create() function, this is a false positive
// (the freeing happens in _sfons_render_delete()). The following macro
// silences the false positive when compilation happens with the analyzer active
#if __clang_analyzer__
#define _SFONS_CLANG_ANALYZER_SILENCE_POTENTIAL_LEAK_FALSE_POSITIVE(x) SOKOL_FREE(x)
#else
#define _SFONS_CLANG_ANALYZER_SILENCE_POTENTIAL_LEAK_FALSE_POSITIVE(x)
#endif
#define _sfons_def(val, def) (((val) == 0) ? (def) : (val))
SOKOL_API_IMPL FONScontext* sfons_create(int width, int height, int flags) {
SOKOL_ASSERT((width > 0) && (height > 0));
static sfons_desc_t _sfons_desc_defaults(const sfons_desc_t* desc) {
SOKOL_ASSERT(desc);
sfons_desc_t res = *desc;
res.width = _sfons_def(res.width, 512);
res.height = _sfons_def(res.height, 512);
return res;
}
SOKOL_API_IMPL FONScontext* sfons_create(const sfons_desc_t* desc) {
SOKOL_ASSERT(desc);
SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
_sfons_t* sfons = (_sfons_t*) _sfons_malloc_clear(&desc->allocator, sizeof(_sfons_t));
sfons->desc = _sfons_desc_defaults(desc);
FONSparams params;
_sfons_t* sfons = (_sfons_t*) SOKOL_MALLOC(sizeof(_sfons_t));
memset(sfons, 0, sizeof(_sfons_t));
memset(&params, 0, sizeof(params));
params.width = width;
params.height = height;
params.flags = (unsigned char) flags;
_sfons_clear(&params, sizeof(params));
params.width = sfons->desc.width;
params.height = sfons->desc.height;
params.flags = FONS_ZERO_TOPLEFT;
params.renderCreate = _sfons_render_create;
params.renderResize = _sfons_render_resize;
params.renderUpdate = _sfons_render_update;
params.renderDraw = _sfons_render_draw;
params.renderDelete = _sfons_render_delete;
params.userPtr = sfons;
FONScontext* ctx = fonsCreateInternal(&params);
_SFONS_CLANG_ANALYZER_SILENCE_POTENTIAL_LEAK_FALSE_POSITIVE(sfons);
return ctx;
return fonsCreateInternal(&params);
}
SOKOL_API_IMPL void sfons_destroy(FONScontext* ctx) {
SOKOL_ASSERT(ctx);
_sfons_t* sfons = (_sfons_t*) ctx->params.userPtr;
fonsDeleteInternal(ctx);
const sfons_allocator_t allocator = sfons->desc.allocator;
_sfons_free(&allocator, sfons);
}
SOKOL_API_IMPL void sfons_flush(FONScontext* ctx) {
@ -1803,9 +1885,9 @@ SOKOL_API_IMPL void sfons_flush(FONScontext* ctx) {
if (sfons->img_dirty) {
sfons->img_dirty = false;
sg_image_data data;
memset(&data, 0, sizeof(data));
_sfons_clear(&data, sizeof(data));
data.subimage[0][0].ptr = ctx->texData;
data.subimage[0][0].size = (size_t) (sfons->width * sfons->height);
data.subimage[0][0].size = (size_t) (sfons->cur_width * sfons->cur_height);
sg_update_image(sfons->img, &data);
}
}

View File

@ -27,12 +27,9 @@
...optionally provide the following macros to override defaults:
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
SOKOL_MALLOC(s) - your own malloc function (default: malloc(s))
SOKOL_FREE(p) - your own free function (default: free(p))
SOKOL_GL_API_DECL - public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_GL_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -)
SOKOL_LOG(msg) - your own logging function (default: puts(msg))
SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false))
If sokol_gl.h is compiled as a DLL, define the following before
@ -382,6 +379,32 @@
...if sokol-gl is in an error-state, sgl_draw() will skip any rendering,
and reset the error code to SGL_NO_ERROR.
RENDER LAYERS
=============
Render layers allow to split sokol-gl rendering into separate draw-command
groups which can then be rendered separately in a sokol-gfx draw pass. This
allows to mix/interleave sokol-gl rendering with other render operations.
Layered rendering is controlled through two functions:
sgl_layer(int layer_id)
sgl_draw_layer(int layer_id)
(and the context-variant sgl_draw_layer(): sgl_context_draw_layer()
The sgl_layer() function sets the 'current layer', any sokol-gl calls
which internally record draw commands will also store the current layer
in the draw command, and later in a sokol-gfx render pass, a call
to sgl_draw_layer() will only render the draw commands that have
a matching layer.
The default layer is '0', this is active after sokol-gl setup, and
is also restored at the start of a new frame (but *not* by calling
sgl_defaults()).
NOTE that calling sgl_draw() is equivalent with sgl_draw_layer(0)
(in general you should either use either use sgl_draw() or
sgl_draw_layer() in an application, but not both).
WORKING WITH CONTEXTS:
======================
@ -466,7 +489,7 @@
The only functions which call into sokol_gfx.h are:
- sgl_setup()
- sgl_shutdown()
- sgl_draw()
- sgl_draw() (and variants)
sgl_setup() must be called after initializing sokol-gfx.
sgl_shutdown() must be called before shutting down sokol-gfx.
@ -511,10 +534,13 @@
all pipeline objects) are destroyed
- the 3 memory buffers are freed
sgl_draw():
sgl_draw() (and variants)
- copy all recorded vertex data into the dynamic sokol-gfx buffer
via a call to sg_update_buffer()
- for each recorded command:
- if the layer number stored in the command doesn't match
the layer that's to be rendered, skip to the next
command
- if it's a viewport command, call sg_apply_viewport()
- if it's a scissor-rect command, call sg_apply_scissor_rect()
- if it's a draw command:
@ -536,10 +562,11 @@
A draw command will be merged with the previous command if "no relevant
state has changed" since the last sgl_end(), meaning:
- no calls to sgl_apply_viewport() and sgl_apply_scissor_rect()
- no calls to sgl_viewport() and sgl_scissor_rect()
- the primitive type hasn't changed
- the primitive type isn't a 'strip type' (no line or triangle strip)
- the pipeline state object hasn't changed
- the current layer hasn't changed
- none of the matrices has changed
- none of the texture state has changed
@ -547,6 +574,55 @@
to render in the previous draw command will be incremented by the
number of vertices in the new draw command.
MEMORY ALLOCATION OVERRIDE
==========================
You can override the memory allocation functions at initialization time
like this:
void* my_alloc(size_t size, void* user_data) {
return malloc(size);
}
void my_free(void* ptr, void* user_data) {
free(ptr);
}
...
sgl_setup(&(sgl_desc_t){
// ...
.allocator = {
.alloc = my_alloc,
.free = my_free,
.user_data = ...;
}
});
...
If no overrides are provided, malloc and free will be used.
LOG FUNCTION OVERRIDE
=====================
You can override the log function at initialization time like this:
void my_log(const char* message, void* user_data) {
printf("sgl says: \s\n", message);
}
...
sgl_setup(&(sgl_desc_t){
// ...
.logger = {
.log_cb = my_log,
.user_data = ...,
}
});
...
If no overrides are provided, puts will be used on most platforms.
On Android, __android_log_write will be used instead.
LICENSE
=======
zlib/libpng license
@ -575,6 +651,7 @@
#define SOKOL_GL_INCLUDED (1)
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h> // size_t, offsetof
#if !defined(SOKOL_GFX_INCLUDED)
#error "Please include sokol_gfx.h before sokol_gl.h"
@ -634,6 +711,31 @@ typedef struct sgl_context_desc_t {
int sample_count;
} sgl_context_desc_t;
/*
sgl_allocator_t
Used in sgl_desc_t to provide custom memory-alloc and -free functions
to sokol_gl.h. If memory management should be overridden, both the
alloc and free function must be provided (e.g. it's not valid to
override one function but not the other).
*/
typedef struct sgl_allocator_t {
void* (*alloc)(size_t size, void* user_data);
void (*free)(void* ptr, void* user_data);
void* user_data;
} sgl_allocator_t;
/*
sgl_logger_t
Used in sgl_desc_t to provide custom log callbacks to sokol_gl.h.
Default behavior is SOKOL_LOG(message).
*/
typedef struct sgl_logger_t {
void (*log_cb)(const char* message, void* user_data);
void* user_data;
} sgl_logger_t;
typedef struct sgl_desc_t {
int max_vertices; // default: 64k
int max_commands; // default: 16k
@ -643,6 +745,8 @@ typedef struct sgl_desc_t {
sg_pixel_format depth_format;
int sample_count;
sg_face_winding face_winding; // default: SG_FACEWINDING_CCW
sgl_allocator_t allocator; // optional memory allocation overrides (default: malloc/free)
sgl_logger_t logger; // optional memory allocation overrides (default: SOKOL_LOG(message))
} sgl_desc_t;
/* the default context handle */
@ -663,6 +767,12 @@ SOKOL_GL_API_DECL void sgl_set_context(sgl_context ctx);
SOKOL_GL_API_DECL sgl_context sgl_get_context(void);
SOKOL_GL_API_DECL sgl_context sgl_default_context(void);
/* draw recorded commands (call inside a sokol-gfx render pass) */
SOKOL_GL_API_DECL void sgl_draw();
SOKOL_GL_API_DECL void sgl_context_draw(sgl_context ctx);
SOKOL_GL_API_DECL void sgl_draw_layer(int layer_id);
SOKOL_GL_API_DECL void sgl_context_draw_layer(sgl_context ctx, int layer_id);
/* create and destroy pipeline objects */
SOKOL_GL_API_DECL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc);
SOKOL_GL_API_DECL sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline_desc* desc);
@ -677,6 +787,7 @@ SOKOL_GL_API_DECL void sgl_scissor_rectf(float x, float y, float w, float h, boo
SOKOL_GL_API_DECL void sgl_enable_texture(void);
SOKOL_GL_API_DECL void sgl_disable_texture(void);
SOKOL_GL_API_DECL void sgl_texture(sg_image img);
SOKOL_GL_API_DECL void sgl_layer(int layer_id);
/* pipeline stack functions */
SOKOL_GL_API_DECL void sgl_load_default_pipeline(void);
@ -745,10 +856,6 @@ SOKOL_GL_API_DECL void sgl_v3f_t2f_c4b(float x, float y, float z, float u, float
SOKOL_GL_API_DECL void sgl_v3f_t2f_c1i(float x, float y, float z, float u, float v, uint32_t rgba);
SOKOL_GL_API_DECL void sgl_end(void);
/* render recorded commands */
SOKOL_GL_API_DECL void sgl_draw();
SOKOL_GL_API_DECL void sgl_context_draw(sgl_context ctx);
#ifdef __cplusplus
} /* extern "C" */
@ -764,9 +871,13 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline
#ifdef SOKOL_GL_IMPL
#define SOKOL_GL_IMPL_INCLUDED (1)
#include <stddef.h> /* offsetof */
#include <string.h> /* memset */
#include <math.h> /* M_PI, sqrtf, sinf, cosf */
#if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE)
#error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sgl_desc_t.allocator to override memory allocation functions"
#endif
#include <stdlib.h> // malloc/free
#include <string.h> // memset
#include <math.h> // M_PI, sqrtf, sinf, cosf
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327
@ -777,29 +888,28 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline
#endif
#ifndef SOKOL_DEBUG
#ifndef NDEBUG
#define SOKOL_DEBUG (1)
#define SOKOL_DEBUG
#endif
#endif
#ifndef SOKOL_ASSERT
#include <assert.h>
#define SOKOL_ASSERT(c) assert(c)
#endif
#ifndef SOKOL_MALLOC
#include <stdlib.h>
#define SOKOL_MALLOC(s) malloc(s)
#define SOKOL_FREE(p) free(p)
#endif
#ifndef SOKOL_LOG
#ifdef SOKOL_DEBUG
#include <stdio.h>
#define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); }
#else
#define SOKOL_LOG(s)
#if !defined(SOKOL_DEBUG)
#define SGL_LOG(s)
#else
#define SGL_LOG(s) _sgl_log(s)
#ifndef SOKOL_LOG
#if defined(__ANDROID__)
#include <android/log.h>
#define SOKOL_LOG(s) __android_log_write(ANDROID_LOG_INFO, "SOKOL_GL", s)
#else
#include <stdio.h>
#define SOKOL_LOG(s) puts(s)
#endif
#endif
#endif
#ifndef SOKOL_UNREACHABLE
#define SOKOL_UNREACHABLE SOKOL_ASSERT(false)
#endif
#define _sgl_def(val, def) (((val) == 0) ? (def) : (val))
#define _SGL_INIT_COOKIE (0xABCDABCD)
@ -2192,6 +2302,7 @@ typedef union {
typedef struct {
_sgl_command_type_t cmd;
int layer_id;
_sgl_args_t args;
} _sgl_command_t;
@ -2208,22 +2319,30 @@ typedef struct {
typedef struct {
_sgl_slot_t slot;
sgl_context_desc_t desc;
int num_vertices;
int num_uniforms;
int num_commands;
int cur_vertex;
int cur_uniform;
int cur_command;
_sgl_vertex_t* vertices;
_sgl_uniform_t* uniforms;
_sgl_command_t* commands;
uint32_t frame_id;
uint32_t update_frame_id;
struct {
int cap;
int next;
_sgl_vertex_t* ptr;
} vertices;
struct {
int cap;
int next;
_sgl_uniform_t* ptr;
} uniforms;
struct {
int cap;
int next;
_sgl_command_t* ptr;
} commands;
/* state tracking */
int base_vertex;
int vtx_count; /* number of times vtx function has been called, used for non-triangle primitives */
sgl_error_t error;
bool in_begin;
int layer_id;
float u, v;
uint32_t rgba;
float point_size;
@ -2266,6 +2385,49 @@ typedef struct {
static _sgl_t _sgl;
/*== PRIVATE FUNCTIONS =======================================================*/
static void _sgl_clear(void* ptr, size_t size) {
SOKOL_ASSERT(ptr && (size > 0));
memset(ptr, 0, size);
}
static void* _sgl_malloc(size_t size) {
SOKOL_ASSERT(size > 0);
void* ptr;
if (_sgl.desc.allocator.alloc) {
ptr = _sgl.desc.allocator.alloc(size, _sgl.desc.allocator.user_data);
}
else {
ptr = malloc(size);
}
SOKOL_ASSERT(ptr);
return ptr;
}
static void* _sgl_malloc_clear(size_t size) {
void* ptr = _sgl_malloc(size);
_sgl_clear(ptr, size);
return ptr;
}
static void _sgl_free(void* ptr) {
if (_sgl.desc.allocator.free) {
_sgl.desc.allocator.free(ptr, _sgl.desc.allocator.user_data);
}
else {
free(ptr);
}
}
#if defined(SOKOL_DEBUG)
static void _sgl_log(const char* msg) {
SOKOL_ASSERT(msg);
if (_sgl.desc.logger.log_cb) {
_sgl.desc.logger.log_cb(msg, _sgl.desc.logger.user_data);
} else {
SOKOL_LOG(msg);
}
}
#endif
static void _sgl_init_pool(_sgl_pool_t* pool, int num) {
SOKOL_ASSERT(pool && (num >= 1));
@ -2274,12 +2436,9 @@ static void _sgl_init_pool(_sgl_pool_t* pool, int num) {
pool->queue_top = 0;
/* generation counters indexable by pool slot index, slot 0 is reserved */
size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size;
pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size);
SOKOL_ASSERT(pool->gen_ctrs);
memset(pool->gen_ctrs, 0, gen_ctrs_size);
pool->gen_ctrs = (uint32_t*) _sgl_malloc_clear(gen_ctrs_size);
/* it's not a bug to only reserve 'num' here */
pool->free_queue = (int*) SOKOL_MALLOC(sizeof(int) * (size_t)num);
SOKOL_ASSERT(pool->free_queue);
pool->free_queue = (int*) _sgl_malloc_clear(sizeof(int) * (size_t)num);
/* never allocate the zero-th pool item since the invalid id is 0 */
for (int i = pool->size-1; i >= 1; i--) {
pool->free_queue[pool->queue_top++] = i;
@ -2289,10 +2448,10 @@ static void _sgl_init_pool(_sgl_pool_t* pool, int num) {
static void _sgl_discard_pool(_sgl_pool_t* pool) {
SOKOL_ASSERT(pool);
SOKOL_ASSERT(pool->free_queue);
SOKOL_FREE(pool->free_queue);
_sgl_free(pool->free_queue);
pool->free_queue = 0;
SOKOL_ASSERT(pool->gen_ctrs);
SOKOL_FREE(pool->gen_ctrs);
_sgl_free(pool->gen_ctrs);
pool->gen_ctrs = 0;
pool->size = 0;
pool->queue_top = 0;
@ -2329,10 +2488,10 @@ static void _sgl_pool_free_index(_sgl_pool_t* pool, int slot_index) {
static void _sgl_reset_context(_sgl_context_t* ctx) {
SOKOL_ASSERT(ctx);
SOKOL_ASSERT(0 == ctx->vertices);
SOKOL_ASSERT(0 == ctx->uniforms);
SOKOL_ASSERT(0 == ctx->commands);
memset(ctx, 0, sizeof(_sgl_context_t));
SOKOL_ASSERT(0 == ctx->vertices.ptr);
SOKOL_ASSERT(0 == ctx->uniforms.ptr);
SOKOL_ASSERT(0 == ctx->commands.ptr);
_sgl_clear(ctx, sizeof(_sgl_context_t));
}
static void _sgl_setup_context_pool(int pool_size) {
@ -2340,20 +2499,18 @@ static void _sgl_setup_context_pool(int pool_size) {
SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE));
_sgl_init_pool(&_sgl.context_pool.pool, pool_size);
size_t pool_byte_size = sizeof(_sgl_context_t) * (size_t)_sgl.context_pool.pool.size;
_sgl.context_pool.contexts = (_sgl_context_t*) SOKOL_MALLOC(pool_byte_size);
SOKOL_ASSERT(_sgl.context_pool.contexts);
memset(_sgl.context_pool.contexts, 0, pool_byte_size);
_sgl.context_pool.contexts = (_sgl_context_t*) _sgl_malloc_clear(pool_byte_size);
}
static void _sgl_discard_context_pool(void) {
SOKOL_ASSERT(0 != _sgl.context_pool.contexts);
SOKOL_FREE(_sgl.context_pool.contexts); _sgl.context_pool.contexts = 0;
_sgl_free(_sgl.context_pool.contexts); _sgl.context_pool.contexts = 0;
_sgl_discard_pool(&_sgl.context_pool.pool);
}
static void _sgl_reset_pipeline(_sgl_pipeline_t* pip) {
SOKOL_ASSERT(pip);
memset(pip, 0, sizeof(_sgl_pipeline_t));
_sgl_clear(pip, sizeof(_sgl_pipeline_t));
}
static void _sgl_setup_pipeline_pool(int pool_size) {
@ -2361,14 +2518,12 @@ static void _sgl_setup_pipeline_pool(int pool_size) {
SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE));
_sgl_init_pool(&_sgl.pip_pool.pool, pool_size);
size_t pool_byte_size = sizeof(_sgl_pipeline_t) * (size_t)_sgl.pip_pool.pool.size;
_sgl.pip_pool.pips = (_sgl_pipeline_t*) SOKOL_MALLOC(pool_byte_size);
SOKOL_ASSERT(_sgl.pip_pool.pips);
memset(_sgl.pip_pool.pips, 0, pool_byte_size);
_sgl.pip_pool.pips = (_sgl_pipeline_t*) _sgl_malloc_clear(pool_byte_size);
}
static void _sgl_discard_pipeline_pool(void) {
SOKOL_ASSERT(0 != _sgl.pip_pool.pips);
SOKOL_FREE(_sgl.pip_pool.pips); _sgl.pip_pool.pips = 0;
_sgl_free(_sgl.pip_pool.pips); _sgl.pip_pool.pips = 0;
_sgl_discard_pool(&_sgl.pip_pool.pool);
}
@ -2510,7 +2665,7 @@ static void _sgl_init_pipeline(sgl_pipeline pip_id, const sg_pipeline_desc* in_d
else {
pip->pip[i] = sg_make_pipeline(&desc);
if (pip->pip[i].id == SG_INVALID_ID) {
SOKOL_LOG("sokol_gl.h: failed to create pipeline object");
SGL_LOG("sokol_gl.h: failed to create pipeline object");
pip->slot.state = SG_RESOURCESTATE_FAILED;
}
}
@ -2524,7 +2679,7 @@ static sgl_pipeline _sgl_make_pipeline(const sg_pipeline_desc* desc, const sgl_c
_sgl_init_pipeline(pip_id, desc, ctx_desc);
}
else {
SOKOL_LOG("sokol_gl.h: pipeline pool exhausted!");
SGL_LOG("sokol_gl.h: pipeline pool exhausted!");
}
return pip_id;
}
@ -2602,39 +2757,45 @@ static sgl_context_desc_t _sgl_context_desc_defaults(const sgl_context_desc_t* d
}
static void _sgl_identity(_sgl_matrix_t*);
static sg_commit_listener _sgl_make_commit_listener(_sgl_context_t* ctx);
static void _sgl_init_context(sgl_context ctx_id, const sgl_context_desc_t* in_desc) {
SOKOL_ASSERT((ctx_id.id != SG_INVALID_ID) && in_desc);
_sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
SOKOL_ASSERT(ctx);
ctx->desc = _sgl_context_desc_defaults(in_desc);
// NOTE: frame_id must be non-zero, so that updates trigger in first frame
ctx->frame_id = 1;
ctx->cur_img = _sgl.def_img;
// allocate buffers and pools
ctx->num_vertices = ctx->desc.max_vertices;
ctx->num_commands = ctx->num_uniforms = ctx->desc.max_commands;
ctx->vertices = (_sgl_vertex_t*) SOKOL_MALLOC((size_t)ctx->num_vertices * sizeof(_sgl_vertex_t));
SOKOL_ASSERT(ctx->vertices);
ctx->uniforms = (_sgl_uniform_t*) SOKOL_MALLOC((size_t)ctx->num_uniforms * sizeof(_sgl_uniform_t));
SOKOL_ASSERT(ctx->uniforms);
ctx->commands = (_sgl_command_t*) SOKOL_MALLOC((size_t)ctx->num_commands * sizeof(_sgl_command_t));
SOKOL_ASSERT(ctx->commands);
ctx->vertices.cap = ctx->desc.max_vertices;
ctx->commands.cap = ctx->uniforms.cap = ctx->desc.max_commands;
ctx->vertices.ptr = (_sgl_vertex_t*) _sgl_malloc((size_t)ctx->vertices.cap * sizeof(_sgl_vertex_t));
ctx->uniforms.ptr = (_sgl_uniform_t*) _sgl_malloc((size_t)ctx->uniforms.cap * sizeof(_sgl_uniform_t));
ctx->commands.ptr = (_sgl_command_t*) _sgl_malloc((size_t)ctx->commands.cap * sizeof(_sgl_command_t));
// create sokol-gfx resource objects
sg_push_debug_group("sokol-gl");
sg_buffer_desc vbuf_desc;
memset(&vbuf_desc, 0, sizeof(vbuf_desc));
vbuf_desc.size = (size_t)ctx->num_vertices * sizeof(_sgl_vertex_t);
_sgl_clear(&vbuf_desc, sizeof(vbuf_desc));
vbuf_desc.size = (size_t)ctx->vertices.cap * sizeof(_sgl_vertex_t);
vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER;
vbuf_desc.usage = SG_USAGE_STREAM;
vbuf_desc.label = "sgl-vertex-buffer";
ctx->vbuf = sg_make_buffer(&vbuf_desc);
SOKOL_ASSERT(SG_INVALID_ID != ctx->vbuf.id);
ctx->bind.vertex_buffers[0] = ctx->vbuf;
sg_pipeline_desc def_pip_desc;
memset(&def_pip_desc, 0, sizeof(def_pip_desc));
_sgl_clear(&def_pip_desc, sizeof(def_pip_desc));
def_pip_desc.depth.write_enabled = true;
ctx->def_pip = _sgl_make_pipeline(&def_pip_desc, &ctx->desc);
if (!sg_add_commit_listener(_sgl_make_commit_listener(ctx))) {
// FIXME: this should actually result in an invalid context,
// fix this when proper error logging/reporting is added
SGL_LOG("sokol_gl.h: failed to add sokol-gfx commit listener!");
}
sg_pop_debug_group();
// default state
@ -2654,7 +2815,7 @@ static sgl_context _sgl_make_context(const sgl_context_desc_t* desc) {
_sgl_init_context(ctx_id, desc);
}
else {
SOKOL_LOG("sokol_gl.h: context pool exhausted!");
SGL_LOG("sokol_gl.h: context pool exhausted!");
}
return ctx_id;
}
@ -2662,21 +2823,21 @@ static sgl_context _sgl_make_context(const sgl_context_desc_t* desc) {
static void _sgl_destroy_context(sgl_context ctx_id) {
_sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
if (ctx) {
SOKOL_ASSERT(ctx->vertices);
SOKOL_ASSERT(ctx->uniforms);
SOKOL_ASSERT(ctx->commands);
SOKOL_ASSERT(ctx->vertices.ptr);
SOKOL_ASSERT(ctx->uniforms.ptr);
SOKOL_ASSERT(ctx->commands.ptr);
SOKOL_FREE(ctx->vertices);
SOKOL_FREE(ctx->uniforms);
SOKOL_FREE(ctx->commands);
ctx->vertices = 0;
ctx->uniforms = 0;
ctx->commands = 0;
_sgl_free(ctx->vertices.ptr);
_sgl_free(ctx->uniforms.ptr);
_sgl_free(ctx->commands.ptr);
ctx->vertices.ptr = 0;
ctx->uniforms.ptr = 0;
ctx->commands.ptr = 0;
sg_push_debug_group("sokol-gl");
sg_destroy_buffer(ctx->vbuf);
_sgl_destroy_pipeline(ctx->def_pip);
sg_remove_commit_listener(_sgl_make_commit_listener(ctx));
sg_pop_debug_group();
_sgl_reset_context(ctx);
@ -2684,25 +2845,40 @@ static void _sgl_destroy_context(sgl_context ctx_id) {
}
}
static inline void _sgl_begin(_sgl_context_t* ctx, _sgl_primitive_type_t mode) {
static void _sgl_begin(_sgl_context_t* ctx, _sgl_primitive_type_t mode) {
ctx->in_begin = true;
ctx->base_vertex = ctx->cur_vertex;
ctx->base_vertex = ctx->vertices.next;
ctx->vtx_count = 0;
ctx->cur_prim_type = mode;
}
static void _sgl_rewind(_sgl_context_t* ctx) {
ctx->frame_id++;
ctx->vertices.next = 0;
ctx->uniforms.next = 0;
ctx->commands.next = 0;
ctx->base_vertex = 0;
ctx->cur_vertex = 0;
ctx->cur_uniform = 0;
ctx->cur_command = 0;
ctx->error = SGL_NO_ERROR;
ctx->layer_id = 0;
ctx->matrix_dirty = true;
}
static inline _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) {
if (ctx->cur_vertex < ctx->num_vertices) {
return &ctx->vertices[ctx->cur_vertex++];
// called from inside sokol-gfx sg_commit()
static void _sgl_commit_listener(void* userdata) {
_sgl_context_t* ctx = _sgl_lookup_context((uint32_t)(uintptr_t)userdata);
if (ctx) {
_sgl_rewind(ctx);
}
}
static sg_commit_listener _sgl_make_commit_listener(_sgl_context_t* ctx) {
sg_commit_listener listener = { _sgl_commit_listener, (void*)(uintptr_t)(ctx->slot.id) };
return listener;
}
static _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) {
if (ctx->vertices.next < ctx->vertices.cap) {
return &ctx->vertices.ptr[ctx->vertices.next++];
}
else {
ctx->error = SGL_ERROR_VERTICES_FULL;
@ -2710,9 +2886,9 @@ static inline _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) {
}
}
static inline _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) {
if (ctx->cur_uniform < ctx->num_uniforms) {
return &ctx->uniforms[ctx->cur_uniform++];
static _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) {
if (ctx->uniforms.next < ctx->uniforms.cap) {
return &ctx->uniforms.ptr[ctx->uniforms.next++];
}
else {
ctx->error = SGL_ERROR_UNIFORMS_FULL;
@ -2720,18 +2896,18 @@ static inline _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) {
}
}
static inline _sgl_command_t* _sgl_prev_command(_sgl_context_t* ctx) {
if (ctx->cur_command > 0) {
return &ctx->commands[ctx->cur_command - 1];
static _sgl_command_t* _sgl_cur_command(_sgl_context_t* ctx) {
if (ctx->commands.next > 0) {
return &ctx->commands.ptr[ctx->commands.next - 1];
}
else {
return 0;
}
}
static inline _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) {
if (ctx->cur_command < ctx->num_commands) {
return &ctx->commands[ctx->cur_command++];
static _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) {
if (ctx->commands.next < ctx->commands.cap) {
return &ctx->commands.ptr[ctx->commands.next++];
}
else {
ctx->error = SGL_ERROR_COMMANDS_FULL;
@ -2739,17 +2915,17 @@ static inline _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) {
}
}
static inline uint32_t _sgl_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
static uint32_t _sgl_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
return (uint32_t)(((uint32_t)a<<24)|((uint32_t)b<<16)|((uint32_t)g<<8)|r);
}
static inline float _sgl_clamp(float v, float lo, float hi) {
static float _sgl_clamp(float v, float lo, float hi) {
if (v < lo) return lo;
else if (v > hi) return hi;
else return v;
}
static inline uint32_t _sgl_pack_rgbaf(float r, float g, float b, float a) {
static uint32_t _sgl_pack_rgbaf(float r, float g, float b, float a) {
uint8_t r_u8 = (uint8_t) (_sgl_clamp(r, 0.0f, 1.0f) * 255.0f);
uint8_t g_u8 = (uint8_t) (_sgl_clamp(g, 0.0f, 1.0f) * 255.0f);
uint8_t b_u8 = (uint8_t) (_sgl_clamp(b, 0.0f, 1.0f) * 255.0f);
@ -2757,7 +2933,7 @@ static inline uint32_t _sgl_pack_rgbaf(float r, float g, float b, float a) {
return _sgl_pack_rgbab(r_u8, g_u8, b_u8, a_u8);
}
static inline void _sgl_vtx(_sgl_context_t* ctx, float x, float y, float z, float u, float v, uint32_t rgba) {
static void _sgl_vtx(_sgl_context_t* ctx, float x, float y, float z, float u, float v, uint32_t rgba) {
SOKOL_ASSERT(ctx->in_begin);
_sgl_vertex_t* vtx;
/* handle non-native primitive types */
@ -2971,27 +3147,28 @@ static void _sgl_lookat(_sgl_matrix_t* dst,
}
/* current top-of-stack projection matrix */
static inline _sgl_matrix_t* _sgl_matrix_projection(_sgl_context_t* ctx) {
static _sgl_matrix_t* _sgl_matrix_projection(_sgl_context_t* ctx) {
return &ctx->matrix_stack[SGL_MATRIXMODE_PROJECTION][ctx->matrix_tos[SGL_MATRIXMODE_PROJECTION]];
}
/* get top-of-stack modelview matrix */
static inline _sgl_matrix_t* _sgl_matrix_modelview(_sgl_context_t* ctx) {
static _sgl_matrix_t* _sgl_matrix_modelview(_sgl_context_t* ctx) {
return &ctx->matrix_stack[SGL_MATRIXMODE_MODELVIEW][ctx->matrix_tos[SGL_MATRIXMODE_MODELVIEW]];
}
/* get top-of-stack texture matrix */
static inline _sgl_matrix_t* _sgl_matrix_texture(_sgl_context_t* ctx) {
static _sgl_matrix_t* _sgl_matrix_texture(_sgl_context_t* ctx) {
return &ctx->matrix_stack[SGL_MATRIXMODE_TEXTURE][ctx->matrix_tos[SGL_MATRIXMODE_TEXTURE]];
}
/* get pointer to current top-of-stack of current matrix mode */
static inline _sgl_matrix_t* _sgl_matrix(_sgl_context_t* ctx) {
static _sgl_matrix_t* _sgl_matrix(_sgl_context_t* ctx) {
return &ctx->matrix_stack[ctx->cur_matrix_mode][ctx->matrix_tos[ctx->cur_matrix_mode]];
}
// return sg_context_desc_t with patched defaults
static sgl_desc_t _sgl_desc_defaults(const sgl_desc_t* desc) {
SOKOL_ASSERT((desc->allocator.alloc && desc->allocator.free) || (!desc->allocator.alloc && !desc->allocator.free));
sgl_desc_t res = *desc;
res.max_vertices = _sgl_def(desc->max_vertices, _SGL_DEFAULT_MAX_VERTICES);
res.max_commands = _sgl_def(desc->max_commands, _SGL_DEFAULT_MAX_COMMANDS);
@ -3010,7 +3187,7 @@ static void _sgl_setup_common(void) {
pixels[i] = 0xFFFFFFFF;
}
sg_image_desc img_desc;
memset(&img_desc, 0, sizeof(img_desc));
_sgl_clear(&img_desc, sizeof(img_desc));
img_desc.type = SG_IMAGETYPE_2D;
img_desc.width = 8;
img_desc.height = 8;
@ -3025,7 +3202,7 @@ static void _sgl_setup_common(void) {
// one shader for all contexts
sg_shader_desc shd_desc;
memset(&shd_desc, 0, sizeof(shd_desc));
_sgl_clear(&shd_desc, sizeof(shd_desc));
shd_desc.attrs[0].name = "position";
shd_desc.attrs[1].name = "texcoord0";
shd_desc.attrs[2].name = "color0";
@ -3097,18 +3274,26 @@ static bool _sgl_is_default_context(sgl_context ctx_id) {
return ctx_id.id == SGL_DEFAULT_CONTEXT.id;
}
static void _sgl_draw(_sgl_context_t* ctx) {
static void _sgl_draw(_sgl_context_t* ctx, int layer_id) {
SOKOL_ASSERT(ctx);
if ((ctx->error == SGL_NO_ERROR) && (ctx->cur_vertex > 0) && (ctx->cur_command > 0)) {
if ((ctx->error == SGL_NO_ERROR) && (ctx->vertices.next > 0) && (ctx->commands.next > 0)) {
sg_push_debug_group("sokol-gl");
uint32_t cur_pip_id = SG_INVALID_ID;
uint32_t cur_img_id = SG_INVALID_ID;
int cur_uniform_index = -1;
sg_push_debug_group("sokol-gl");
const sg_range range = { ctx->vertices, (size_t)ctx->cur_vertex * sizeof(_sgl_vertex_t) };
sg_update_buffer(ctx->vbuf, &range);
ctx->bind.vertex_buffers[0] = ctx->vbuf;
for (int i = 0; i < ctx->cur_command; i++) {
const _sgl_command_t* cmd = &ctx->commands[i];
if (ctx->update_frame_id != ctx->frame_id) {
ctx->update_frame_id = ctx->frame_id;
const sg_range range = { ctx->vertices.ptr, (size_t)ctx->vertices.next * sizeof(_sgl_vertex_t) };
sg_update_buffer(ctx->vbuf, &range);
}
for (int i = 0; i < ctx->commands.next; i++) {
const _sgl_command_t* cmd = &ctx->commands.ptr[i];
if (cmd->layer_id != layer_id) {
continue;
}
switch (cmd->cmd) {
case SGL_COMMAND_VIEWPORT:
{
@ -3138,7 +3323,7 @@ static void _sgl_draw(_sgl_context_t* ctx) {
cur_img_id = args->img.id;
}
if (cur_uniform_index != args->uniform_index) {
const sg_range ub_range = { &ctx->uniforms[args->uniform_index], sizeof(_sgl_uniform_t) };
const sg_range ub_range = { &ctx->uniforms.ptr[args->uniform_index], sizeof(_sgl_uniform_t) };
sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &ub_range);
cur_uniform_index = args->uniform_index;
}
@ -3152,12 +3337,11 @@ static void _sgl_draw(_sgl_context_t* ctx) {
}
sg_pop_debug_group();
}
_sgl_rewind(ctx);
}
static sgl_context_desc_t _sgl_as_context_desc(const sgl_desc_t* desc) {
sgl_context_desc_t ctx_desc;
memset(&ctx_desc, 0, sizeof(ctx_desc));
_sgl_clear(&ctx_desc, sizeof(ctx_desc));
ctx_desc.max_vertices = desc->max_vertices;
ctx_desc.max_commands = desc->max_commands;
ctx_desc.color_format = desc->color_format;
@ -3169,7 +3353,7 @@ static sgl_context_desc_t _sgl_as_context_desc(const sgl_desc_t* desc) {
/*== PUBLIC FUNCTIONS ========================================================*/
SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) {
SOKOL_ASSERT(desc);
memset(&_sgl, 0, sizeof(_sgl));
_sgl_clear(&_sgl, sizeof(_sgl));
_sgl.init_cookie = _SGL_INIT_COOKIE;
_sgl.desc = _sgl_desc_defaults(desc);
_sgl_setup_pipeline_pool(_sgl.desc.pipeline_pool_size);
@ -3234,7 +3418,7 @@ SOKOL_API_IMPL sgl_context sgl_make_context(const sgl_context_desc_t* desc) {
SOKOL_API_IMPL void sgl_destroy_context(sgl_context ctx_id) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
if (_sgl_is_default_context(ctx_id)) {
SOKOL_LOG("sokol_gl.h: cannot destroy default context");
SGL_LOG("sokol_gl.h: cannot destroy default context");
return;
}
_sgl_destroy_context(ctx_id);
@ -3360,6 +3544,16 @@ SOKOL_API_IMPL void sgl_defaults(void) {
ctx->matrix_dirty = true;
}
SOKOL_API_IMPL void sgl_layer(int layer_id) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
_sgl_context_t* ctx = _sgl.cur_ctx;
if (!ctx) {
return;
}
SOKOL_ASSERT(!ctx->in_begin);
ctx->layer_id = layer_id;
}
SOKOL_API_IMPL void sgl_viewport(int x, int y, int w, int h, bool origin_top_left) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
_sgl_context_t* ctx = _sgl.cur_ctx;
@ -3370,6 +3564,7 @@ SOKOL_API_IMPL void sgl_viewport(int x, int y, int w, int h, bool origin_top_lef
_sgl_command_t* cmd = _sgl_next_command(ctx);
if (cmd) {
cmd->cmd = SGL_COMMAND_VIEWPORT;
cmd->layer_id = ctx->layer_id;
cmd->args.viewport.x = x;
cmd->args.viewport.y = y;
cmd->args.viewport.w = w;
@ -3392,6 +3587,7 @@ SOKOL_API_IMPL void sgl_scissor_rect(int x, int y, int w, int h, bool origin_top
_sgl_command_t* cmd = _sgl_next_command(ctx);
if (cmd) {
cmd->cmd = SGL_COMMAND_SCISSOR_RECT;
cmd->layer_id = ctx->layer_id;
cmd->args.scissor_rect.x = x;
cmd->args.scissor_rect.y = y;
cmd->args.scissor_rect.w = w;
@ -3506,7 +3702,7 @@ SOKOL_API_IMPL void sgl_end(void) {
return;
}
SOKOL_ASSERT(ctx->in_begin);
SOKOL_ASSERT(ctx->cur_vertex >= ctx->base_vertex);
SOKOL_ASSERT(ctx->vertices.next >= ctx->base_vertex);
ctx->in_begin = false;
bool matrix_dirty = ctx->matrix_dirty;
if (matrix_dirty) {
@ -3517,37 +3713,39 @@ SOKOL_API_IMPL void sgl_end(void) {
uni->tm = *_sgl_matrix_texture(ctx);
}
}
/* check if command can be merged with previous command */
/* check if command can be merged with current command */
sg_pipeline pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type);
sg_image img = ctx->texturing_enabled ? ctx->cur_img : _sgl.def_img;
_sgl_command_t* prev_cmd = _sgl_prev_command(ctx);
_sgl_command_t* cur_cmd = _sgl_cur_command(ctx);
bool merge_cmd = false;
if (prev_cmd) {
if ((prev_cmd->cmd == SGL_COMMAND_DRAW) &&
if (cur_cmd) {
if ((cur_cmd->cmd == SGL_COMMAND_DRAW) &&
(cur_cmd->layer_id == ctx->layer_id) &&
(ctx->cur_prim_type != SGL_PRIMITIVETYPE_LINE_STRIP) &&
(ctx->cur_prim_type != SGL_PRIMITIVETYPE_TRIANGLE_STRIP) &&
!matrix_dirty &&
(prev_cmd->args.draw.img.id == img.id) &&
(prev_cmd->args.draw.pip.id == pip.id))
(cur_cmd->args.draw.img.id == img.id) &&
(cur_cmd->args.draw.pip.id == pip.id))
{
merge_cmd = true;
}
}
if (merge_cmd) {
/* draw command can be merged with the previous command */
prev_cmd->args.draw.num_vertices += ctx->cur_vertex - ctx->base_vertex;
cur_cmd->args.draw.num_vertices += ctx->vertices.next - ctx->base_vertex;
}
else {
/* append a new draw command */
_sgl_command_t* cmd = _sgl_next_command(ctx);
if (cmd) {
SOKOL_ASSERT(ctx->cur_uniform > 0);
SOKOL_ASSERT(ctx->uniforms.next > 0);
cmd->cmd = SGL_COMMAND_DRAW;
cmd->layer_id = ctx->layer_id;
cmd->args.draw.img = img;
cmd->args.draw.pip = _sgl_get_pipeline(ctx->pip_stack[ctx->pip_tos], ctx->cur_prim_type);
cmd->args.draw.base_vertex = ctx->base_vertex;
cmd->args.draw.num_vertices = ctx->cur_vertex - ctx->base_vertex;
cmd->args.draw.uniform_index = ctx->cur_uniform - 1;
cmd->args.draw.num_vertices = ctx->vertices.next - ctx->base_vertex;
cmd->args.draw.uniform_index = ctx->uniforms.next - 1;
}
}
}
@ -3956,7 +4154,15 @@ SOKOL_API_IMPL void sgl_draw(void) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
_sgl_context_t* ctx = _sgl.cur_ctx;
if (ctx) {
_sgl_draw(ctx);
_sgl_draw(ctx, 0);
}
}
SOKOL_API_IMPL void sgl_draw_layer(int layer_id) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
_sgl_context_t* ctx = _sgl.cur_ctx;
if (ctx) {
_sgl_draw(ctx, layer_id);
}
}
@ -3964,7 +4170,15 @@ SOKOL_API_IMPL void sgl_context_draw(sgl_context ctx_id) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
_sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
if (ctx) {
_sgl_draw(ctx);
_sgl_draw(ctx, 0);
}
}
SOKOL_API_IMPL void sgl_context_draw_layer(sgl_context ctx_id, int layer_id) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
_sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
if (ctx) {
_sgl_draw(ctx, layer_id);
}
}

View File

@ -1,5 +1,7 @@
module audio
import sokol.memory
$if linux {
// provide a nicer error for the user that does not have ALSA installed
#include <alsa/asoundlib.h> # Please install the `libasound2-dev` package
@ -53,6 +55,21 @@ pub fn (x FnStreamingCBWithUserData) str() string {
return '&FnStreamingCBWithUserData{ ${ptr_str(x)} }'
}
[typedef]
pub struct C.saudio_allocator {
pub mut:
alloc memory.FnAllocatorAlloc
free memory.FnAllocatorFree
user_data voidptr
}
[typedef]
pub struct C.saudio_logger {
pub mut:
log_cb memory.FnLogCb
user_data voidptr
}
// only one of `stream_cb` or `stream_userdata_cb` should be used
//
// default values (internal to sokol C library):
@ -64,6 +81,7 @@ pub fn (x FnStreamingCBWithUserData) str() string {
// | buffer_frames | 2048 | buffer size in frames, larger is more latency, smaller means higher CPU |
// | packet_frames | 128 | push model only, number of frames that will be pushed in each packet |
// | num_packets | 64 | for push model only, number of packets in the backend ringbuffer |
[typedef]
pub struct C.saudio_desc {
sample_rate int
num_channels int
@ -72,7 +90,10 @@ pub struct C.saudio_desc {
num_packets int
stream_cb FNStreamingCB
stream_userdata_cb FnStreamingCBWithUserData
user_data voidptr
pub mut:
user_data voidptr
allocator C.saudio_allocator
logger C.saudio_logger
}
fn C.saudio_setup(desc &C.saudio_desc)
@ -99,6 +120,18 @@ fn C.saudio_push(frames &f32, num_frames int) int
// setup - setup sokol-audio
pub fn setup(desc &C.saudio_desc) {
if desc.allocator.alloc == unsafe { nil } && desc.allocator.free == unsafe { nil } {
unsafe {
desc.allocator.alloc = memory.salloc
desc.allocator.free = memory.sfree
desc.allocator.user_data = voidptr(0x100a0d10)
}
}
if desc.logger.log_cb == unsafe { nil } {
unsafe {
desc.logger.log_cb = memory.slog
}
}
C.saudio_setup(desc)
}

View File

@ -1,8 +1,6 @@
module c
pub const (
used_import = 1
)
pub const used_import = 1
#flag -I @VEXEROOT/thirdparty/sokol
#flag -I @VEXEROOT/thirdparty/sokol/util
@ -58,13 +56,6 @@ $if emscripten ? {
#flag linux -ldl
$if gcboehm ? {
#define SOKOL_MALLOC GC_MALLOC
#define SOKOL_CALLOC(n,m) GC_MALLOC((n)*(m))
#define SOKOL_REALLOC GC_REALLOC
#define SOKOL_FREE GC_FREE
}
// To allow for thirdparty initializing window / acceleration contexts
// but still be able to use sokol.gfx e.g. SDL+sokol_gfx
$if !no_sokol_app ? {

View File

@ -3,9 +3,7 @@ module f
import fontstash
import sokol.c
pub const (
used_import = fontstash.used_import + c.used_import
)
pub const used_import = fontstash.used_import + c.used_import
#flag linux -I.

View File

@ -1,19 +1,30 @@
module gfx
import sokol.c
import sokol.memory
pub const (
version = 1
used_import = c.used_import
)
pub const version = 1
// setup and misc functions
[inline]
pub const used_import = c.used_import
// setup initialises the SOKOL's gfx library, based on the information passed in `desc`
pub fn setup(desc &Desc) {
if desc.allocator.alloc == unsafe { nil } && desc.allocator.free == unsafe { nil } {
unsafe {
desc.allocator.alloc = memory.salloc
desc.allocator.free = memory.sfree
desc.allocator.user_data = voidptr(0x1006fec5)
}
}
if desc.logger.log_cb == unsafe { nil } {
unsafe {
desc.logger.log_cb = memory.slog
}
}
C.sg_setup(desc)
}
[inline]
// shutdown tells the SOKOL's gfx library to shutdown, and release the resources it is using
pub fn shutdown() {
C.sg_shutdown()
}

View File

@ -0,0 +1,18 @@
module gfx
import sokol.memory
[typedef]
pub struct C.sg_allocator {
pub mut:
alloc memory.FnAllocatorAlloc
free memory.FnAllocatorFree
user_data voidptr
}
[typedef]
pub struct C.sg_logger {
pub mut:
log_cb memory.FnLogCb
user_data voidptr
}

View File

@ -1,30 +1,25 @@
module gfx
struct C.sg_desc {
// C.sg_desc describes
pub struct C.sg_desc {
pub mut:
_start_canary u32
buffer_pool_size int
image_pool_size int
shader_pool_size int
pipeline_pool_size int
pass_pool_size int
context_pool_size int
context ContextDesc
/*
// GL specific
gl_force_gles2 bool
// Metal-specific
mtl_device voidptr
mtl_renderpass_descriptor_cb fn() voidptr
mtl_drawable_cb fn() voidptr
mtl_global_uniform_buffer_size int
mtl_sampler_cache_size int
// D3D11-specific
d3d11_device voidptr
d3d11_device_context voidptr
d3d11_render_target_view_cb fn() voidptr
d3d11_depth_stencil_view_cb fn() voidptr
*/
_start_canary u32
buffer_pool_size int
image_pool_size int
shader_pool_size int
pipeline_pool_size int
pass_pool_size int
context_pool_size int
uniform_buffer_size int
staging_buffer_size int
sampler_cache_size int
max_commit_listeners int
disable_validation bool // disable validation layer even in debug mode, useful for tests
//
allocator C.sg_allocator
logger C.sg_logger
//
context ContextDesc
_end_canary u32
}

View File

@ -0,0 +1,38 @@
module memory
pub type FnAllocatorAlloc = fn (size usize, user_data voidptr) voidptr
pub type FnAllocatorFree = fn (ptr voidptr, user_data voidptr)
pub type FnLogCb = fn (const_message &char, user_data voidptr)
// salloc - used in the allocator structs, that the SOKOL libraries use, for allocating new memory blocks
pub fn salloc(size usize, user_data voidptr) voidptr {
res := unsafe { malloc(int(size)) }
$if trace_sokol_memory ? {
eprintln('sokol.memory.salloc | user_data: ${user_data:x} | size: ${size:10} | res: ${res:x}')
}
$if trace_sokol_memory_salloc_backtrace ? {
print_backtrace()
}
return res
}
// sfree - used in the allocator structs, that the SOKOL libraries use, for freeing memory
pub fn sfree(ptr voidptr, user_data voidptr) {
$if trace_sokol_memory ? {
eprintln(' sokol.memory.sfree | user_data: ${user_data:x} | ptr: ${ptr:x}')
}
$if trace_sokol_memory_sfree_backtrace ? {
print_backtrace()
}
unsafe { free(ptr) }
}
pub fn slog(const_message &char, user_data voidptr) {
$if trace_sokol_memory ? {
C.fprintf(C.stderr, c'sokol.memory.slog | user_data: %p, message: %s\n', user_data,
const_message)
}
C.puts(const_message)
}

View File

@ -183,6 +183,10 @@ pub enum KeyCode {
menu = 348
}
// TouchToolType is an Android specific 'tool type' enum for touch events.
// This lets the application check what type of input device was used for touch events.
// NOTE: the values must remain in sync with the corresponding Android SDK type, so don't change those.
// See https://developer.android.com/reference/android/view/MotionEvent#TOOL_TYPE_UNKNOWN
pub enum TouchToolType {
unknown
finger

View File

@ -2,6 +2,7 @@
module sapp
import sokol.gfx
import sokol.memory
pub const used_import = gfx.used_import
@ -180,8 +181,19 @@ pub fn get_clipboard_string() &char {
}
// special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub)
[inline]
pub fn run(desc &Desc) {
if desc.allocator.alloc == unsafe { nil } && desc.allocator.free == unsafe { nil } {
unsafe {
desc.allocator.alloc = memory.salloc
desc.allocator.free = memory.sfree
desc.allocator.user_data = voidptr(0x10005a44)
}
}
if desc.logger.log_cb == unsafe { nil } {
unsafe {
desc.logger.log_cb = memory.slog
}
}
g_desc = *desc
C.sapp_run(desc)
}

View File

@ -0,0 +1,18 @@
module sapp
import sokol.memory
[typedef]
pub struct C.sapp_allocator {
pub mut:
alloc memory.FnAllocatorAlloc
free memory.FnAllocatorFree
user_data voidptr
}
[typedef]
pub struct C.sapp_logger {
pub mut:
log_cb memory.FnLogCb
user_data voidptr
}

View File

@ -50,20 +50,20 @@ pub:
event_userdata_cb fn (&Event, voidptr)
fail_userdata_cb fn (&char, voidptr)
width int // the preferred width of the window / canvas
height int // the preferred height of the window / canvas
sample_count int // MSAA sample count
swap_interval int // the preferred swap interval (ignored on some platforms)
high_dpi bool // whether the rendering canvas is full-resolution on HighDPI displays
fullscreen bool // whether the window should be created in fullscreen mode
alpha bool // whether the framebuffer should have an alpha channel (ignored on some platforms)
window_title &char // the window title as UTF-8 encoded string
enable_clipboard bool // enable clipboard access, default is false
clipboard_size int // max size of clipboard content in bytes
enable_dragndrop bool // enable file dropping (drag'n'drop), default is false
max_dropped_files int // max number of dropped files to process (default: 1)
max_dropped_file_path_length int // max length in bytes of a dropped UTF-8 file path (default: 2048)
icon IconDesc
width int // the preferred width of the window / canvas
height int // the preferred height of the window / canvas
sample_count int // MSAA sample count
swap_interval int // the preferred swap interval (ignored on some platforms)
high_dpi bool // whether the rendering canvas is full-resolution on HighDPI displays
fullscreen bool // whether the window should be created in fullscreen mode
alpha bool // whether the framebuffer should have an alpha channel (ignored on some platforms)
window_title &char // the window title as UTF-8 encoded string
enable_clipboard bool // enable clipboard access, default is false
clipboard_size int // max size of clipboard content in bytes
enable_dragndrop bool // enable file dropping (drag'n'drop), default is false
max_dropped_files int // max number of dropped files to process (default: 1)
max_dropped_file_path_length int // max length in bytes of a dropped UTF-8 file path (default: 2048)
icon IconDesc // the initial window icon to set
// backend-specific options
gl_force_gles2 bool // if true, setup GLES2/WebGL even if GLES3/WebGL2 is available
win32_console_utf8 bool // if true, set the output console codepage to UTF-8
@ -77,6 +77,9 @@ pub:
ios_keyboard_resizes_canvas bool // if true, showing the iOS keyboard shrinks the canvas
// V patches
__v_native_render bool // V patch to allow for native rendering
pub mut:
allocator C.sapp_allocator // optional memory allocation overrides (default: malloc/free)
logger C.sapp_logger // optional log callback overrides (default: SAPP_LOG(message))
}
pub type Desc = C.sapp_desc
@ -84,25 +87,25 @@ pub type Desc = C.sapp_desc
[typedef]
pub struct C.sapp_event {
pub:
frame_count u64
@type EventType
key_code KeyCode
char_code u32
key_repeat bool
modifiers u32
mouse_button MouseButton
mouse_x f32
mouse_y f32
mouse_dx f32
mouse_dy f32
scroll_x f32
scroll_y f32
num_touches int
touches [max_touchpoints]TouchPoint
window_width int
window_height int
framebuffer_width int
framebuffer_height int
frame_count u64 // current frame counter, always valid, useful for checking if two events were issued in the same frame
@type EventType // the event type, always valid
key_code KeyCode // the virtual key code, only valid in KEY_UP, KEY_DOWN
char_code u32 // the UTF-32 character code, only valid in CHAR events
key_repeat bool // true if this is a key-repeat event, valid in KEY_UP, KEY_DOWN and CHAR
modifiers u32 // current modifier keys, valid in all key-, char- and mouse-events
mouse_button MouseButton // mouse button that was pressed or released, valid in MOUSE_DOWN, MOUSE_UP
mouse_x f32 // current horizontal mouse position in pixels, always valid except during mouse lock
mouse_y f32 // current vertical mouse position in pixels, always valid except during mouse lock
mouse_dx f32 // relative horizontal mouse movement since last frame, always valid
mouse_dy f32 // relative vertical mouse movement since last frame, always valid
scroll_x f32 // horizontal mouse wheel scroll distance, valid in MOUSE_SCROLL events
scroll_y f32 // vertical mouse wheel scroll distance, valid in MOUSE_SCROLL events
num_touches int // number of valid items in the touches[] array
touches [max_touchpoints]TouchPoint // current touch points, valid in TOUCHES_BEGIN, TOUCHES_MOVED, TOUCHES_ENDED
window_width int // current window- and framebuffer width in pixels, always valid
window_height int // current window- and framebuffer height in pixels, always valid
framebuffer_width int // = window_width * dpi_scale
framebuffer_height int // = window_height * dpi_scale
}
pub type Event = C.sapp_event
@ -114,11 +117,11 @@ pub fn (e &C.sapp_event) str() string {
[typedef]
pub struct C.sapp_touchpoint {
pub:
identifier u64
pos_x f32
pos_y f32
tool_type TouchToolType
changed bool
identifier u64
pos_x f32
pos_y f32
android_tooltype TouchToolType
changed bool
}
pub type TouchPoint = C.sapp_touchpoint

View File

@ -2,13 +2,27 @@ module sfons
import fontstash
import sokol.f
import sokol.memory
// keep v from warning about unused imports
const used_import = f.used_import + fontstash.used_import + 1
// create a new Context/font atlas, for rendering glyphs, given its dimensions `width` and `height`
[inline]
pub fn create(width int, height int, flags int) &fontstash.Context {
return C.sfons_create(width, height, flags)
assert is_power_of_two(width)
assert is_power_of_two(height)
allocator := C.sfons_allocator_t{
alloc: memory.salloc
free: memory.sfree
user_data: voidptr(0x100005f0)
}
desc := C.sfons_desc_t{
width: width
height: height
allocator: allocator
}
return C.sfons_create(&desc)
}
[inline]
@ -25,3 +39,7 @@ pub fn rgba(r u8, g u8, b u8, a u8) u32 {
pub fn flush(ctx &fontstash.Context) {
C.sfons_flush(ctx)
}
fn is_power_of_two(x int) bool {
return x in [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768]
}

View File

@ -1,8 +1,26 @@
module sfons
import fontstash
import sokol.memory
fn C.sfons_create(width int, height int, flags int) &fontstash.Context
[typedef]
pub struct C.sfons_allocator_t {
pub:
alloc memory.FnAllocatorAlloc
free memory.FnAllocatorFree
user_data voidptr
}
[typedef]
pub struct C.sfons_desc_t {
pub:
width int // initial width of font atlas texture (default: 512, must be power of 2)
height int // initial height of font atlas texture (default: 512, must be power of 2)
allocator C.sfons_allocator_t // optional memory allocation overrides
}
fn C.sfons_create(desc &C.sfons_desc_t) &fontstash.Context
fn C.sfons_destroy(ctx &fontstash.Context)
fn C.sfons_rgba(r u8, g u8, b u8, a u8) u32
fn C.sfons_flush(ctx &fontstash.Context)
fn C.sfons_rgba(r u8, g u8, b u8, a u8) u32

View File

@ -1,19 +1,29 @@
module sgl
import sokol.gfx
import sokol.memory
pub const (
version = gfx.version + 1
context = Context{0x00010001} // C.SGL_DEFAULT_CONTEXT = { 0x00010001 }
)
pub const version = gfx.version + 1
pub const context = Context{0x00010001} // C.SGL_DEFAULT_CONTEXT = { 0x00010001 }
// setup/shutdown/misc
[inline]
pub fn setup(desc &Desc) {
if desc.allocator.alloc == unsafe { nil } && desc.allocator.free == unsafe { nil } {
unsafe {
desc.allocator.alloc = memory.salloc
desc.allocator.free = memory.sfree
desc.allocator.user_data = voidptr(0x10000561)
}
}
if desc.logger.log_cb == unsafe { nil } {
unsafe {
desc.logger.log_cb = memory.slog
}
}
C.sgl_setup(desc)
}
[inline]
pub fn shutdown() {
C.sgl_shutdown()
}

View File

@ -0,0 +1,18 @@
module sgl
import sokol.memory
[typedef]
pub struct C.sgl_allocator_t {
pub mut:
alloc memory.FnAllocatorAlloc
free memory.FnAllocatorFree
user_data voidptr
}
[typedef]
pub struct C.sgl_logger_t {
pub mut:
log_cb memory.FnLogCb
user_data voidptr
}

View File

@ -25,7 +25,7 @@ pub type Context = C.sgl_context
pub type ContextDesc = C.sgl_context_desc_t
[typedef]
struct C.sgl_context_desc_t {
pub struct C.sgl_context_desc_t {
max_vertices int // default: 64k
max_commands int // default: 16k
color_format gfx.PixelFormat // C.sg_pixel_format
@ -36,7 +36,8 @@ struct C.sgl_context_desc_t {
pub type Desc = C.sgl_desc_t
[typedef]
struct C.sgl_desc_t {
pub struct C.sgl_desc_t {
pub:
max_vertices int // size for vertex buffer
max_commands int // size of uniform- and command-buffers
context_pool_size int // max number of contexts (including default context), default: 4
@ -45,4 +46,7 @@ struct C.sgl_desc_t {
depth_format gfx.PixelFormat // C.sg_pixel_format
sample_count int
face_winding gfx.FaceWinding // C.sg_face_winding // default front face winding is CCW
pub mut:
allocator C.sgl_allocator_t // optional memory allocation overrides (default: malloc/free)
logger C.sgl_logger_t // optional memory allocation overrides (default: SOKOL_LOG(message))
}

View File

@ -2,4 +2,4 @@ vlib/v/checker/tests/modules/module_with_redeclaration/redeclare_time_structs.v:
1 | module module_with_redeclaration
| ^
2 |
3 | pub const used = 1
3 | import sokol.memory

View File

@ -1,6 +1,27 @@
module module_with_redeclaration
import sokol.memory
pub const used = 1
struct C.saudio_desc {
[typedef]
pub struct C.saudio_allocator {
pub mut:
alloc memory.FnAllocatorAlloc
free memory.FnAllocatorFree
user_data voidptr
}
[typedef]
pub struct C.saudio_logger {
pub mut:
log_cb memory.FnLogCb
user_data voidptr
}
struct C.saudio_desc {
pub mut:
user_data voidptr
allocator C.saudio_allocator
logger C.saudio_logger
}

View File

@ -1,24 +1,52 @@
vlib/sokol/audio/audio.v:86:26: error: struct `C.saudio_desc` was declared as private to module `module_with_redeclaration`, so it can not be used inside module `sokol.audio`
84 | fn C.saudio_userdata() voidptr
85 |
86 | fn C.saudio_query_desc() C.saudio_desc
vlib/sokol/audio/audio.v:107:26: error: struct `C.saudio_desc` was declared as private to module `module_with_redeclaration`, so it can not be used inside module `sokol.audio`
105 | fn C.saudio_userdata() voidptr
106 |
107 | fn C.saudio_query_desc() C.saudio_desc
| ~~~~~~~~~~~~~
87 |
88 | fn C.saudio_sample_rate() int
vlib/sokol/audio/audio.v:101:19: error: struct `C.saudio_desc` was declared as private to module `module_with_redeclaration`, so it can not be used inside module `sokol.audio`
99 |
100 | // setup - setup sokol-audio
101 | pub fn setup(desc &C.saudio_desc) {
108 |
109 | fn C.saudio_sample_rate() int
vlib/sokol/audio/audio.v:122:19: error: struct `C.saudio_desc` was declared as private to module `module_with_redeclaration`, so it can not be used inside module `sokol.audio`
120 |
121 | // setup - setup sokol-audio
122 | pub fn setup(desc &C.saudio_desc) {
| ~~~~~~~~~~~~~~
102 | C.saudio_setup(desc)
103 | }
vlib/sokol/audio/audio.v:121:16: error: struct `C.saudio_desc` was declared as private to module `module_with_redeclaration`, so it can not be used inside module `sokol.audio`
119 |
120 | // query - return a copy of the original saudio_desc struct
121 | pub fn query() C.saudio_desc {
123 | if desc.allocator.alloc == unsafe { nil } && desc.allocator.free == unsafe { nil } {
124 | unsafe {
vlib/sokol/audio/audio.v:125:9: error: struct `C.saudio_desc` was declared as private to module `module_with_redeclaration`, so it can not be used inside module `sokol.audio`
123 | if desc.allocator.alloc == unsafe { nil } && desc.allocator.free == unsafe { nil } {
124 | unsafe {
125 | desc.allocator.alloc = memory.salloc
| ~~~~~~~~~
126 | desc.allocator.free = memory.sfree
127 | desc.allocator.user_data = voidptr(0x100a0d10)
vlib/sokol/audio/audio.v:126:9: error: struct `C.saudio_desc` was declared as private to module `module_with_redeclaration`, so it can not be used inside module `sokol.audio`
124 | unsafe {
125 | desc.allocator.alloc = memory.salloc
126 | desc.allocator.free = memory.sfree
| ~~~~~~~~~
127 | desc.allocator.user_data = voidptr(0x100a0d10)
128 | }
vlib/sokol/audio/audio.v:127:9: error: struct `C.saudio_desc` was declared as private to module `module_with_redeclaration`, so it can not be used inside module `sokol.audio`
125 | desc.allocator.alloc = memory.salloc
126 | desc.allocator.free = memory.sfree
127 | desc.allocator.user_data = voidptr(0x100a0d10)
| ~~~~~~~~~
128 | }
129 | }
vlib/sokol/audio/audio.v:132:9: error: struct `C.saudio_desc` was declared as private to module `module_with_redeclaration`, so it can not be used inside module `sokol.audio`
130 | if desc.logger.log_cb == unsafe { nil } {
131 | unsafe {
132 | desc.logger.log_cb = memory.slog
| ~~~~~~
133 | }
134 | }
vlib/sokol/audio/audio.v:154:16: error: struct `C.saudio_desc` was declared as private to module `module_with_redeclaration`, so it can not be used inside module `sokol.audio`
152 |
153 | // query - return a copy of the original saudio_desc struct
154 | pub fn query() C.saudio_desc {
| ~~~~~~~~~~~~~
122 | return C.saudio_query_desc()
123 | }
155 | return C.saudio_query_desc()
156 | }
vlib/v/checker/tests/private_redeclaration_of_C_struct.vv:7:10: error: struct `C.saudio_desc` was declared as private to module `module_with_redeclaration`, so it can not be used inside module `main`
5 |
6 | fn main() {