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 // stb_truetype.h - v1.26 - public domain
// authored from 2009-2020 by Sean Barrett / RAD Game Tools // authored from 2009-2021 by Sean Barrett / RAD Game Tools
// //
// ======================================================================= // =======================================================================
// //
@ -58,6 +58,8 @@
// //
// VERSION HISTORY // 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.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.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.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
@ -270,8 +272,8 @@
//// SAMPLE PROGRAMS //// 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 #if 0
#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
#include "stb_truetype.h" #include "stb_truetype.h"
@ -297,6 +299,8 @@ void my_stbtt_initfont(void)
void my_stbtt_print(float x, float y, char *text) void my_stbtt_print(float x, float y, char *text)
{ {
// assume orthographic projection with units = screen pixels, origin at top left // 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); glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, ftex); glBindTexture(GL_TEXTURE_2D, ftex);
glBegin(GL_QUADS); glBegin(GL_QUADS);
@ -304,10 +308,10 @@ void my_stbtt_print(float x, float y, char *text)
if (*text >= 32 && *text < 128) { if (*text >= 32 && *text < 128) {
stbtt_aligned_quad q; stbtt_aligned_quad q;
stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 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.s0,q.t0); glVertex2f(q.x0,q.y0);
glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0);
glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1);
glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1);
} }
++text; ++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); STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
// frees the data allocated above // 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_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); STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg);
// fills svg with the character's SVG data. // 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; search += 2;
{ {
stbtt_uint16 offset, start; stbtt_uint16 offset, start, last;
stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); 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); 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; return 0;
offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); 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); if (comp_verts) STBTT_free(comp_verts, info->userdata);
return 0; 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)); STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
if (vertices) STBTT_free(vertices, info->userdata); if (vertices) STBTT_free(vertices, info->userdata);
vertices = tmp; 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); subrs = stbtt__cid_get_glyph_subrs(info, glyph_index);
has_subrs = 1; has_subrs = 1;
} }
// fallthrough // FALLTHROUGH
case 0x1D: // callgsubr case 0x1D: // callgsubr
if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); if (sp < 1) return STBTT__CSERR("call(g|)subr stack");
v = (int) s[--sp]; v = (int) s[--sp];
@ -2239,7 +2244,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st
} break; } break;
default: default:
if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) if (b0 != 255 && b0 != 28 && b0 < 32)
return STBTT__CSERR("reserved operator"); return STBTT__CSERR("reserved operator");
// push immediate // push immediate
@ -2351,7 +2356,7 @@ STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningent
return length; 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_uint8 *data = info->data + info->kern;
stbtt_uint32 needle, straw; stbtt_uint32 needle, straw;
@ -2381,243 +2386,225 @@ static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph
return 0; 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); stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
switch(coverageFormat) { switch (coverageFormat) {
case 1: { case 1: {
stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
// Binary search. // Binary search.
stbtt_int32 l=0, r=glyphCount-1, m; stbtt_int32 l=0, r=glyphCount-1, m;
int straw, needle=glyph; int straw, needle=glyph;
while (l <= r) { while (l <= r) {
stbtt_uint8 *glyphArray = coverageTable + 4; stbtt_uint8 *glyphArray = coverageTable + 4;
stbtt_uint16 glyphID; stbtt_uint16 glyphID;
m = (l + r) >> 1; m = (l + r) >> 1;
glyphID = ttUSHORT(glyphArray + 2 * m); glyphID = ttUSHORT(glyphArray + 2 * m);
straw = glyphID; straw = glyphID;
if (needle < straw) if (needle < straw)
r = m - 1; r = m - 1;
else if (needle > straw) else if (needle > straw)
l = m + 1; l = m + 1;
else { else {
return m; return m;
}
} }
} break; }
break;
}
case 2: { case 2: {
stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
stbtt_uint8 *rangeArray = coverageTable + 4; stbtt_uint8 *rangeArray = coverageTable + 4;
// Binary search. // Binary search.
stbtt_int32 l=0, r=rangeCount-1, m; stbtt_int32 l=0, r=rangeCount-1, m;
int strawStart, strawEnd, needle=glyph; int strawStart, strawEnd, needle=glyph;
while (l <= r) { while (l <= r) {
stbtt_uint8 *rangeRecord; stbtt_uint8 *rangeRecord;
m = (l + r) >> 1; m = (l + r) >> 1;
rangeRecord = rangeArray + 6 * m; rangeRecord = rangeArray + 6 * m;
strawStart = ttUSHORT(rangeRecord); strawStart = ttUSHORT(rangeRecord);
strawEnd = ttUSHORT(rangeRecord + 2); strawEnd = ttUSHORT(rangeRecord + 2);
if (needle < strawStart) if (needle < strawStart)
r = m - 1; r = m - 1;
else if (needle > strawEnd) else if (needle > strawEnd)
l = m + 1; l = m + 1;
else { else {
stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
return startCoverageIndex + glyph - strawStart; return startCoverageIndex + glyph - strawStart;
}
} }
} break; }
break;
}
default: { default: return -1; // unsupported
// There are no other cases. }
STBTT_assert(0);
} break;
}
return -1; return -1;
} }
static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)
{ {
stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
switch(classDefFormat) switch (classDefFormat)
{ {
case 1: { case 1: {
stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
stbtt_uint8 *classDef1ValueArray = classDefTable + 6; stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
break;
}
classDefTable = classDef1ValueArray + 2 * glyphCount; case 2: {
} break; stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
stbtt_uint8 *classRangeRecords = classDefTable + 4;
case 2: { // Binary search.
stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); stbtt_int32 l=0, r=classRangeCount-1, m;
stbtt_uint8 *classRangeRecords = classDefTable + 4; 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. default:
stbtt_int32 l=0, r=classRangeCount-1, m; return -1; // Unsupported definition type, return an error.
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);
}
classDefTable = classRangeRecords + 6 * classRangeCount; // "All glyphs not assigned to a class fall into class 0". (OpenType spec)
} break; return 0;
default: {
// There are no other cases.
STBTT_assert(0);
} break;
}
return -1;
} }
// Define to STBTT_assert(x) if you want to break on unimplemented formats. // Define to STBTT_assert(x) if you want to break on unimplemented formats.
#define STBTT_GPOS_TODO_assert(x) #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_uint16 lookupListOffset;
stbtt_uint8 *lookupList; stbtt_uint8 *lookupList;
stbtt_uint16 lookupCount; stbtt_uint16 lookupCount;
stbtt_uint8 *data; stbtt_uint8 *data;
stbtt_int32 i; 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+0) != 1) return 0; // Major version 1
if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
lookupListOffset = ttUSHORT(data+8); lookupListOffset = ttUSHORT(data+8);
lookupList = data + lookupListOffset; lookupList = data + lookupListOffset;
lookupCount = ttUSHORT(lookupList); lookupCount = ttUSHORT(lookupList);
for (i=0; i<lookupCount; ++i) { for (i=0; i<lookupCount; ++i) {
stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i); stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
stbtt_uint8 *lookupTable = lookupList + lookupOffset; stbtt_uint8 *lookupTable = lookupList + lookupOffset;
stbtt_uint16 lookupType = ttUSHORT(lookupTable); stbtt_uint16 lookupType = ttUSHORT(lookupTable);
stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4); stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
stbtt_uint8 *subTableOffsets = lookupTable + 6; stbtt_uint8 *subTableOffsets = lookupTable + 6;
switch(lookupType) { if (lookupType != 2) // Pair Adjustment Positioning Subtable
case 2: { // Pair Adjustment Positioning Subtable continue;
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;
switch (posFormat) { for (sti=0; sti<subTableCount; sti++) {
case 1: { stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
stbtt_int32 l, r, m; stbtt_uint8 *table = lookupTable + subtableOffset;
int straw, needle; stbtt_uint16 posFormat = ttUSHORT(table);
stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
stbtt_int32 valueRecordPairSizeInBytes = 2; if (coverageIndex == -1) continue;
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;
STBTT_assert(coverageIndex < pairSetCount); switch (posFormat) {
STBTT__NOTUSED(pairSetCount); 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; if (coverageIndex >= pairSetCount) return 0;
r=pairValueCount-1;
l=0;
// Binary search. needle=glyph2;
while (l <= r) { r=pairValueCount-1;
stbtt_uint16 secondGlyph; l=0;
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;
case 2: { // Binary search.
stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); while (l <= r) {
stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); 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); case 2: {
stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); 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 class1Count = ttUSHORT(table + 12);
stbtt_uint16 class2Count = ttUSHORT(table + 14); stbtt_uint16 class2Count = ttUSHORT(table + 14);
STBTT_assert(glyph1class < class1Count); stbtt_uint8 *class1Records, *class2Records;
STBTT_assert(glyph2class < class2Count); stbtt_int16 xAdvance;
// TODO: Support more formats. if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed
STBTT_GPOS_TODO_assert(valueFormat1 == 4); if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed
if (valueFormat1 != 4) return 0;
STBTT_GPOS_TODO_assert(valueFormat2 == 0);
if (valueFormat2 != 0) return 0;
if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) { class1Records = table + 16;
stbtt_uint8 *class1Records = table + 16; class2Records = class1Records + 2 * (glyph1class * class2Count);
stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count); xAdvance = ttSHORT(class2Records + 2 * glyph2class);
stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class); return xAdvance;
return xAdvance; } else
} return 0;
} break; break;
}
default: {
// There are no other cases.
STBTT_assert(0);
break;
};
}
}
break;
};
default: default:
// TODO: Implement other stuff. return 0; // Unsupported position format
break; }
} }
} }
return 0; return 0;
} }
STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) 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) 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; float y_bottom = y_top+1;
@ -3129,13 +3133,13 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
float height; float height;
// simple case, only spans one pixel // simple case, only spans one pixel
int x = (int) x_top; int x = (int) x_top;
height = sy1 - sy0; height = (sy1 - sy0) * e->direction;
STBTT_assert(x >= 0 && x < len); STBTT_assert(x >= 0 && x < len);
scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);
scanline_fill[x] += e->direction * height; // everything right of this pixel is filled scanline_fill[x] += height; // everything right of this pixel is filled
} else { } else {
int x,x1,x2; int x,x1,x2;
float y_crossing, step, sign, area; float y_crossing, y_final, step, sign, area;
// covers 2+ pixels // covers 2+ pixels
if (x_top > x_bottom) { if (x_top > x_bottom) {
// flip scanline vertically; signed area is the same // 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; dy = -dy;
t = x0, x0 = xb, xb = t; t = x0, x0 = xb, xb = t;
} }
STBTT_assert(dy >= 0);
STBTT_assert(dx >= 0);
x1 = (int) x_top; x1 = (int) x_top;
x2 = (int) x_bottom; x2 = (int) x_bottom;
// compute intersection with y axis at x1+1 // 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; 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) { 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; 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); // 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 * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); 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); scanline_fill[x2] += sign * (sy1-sy0);
} }
} else { } 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 // clipping logic. since this does not match the intended use
// of this library, we use a different, very slow brute // of this library, we use a different, very slow brute
// force implementation // 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; int x;
for (x=0; x < len; ++x) { for (x=0; x < len; ++x) {
// cases: // cases:
@ -4414,15 +4471,14 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
float y_frac; float y_frac;
int winding = 0; int winding = 0;
orig[0] = x;
orig[1] = y;
// make sure y never passes through a vertex of the shape // make sure y never passes through a vertex of the shape
y_frac = (float) STBTT_fmod(y, 1.0f); y_frac = (float) STBTT_fmod(y, 1.0f);
if (y_frac < 0.01f) if (y_frac < 0.01f)
y += 0.01f; y += 0.01f;
else if (y_frac > 0.99f) else if (y_frac > 0.99f)
y -= 0.01f; y -= 0.01f;
orig[0] = x;
orig[1] = y; orig[1] = y;
// test a ray from (-infinity,y) to (x,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); 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) static int stbtt__solve_cubic(float a, float b, float c, float* r)
{ {
float s = -a / 3; float s = -a / 3;
float p = b - a*a / 3; float p = b - a*a / 3;
float q = a * (2*a*a - 9*b) / 27 + c; float q = a * (2*a*a - 9*b) / 27 + c;
float p3 = p*p*p; float p3 = p*p*p;
float d = q*q + 4*p3 / 27; float d = q*q + 4*p3 / 27;
if (d >= 0) { if (d >= 0) {
float z = (float) STBTT_sqrt(d); float z = (float) STBTT_sqrt(d);
float u = (-q + z) / 2; float u = (-q + z) / 2;
float v = (-q - z) / 2; float v = (-q - z) / 2;
u = stbtt__cuberoot(u); u = stbtt__cuberoot(u);
v = stbtt__cuberoot(v); v = stbtt__cuberoot(v);
r[0] = s + u + v; r[0] = s + u + v;
return 1; return 1;
} else { } else {
float u = (float) STBTT_sqrt(-p/3); 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 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 m = (float) STBTT_cos(v);
float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f;
r[0] = s + u * 2 * m; r[0] = s + u * 2 * m;
r[1] = s - u * (m + n); r[1] = s - u * (m + n);
r[2] = 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[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[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); //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) { for (i=0; i < num_verts; ++i) {
float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; 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 if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) {
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) {
float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; 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 // coarse culling against bbox
//if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && //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) // 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); STBTT_assert(i != 0);
if (dist < min_dist) { if (dist < min_dist) {
// check position along line // 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 ax = x1-x0, ay = y1-y0;
float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
float mx = x0 - sx, my = y0 - sy; 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]; float a_inv = precompute[i];
if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula 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); 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; float d = (mx*ax+my*ay) * a_inv;
num = stbtt__solve_cubic(b, c, d, res); 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) { if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) {
t = res[0], it = 1.0f - t; t = res[0], it = 1.0f - t;
px = it*it*x0 + 2*t*it*x1 + t*t*x2; 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 // 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.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod
// 1.18 (2018-01-29) add missing function // 1.18 (2018-01-29) add missing function
// 1.17 (2017-07-23) make more arguments const; doc fix // 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_DUMMY_BACKEND - use a dummy backend
SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 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_AUDIO_API_DECL- public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_AUDIO_API_DECL SOKOL_API_DECL - same as SOKOL_AUDIO_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -) SOKOL_API_IMPL - public function implementation prefix (default: -)
@ -316,6 +313,8 @@
"Blob URLs": https://www.html5rocks.com/en/tutorials/workers/basics/ "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
===================== =====================
The CoreAudio backend is selected on macOS and iOS (__APPLE__ is defined). 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 header must be present (usually both are installed with some sort
of ALSA development package). 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 LICENSE
======= =======
@ -392,6 +447,7 @@
distribution. distribution.
*/ */
#define SOKOL_AUDIO_INCLUDED (1) #define SOKOL_AUDIO_INCLUDED (1)
#include <stddef.h> // size_t
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
@ -412,15 +468,42 @@
extern "C" { extern "C" {
#endif #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 { typedef struct saudio_desc {
int sample_rate; /* requested sample rate */ int sample_rate; // requested sample rate
int num_channels; /* number of channels, default: 1 (mono) */ int num_channels; // number of channels, default: 1 (mono)
int buffer_frames; /* number of frames in streaming buffer */ int buffer_frames; // number of frames in streaming buffer
int packet_frames; /* number of frames in a packet */ int packet_frames; // number of frames in a packet
int num_packets; /* number of packets in packet queue */ 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_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 (*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 */ 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; } saudio_desc;
/* setup sokol-audio */ /* setup sokol-audio */
@ -458,6 +541,12 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc);
/*=== IMPLEMENTATION =========================================================*/ /*=== IMPLEMENTATION =========================================================*/
#ifdef SOKOL_AUDIO_IMPL #ifdef SOKOL_AUDIO_IMPL
#define SOKOL_AUDIO_IMPL_INCLUDED (1) #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 <string.h> // memset, memcpy
#include <stddef.h> // size_t #include <stddef.h> // size_t
@ -466,24 +555,26 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc);
#endif #endif
#ifndef SOKOL_DEBUG #ifndef SOKOL_DEBUG
#ifndef NDEBUG #ifndef NDEBUG
#define SOKOL_DEBUG (1) #define SOKOL_DEBUG
#endif #endif
#endif #endif
#ifndef SOKOL_ASSERT #ifndef SOKOL_ASSERT
#include <assert.h> #include <assert.h>
#define SOKOL_ASSERT(c) assert(c) #define SOKOL_ASSERT(c) assert(c)
#endif #endif
#ifndef SOKOL_MALLOC
#include <stdlib.h> #if !defined(SOKOL_DEBUG)
#define SOKOL_MALLOC(s) malloc(s) #define SAUDIO_LOG(s)
#define SOKOL_FREE(p) free(p) #else
#endif #define SAUDIO_LOG(s) _saudio_log(s)
#ifndef SOKOL_LOG #ifndef SOKOL_LOG
#ifdef SOKOL_DEBUG #if defined(__ANDROID__)
#include <stdio.h> #include <android/log.h>
#define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } #define SOKOL_LOG(s) __android_log_write(ANDROID_LOG_INFO, "SOKOL_AUDIO", s)
#else #else
#define SOKOL_LOG(s) #include <stdio.h>
#define SOKOL_LOG(s) puts(s)
#endif
#endif #endif
#endif #endif
@ -670,6 +761,9 @@ typedef OSStatus _saudio_OSStatus;
#define _saudio_kAudioFormatFlagIsPacked (kAudioFormatFlagIsPacked) #define _saudio_kAudioFormatFlagIsPacked (kAudioFormatFlagIsPacked)
#else #else
#ifdef __cplusplus
extern "C" {
#endif
// embedded AudioToolbox declarations // embedded AudioToolbox declarations
typedef uint32_t _saudio_AudioFormatID; 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 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 AudioQueueStart(_saudio_AudioQueueRef inAQ, const _saudio_AudioTimeStamp * inStartTime);
extern _saudio_OSStatus AudioQueueStop(_saudio_AudioQueueRef inAQ, bool inImmediate); extern _saudio_OSStatus AudioQueueStop(_saudio_AudioQueueRef inAQ, bool inImmediate);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // SAUDIO_OSX_USE_SYSTEM_HEADERS #endif // SAUDIO_OSX_USE_SYSTEM_HEADERS
typedef struct { 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 ===================================================*/ /*=== MUTEX IMPLEMENTATION ===================================================*/
#if defined(_SAUDIO_NOTHREADS) #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)); SOKOL_ASSERT((packet_size > 0) && (num_packets > 0));
fifo->packet_size = packet_size; fifo->packet_size = packet_size;
fifo->num_packets = num_packets; fifo->num_packets = num_packets;
fifo->base_ptr = (uint8_t*) SOKOL_MALLOC((size_t)(packet_size * num_packets)); fifo->base_ptr = (uint8_t*) _saudio_malloc((size_t)(packet_size * num_packets));
SOKOL_ASSERT(fifo->base_ptr);
fifo->cur_packet = -1; fifo->cur_packet = -1;
fifo->cur_offset = 0; fifo->cur_offset = 0;
_saudio_ring_init(&fifo->read_queue, num_packets); _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_PRIVATE void _saudio_fifo_shutdown(_saudio_fifo_t* fifo) {
SOKOL_ASSERT(fifo->base_ptr); SOKOL_ASSERT(fifo->base_ptr);
SOKOL_FREE(fifo->base_ptr); _saudio_free(fifo->base_ptr);
fifo->base_ptr = 0; fifo->base_ptr = 0;
fifo->valid = false; fifo->valid = false;
_saudio_mutex_destroy(&fifo->mutex); _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; int num_bytes = (int) buffer->mAudioDataByteSize;
if (0 == _saudio_fifo_read(&_saudio.fifo, ptr, num_bytes)) { if (0 == _saudio_fifo_read(&_saudio.fifo, ptr, num_bytes)) {
/* not enough read data available, fill the entire buffer with silence */ /* 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); AudioQueueEnqueueBuffer(queue, buffer, 0, NULL);
@ -1213,7 +1356,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
/* create an audio queue with fp32 samples */ /* create an audio queue with fp32 samples */
_saudio_AudioStreamBasicDescription fmt; _saudio_AudioStreamBasicDescription fmt;
memset(&fmt, 0, sizeof(fmt)); _saudio_clear(&fmt, sizeof(fmt));
fmt.mSampleRate = (double) _saudio.sample_rate; fmt.mSampleRate = (double) _saudio.sample_rate;
fmt.mFormatID = _saudio_kAudioFormatLinearPCM; fmt.mFormatID = _saudio_kAudioFormatLinearPCM;
fmt.mFormatFlags = _saudio_kLinearPCMFormatFlagIsFloat | _saudio_kAudioFormatFlagIsPacked; fmt.mFormatFlags = _saudio_kLinearPCMFormatFlagIsFloat | _saudio_kAudioFormatFlagIsPacked;
@ -1223,16 +1366,16 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
fmt.mBytesPerPacket = fmt.mBytesPerFrame; fmt.mBytesPerPacket = fmt.mBytesPerFrame;
fmt.mBitsPerChannel = 32; fmt.mBitsPerChannel = 32;
_saudio_OSStatus res = AudioQueueNewOutput(&fmt, _saudio_coreaudio_callback, 0, NULL, NULL, 0, &_saudio.backend.ca_audio_queue); _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 */ /* create 2 audio buffers */
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
_saudio_AudioQueueBufferRef buf = NULL; _saudio_AudioQueueBufferRef buf = NULL;
const uint32_t buf_byte_size = (uint32_t)_saudio.buffer_frames * fmt.mBytesPerFrame; const uint32_t buf_byte_size = (uint32_t)_saudio.buffer_frames * fmt.mBytesPerFrame;
res = AudioQueueAllocateBuffer(_saudio.backend.ca_audio_queue, buf_byte_size, &buf); 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; 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); AudioQueueEnqueueBuffer(_saudio.backend.ca_audio_queue, buf, 0, NULL);
} }
@ -1241,7 +1384,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
/* ...and start playback */ /* ...and start playback */
res = AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); res = AudioQueueStart(_saudio.backend.ca_audio_queue, NULL);
SOKOL_ASSERT(0 == res); SOKOL_ASSERT(0 == res); (void)res;
return true; return true;
} }
@ -1284,7 +1427,7 @@ _SOKOL_PRIVATE void* _saudio_alsa_cb(void* param) {
else { else {
if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.buffer, _saudio.backend.buffer_byte_size)) { 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 */ /* 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 dir; uint32_t rate;
int rc = snd_pcm_open(&_saudio.backend.device, "default", SND_PCM_STREAM_PLAYBACK, 0); int rc = snd_pcm_open(&_saudio.backend.device, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) { if (rc < 0) {
SOKOL_LOG("sokol_audio.h: snd_pcm_open() failed"); SAUDIO_LOG("sokol_audio.h: snd_pcm_open() failed");
return false; 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_any(_saudio.backend.device, params);
snd_pcm_hw_params_set_access(_saudio.backend.device, params, SND_PCM_ACCESS_RW_INTERLEAVED); 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)) { 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; goto error;
} }
if (0 > snd_pcm_hw_params_set_buffer_size(_saudio.backend.device, params, (snd_pcm_uframes_t)_saudio.buffer_frames)) { 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; goto error;
} }
if (0 > snd_pcm_hw_params_set_channels(_saudio.backend.device, params, (uint32_t)_saudio.num_channels)) { 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; goto error;
} }
/* let ALSA pick a nearby sampling rate */ /* let ALSA pick a nearby sampling rate */
rate = (uint32_t) _saudio.sample_rate; rate = (uint32_t) _saudio.sample_rate;
dir = 0; dir = 0;
if (0 > snd_pcm_hw_params_set_rate_near(_saudio.backend.device, params, &rate, &dir)) { 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; goto error;
} }
if (0 > snd_pcm_hw_params(_saudio.backend.device, params)) { 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; goto error;
} }
@ -1339,12 +1482,11 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
/* allocate the streaming buffer */ /* allocate the streaming buffer */
_saudio.backend.buffer_byte_size = _saudio.buffer_frames * _saudio.bytes_per_frame; _saudio.backend.buffer_byte_size = _saudio.buffer_frames * _saudio.bytes_per_frame;
_saudio.backend.buffer_frames = _saudio.buffer_frames; _saudio.backend.buffer_frames = _saudio.buffer_frames;
_saudio.backend.buffer = (float*) SOKOL_MALLOC((size_t)_saudio.backend.buffer_byte_size); _saudio.backend.buffer = (float*) _saudio_malloc_clear((size_t)_saudio.backend.buffer_byte_size);
memset(_saudio.backend.buffer, 0, (size_t)_saudio.backend.buffer_byte_size);
/* create the buffer-streaming start thread */ /* create the buffer-streaming start thread */
if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_alsa_cb, 0)) { 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; goto error;
} }
@ -1363,7 +1505,7 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) {
pthread_join(_saudio.backend.thread, 0); pthread_join(_saudio.backend.thread, 0);
snd_pcm_drain(_saudio.backend.device); snd_pcm_drain(_saudio.backend.device);
snd_pcm_close(_saudio.backend.device); snd_pcm_close(_saudio.backend.device);
SOKOL_FREE(_saudio.backend.buffer); _saudio_free(_saudio.backend.buffer);
}; };
/*=== WASAPI BACKEND IMPLEMENTATION ==========================================*/ /*=== WASAPI BACKEND IMPLEMENTATION ==========================================*/
@ -1419,7 +1561,7 @@ _SOKOL_PRIVATE void _saudio_wasapi_fill_buffer(void) {
else { else {
if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.thread.src_buffer, _saudio.backend.thread.src_buffer_byte_size)) { 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 */ /* 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) { _SOKOL_PRIVATE void _saudio_wasapi_release(void) {
if (_saudio.backend.thread.src_buffer) { 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; _saudio.backend.thread.src_buffer = 0;
} }
if (_saudio.backend.render_client) { if (_saudio.backend.render_client) {
@ -1537,17 +1679,17 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
#endif #endif
_saudio.backend.thread.buffer_end_event = CreateEvent(0, FALSE, FALSE, 0); _saudio.backend.thread.buffer_end_event = CreateEvent(0, FALSE, FALSE, 0);
if (0 == _saudio.backend.thread.buffer_end_event) { 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; goto error;
} }
#if defined(_SAUDIO_UWP) #if defined(_SAUDIO_UWP)
_saudio.backend.interface_activation_mutex = CreateMutexA(NULL, FALSE, "interface_activation_mutex"); _saudio.backend.interface_activation_mutex = CreateMutexA(NULL, FALSE, "interface_activation_mutex");
if (_saudio.backend.interface_activation_mutex == NULL) { 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; goto error;
} }
if (FAILED(StringFromIID(_SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_Devinterface_Audio_Render), &_saudio.backend.interface_activation_audio_interface_uid_string))) { 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; goto error;
} }
@ -1561,7 +1703,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
static IActivateAudioInterfaceCompletionHandler completion_handler_interface = { &completion_handler_interface_vtable }; 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))) { 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; goto error;
} }
while (!(_saudio.backend.audio_client)) { while (!(_saudio.backend.audio_client)) {
@ -1571,7 +1713,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
} }
if (!(_saudio.backend.interface_activation_success)) { 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; goto error;
} }
@ -1581,14 +1723,14 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
_SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IMMDeviceEnumerator), _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IMMDeviceEnumerator),
(void**)&_saudio.backend.device_enumerator))) (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; goto error;
} }
if (FAILED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(_saudio.backend.device_enumerator, if (FAILED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(_saudio.backend.device_enumerator,
eRender, eConsole, eRender, eConsole,
&_saudio.backend.device))) &_saudio.backend.device)))
{ {
SOKOL_LOG("sokol_audio wasapi: GetDefaultAudioEndPoint failed"); SAUDIO_LOG("sokol_audio wasapi: GetDefaultAudioEndPoint failed");
goto error; goto error;
} }
if (FAILED(IMMDevice_Activate(_saudio.backend.device, if (FAILED(IMMDevice_Activate(_saudio.backend.device,
@ -1596,13 +1738,13 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
CLSCTX_ALL, 0, CLSCTX_ALL, 0,
(void**)&_saudio.backend.audio_client))) (void**)&_saudio.backend.audio_client)))
{ {
SOKOL_LOG("sokol_audio wasapi: device activate failed"); SAUDIO_LOG("sokol_audio wasapi: device activate failed");
goto error; goto error;
} }
#endif #endif
WAVEFORMATEXTENSIBLE fmtex; WAVEFORMATEXTENSIBLE fmtex;
memset(&fmtex, 0, sizeof(fmtex)); _saudio_clear(&fmtex, sizeof(fmtex));
fmtex.Format.nChannels = (WORD)_saudio.num_channels; fmtex.Format.nChannels = (WORD)_saudio.num_channels;
fmtex.Format.nSamplesPerSec = (DWORD)_saudio.sample_rate; fmtex.Format.nSamplesPerSec = (DWORD)_saudio.sample_rate;
fmtex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; 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, AUDCLNT_STREAMFLAGS_EVENTCALLBACK|AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM|AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY,
dur, 0, (WAVEFORMATEX*)&fmtex, 0))) 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; goto error;
} }
if (FAILED(IAudioClient_GetBufferSize(_saudio.backend.audio_client, &_saudio.backend.thread.dst_buffer_frames))) { 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; goto error;
} }
if (FAILED(IAudioClient_GetService(_saudio.backend.audio_client, if (FAILED(IAudioClient_GetService(_saudio.backend.audio_client,
_SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioRenderClient), _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioRenderClient),
(void**)&_saudio.backend.render_client))) (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; goto error;
} }
if (FAILED(IAudioClient_SetEventHandle(_saudio.backend.audio_client, _saudio.backend.thread.buffer_end_event))) { 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; goto error;
} }
_saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); _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; _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 */ /* 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); _saudio.backend.thread.src_buffer = (float*) _saudio_malloc((size_t)_saudio.backend.thread.src_buffer_byte_size);
SOKOL_ASSERT(_saudio.backend.thread.src_buffer);
/* create streaming thread */ /* create streaming thread */
_saudio.backend.thread.thread_handle = CreateThread(NULL, 0, _saudio_wasapi_thread_fn, 0, 0, 0); _saudio.backend.thread.thread_handle = CreateThread(NULL, 0, _saudio_wasapi_thread_fn, 0, 0, 0);
if (0 == _saudio.backend.thread.thread_handle) { if (0 == _saudio.backend.thread.thread_handle) {
SOKOL_LOG("sokol_audio wasapi: CreateThread failed"); SAUDIO_LOG("sokol_audio wasapi: CreateThread failed");
goto error; goto error;
} }
return true; 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; const int num_bytes = num_frames * _saudio.bytes_per_frame;
if (0 == _saudio_fifo_read(&_saudio.fifo, _saudio.backend.buffer, num_bytes)) { if (0 == _saudio_fifo_read(&_saudio.fifo, _saudio.backend.buffer, num_bytes)) {
/* not enough read data available, fill the entire buffer with silence */ /* 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; 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', latencyHint: 'interactive',
}); });
} }
else if (typeof webkitAudioContext !== 'undefined') {
Module._saudio_context = new webkitAudioContext({
sampleRate: sample_rate,
latencyHint: 'interactive',
});
}
else { else {
Module._saudio_context = null; Module._saudio_context = null;
console.log('sokol_audio.h: no WebAudio support'); 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) { if (Module._saudio_context) {
console.log('sokol_audio.h: sample rate ', Module._saudio_context.sampleRate); 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 = Module._saudio_context.createScriptProcessor(buffer_size, 0, num_channels);
Module._saudio_node.onaudioprocess = function pump_audio(event) { Module._saudio_node.onaudioprocess = (event) => {
var num_frames = event.outputBuffer.length; const num_frames = event.outputBuffer.length;
var ptr = __saudio_emsc_pull(num_frames); const ptr = __saudio_emsc_pull(num_frames);
if (ptr) { if (ptr) {
var num_channels = event.outputBuffer.numberOfChannels; const num_channels = event.outputBuffer.numberOfChannels;
for (var chn = 0; chn < num_channels; chn++) { for (let chn = 0; chn < num_channels; chn++) {
var chan = event.outputBuffer.getChannelData(chn); const chan = event.outputBuffer.getChannelData(chn);
for (var i = 0; i < num_frames; i++) { for (let i = 0; i < num_frames; i++) {
chan[i] = HEAPF32[(ptr>>2) + ((num_channels*i)+chn)] 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); Module._saudio_node.connect(Module._saudio_context.destination);
// in some browsers, WebAudio needs to be activated on a user action // 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) {
if (Module._saudio_context.state === 'suspended') { if (Module._saudio_context.state === 'suspended') {
Module._saudio_context.resume(); 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 */ /* shutdown the WebAudioContext and ScriptProcessorNode */
EM_JS(void, saudio_js_shutdown, (void), { 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) { if (Module._saudio_node) {
Module._saudio_node.disconnect(); Module._saudio_node.disconnect();
} }
Module._saudio_context.close(); ctx.close();
Module._saudio_context = null; Module._saudio_context = null;
Module._saudio_node = null; Module._saudio_node = null;
} }
@ -1819,7 +1956,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
_saudio.sample_rate = saudio_js_sample_rate(); _saudio.sample_rate = saudio_js_sample_rate();
_saudio.buffer_frames = saudio_js_buffer_frames(); _saudio.buffer_frames = saudio_js_buffer_frames();
const size_t buf_size = (size_t) (_saudio.buffer_frames * _saudio.bytes_per_frame); 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; return true;
} }
else { else {
@ -1830,7 +1967,7 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { _SOKOL_PRIVATE void _saudio_backend_shutdown(void) {
saudio_js_shutdown(); saudio_js_shutdown();
if (_saudio.backend.buffer) { if (_saudio.backend.buffer) {
SOKOL_FREE(_saudio.backend.buffer); _saudio_free(_saudio.backend.buffer);
_saudio.backend.buffer = 0; _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); 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)) { 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 */ /* 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_PRIVATE void* _saudio_opensles_thread_fn(void* param) {
_SOKOL_UNUSED(param);
while (!_saudio.backend.thread_stop) { while (!_saudio.backend.thread_stop) {
/* get next output buffer, advance, next buffer. */ /* get next output buffer, advance, next buffer. */
int16_t* out_buffer = _saudio.backend.output_buffers[_saudio.backend.active_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++) { 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) { _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) { for (int i = 0; i < SAUDIO_NUM_BUFFERS; ++i) {
const int buffer_size_bytes = (int)sizeof(int16_t) * _saudio.num_channels * _saudio.buffer_frames; 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); _saudio.backend.output_buffers[i] = (int16_t*) _saudio_malloc_clear((size_t)buffer_size_bytes);
SOKOL_ASSERT(_saudio.backend.output_buffers[i]);
memset(_saudio.backend.output_buffers[i], 0x0, (size_t)buffer_size_bytes);
} }
{ {
const int buffer_size_bytes = _saudio.bytes_per_frame * _saudio.buffer_frames; const int buffer_size_bytes = _saudio.bytes_per_frame * _saudio.buffer_frames;
_saudio.backend.src_buffer = (float*) SOKOL_MALLOC((size_t)buffer_size_bytes); _saudio.backend.src_buffer = (float*) _saudio_malloc_clear((size_t)buffer_size_bytes);
SOKOL_ASSERT(_saudio.backend.src_buffer);
memset(_saudio.backend.src_buffer, 0x0, (size_t)buffer_size_bytes);
} }
/* Create engine */ /* 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) { 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(); _saudio_backend_shutdown();
return false; return false;
} }
(*_saudio.backend.engine_obj)->Realize(_saudio.backend.engine_obj, SL_BOOLEAN_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) { 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(); _saudio_backend_shutdown();
return false; 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) 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(); _saudio_backend_shutdown();
return false; return false;
} }
(*_saudio.backend.output_mix_obj)->Realize(_saudio.backend.output_mix_obj, SL_BOOLEAN_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) { 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_API_IMPL void saudio_setup(const saudio_desc* desc) {
SOKOL_ASSERT(!_saudio.valid); SOKOL_ASSERT(!_saudio.valid);
SOKOL_ASSERT(desc); 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.desc = *desc;
_saudio.stream_cb = desc->stream_cb; _saudio.stream_cb = desc->stream_cb;
_saudio.stream_userdata_cb = desc->stream_userdata_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 the requested packet size
*/ */
if (0 != (_saudio.buffer_frames % _saudio.packet_frames)) { 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(); _saudio_backend_shutdown();
return; 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: ...optionally provide the following macros to override defaults:
SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 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_FONTSTASH_API_DECL - public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_FONTSTASH_API_DECL SOKOL_API_DECL - same as SOKOL_FONTSTASH_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -) 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)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false))
Include the following headers before including sokol_fontstash.h: Include the following headers before including sokol_fontstash.h:
@ -55,7 +52,10 @@
--- Create at least one fontstash context with sfons_create() (this replaces --- Create at least one fontstash context with sfons_create() (this replaces
glfonsCreate() from fontstash.h's example GL renderer: 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 Each FONScontext manages one font atlas texture which can hold rasterized
glyphs for multiple fonts. glyphs for multiple fonts.
@ -88,7 +88,7 @@
--- finally on application shutdown, call: --- finally on application shutdown, call:
sfons_shutdown() sfons_destroy(FONScontext* ctx)
before sgl_shutdown() and sg_shutdown() before sgl_shutdown() and sg_shutdown()
@ -96,7 +96,7 @@
WHAT HAPPENS UNDER THE HOOD: 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 a sokol-gfx shader compatible with sokol-gl
- creates an sgl_pipeline object with alpha-blending using - creates an sgl_pipeline object with alpha-blending using
this shader this shader
@ -122,14 +122,43 @@
all calls to fonsDrawText() will be merged into a single draw call all calls to fonsDrawText() will be merged into a single draw call
as long as all calls use the same FONScontext 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 - this will call sg_update_image() on the font atlas texture
if fontstash.h has added any rasterized glyphs since the last if fontstash.h has added any rasterized glyphs since the last
frame frame
sfons_shutdown(): sfons_destroy(FONScontext* ctx):
- destroy the font atlas texture, sgl_pipeline and sg_shader objects - 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 LICENSE
======= =======
zlib/libpng license zlib/libpng license
@ -158,6 +187,7 @@
#define SOKOL_FONTSTASH_INCLUDED (1) #define SOKOL_FONTSTASH_INCLUDED (1)
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h> // size_t
#if !defined(SOKOL_GFX_INCLUDED) #if !defined(SOKOL_GFX_INCLUDED)
#error "Please include sokol_gfx.h before sokol_fontstash.h" #error "Please include sokol_gfx.h before sokol_fontstash.h"
@ -179,7 +209,30 @@
extern "C" { extern "C" {
#endif #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_destroy(FONScontext* ctx);
SOKOL_FONTSTASH_API_DECL void sfons_flush(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); 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 ----------------------------------------------------------*/ /*-- IMPLEMENTATION ----------------------------------------------------------*/
#ifdef SOKOL_FONTSTASH_IMPL #ifdef SOKOL_FONTSTASH_IMPL
#define SOKOL_FONTSTASH_IMPL_INCLUDED (1) #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) #if !defined(SOKOL_GL_INCLUDED)
#error "Please include sokol_gl.h before sokol_fontstash.h" #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 #endif
#ifndef SOKOL_DEBUG #ifndef SOKOL_DEBUG
#ifndef NDEBUG #ifndef NDEBUG
#define SOKOL_DEBUG (1) #define SOKOL_DEBUG
#endif #endif
#endif #endif
#ifndef SOKOL_ASSERT #ifndef SOKOL_ASSERT
#include <assert.h> #include <assert.h>
#define SOKOL_ASSERT(c) assert(c) #define SOKOL_ASSERT(c) assert(c)
#endif #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 #ifndef SOKOL_UNREACHABLE
#define SOKOL_UNREACHABLE SOKOL_ASSERT(false) #define SOKOL_UNREACHABLE SOKOL_ASSERT(false)
#endif #endif
@ -1609,13 +1655,48 @@ static const char* _sfons_fs_source_dummy = "";
#endif #endif
typedef struct _sfons_t { typedef struct _sfons_t {
sfons_desc_t desc;
sg_shader shd; sg_shader shd;
sgl_pipeline pip; sgl_pipeline pip;
sg_image img; sg_image img;
int width, height; int cur_width, cur_height;
bool img_dirty; bool img_dirty;
} _sfons_t; } _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) { static int _sfons_render_create(void* user_ptr, int width, int height) {
SOKOL_ASSERT(user_ptr && (width > 8) && (height > 8)); SOKOL_ASSERT(user_ptr && (width > 8) && (height > 8));
_sfons_t* sfons = (_sfons_t*) user_ptr; _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 */ /* sokol-gl compatible shader which treats RED channel as alpha */
if (sfons->shd.id == SG_INVALID_ID) { if (sfons->shd.id == SG_INVALID_ID) {
sg_shader_desc shd_desc; 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[0].name = "position";
shd_desc.attrs[1].name = "texcoord0"; shd_desc.attrs[1].name = "texcoord0";
shd_desc.attrs[2].name = "color0"; 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 */ /* sokol-gl pipeline object */
if (sfons->pip.id == SG_INVALID_ID) { if (sfons->pip.id == SG_INVALID_ID) {
sg_pipeline_desc pip_desc; 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.shader = sfons->shd;
pip_desc.colors[0].blend.enabled = true; pip_desc.colors[0].blend.enabled = true;
pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA; 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); sg_destroy_image(sfons->img);
sfons->img.id = SG_INVALID_ID; sfons->img.id = SG_INVALID_ID;
} }
sfons->width = width; sfons->cur_width = width;
sfons->height = height; sfons->cur_height = height;
SOKOL_ASSERT(sfons->img.id == SG_INVALID_ID); SOKOL_ASSERT(sfons->img.id == SG_INVALID_ID);
sg_image_desc img_desc; sg_image_desc img_desc;
memset(&img_desc, 0, sizeof(img_desc)); _sfons_clear(&img_desc, sizeof(img_desc));
img_desc.width = sfons->width; img_desc.width = sfons->cur_width;
img_desc.height = sfons->height; img_desc.height = sfons->cur_height;
img_desc.min_filter = SG_FILTER_LINEAR; img_desc.min_filter = SG_FILTER_LINEAR;
img_desc.mag_filter = SG_FILTER_LINEAR; img_desc.mag_filter = SG_FILTER_LINEAR;
img_desc.usage = SG_USAGE_DYNAMIC; img_desc.usage = SG_USAGE_DYNAMIC;
@ -1759,42 +1840,43 @@ static void _sfons_render_delete(void* user_ptr) {
sg_destroy_shader(sfons->shd); sg_destroy_shader(sfons->shd);
sfons->shd.id = SG_INVALID_ID; sfons->shd.id = SG_INVALID_ID;
} }
SOKOL_FREE(sfons);
} }
// NOTE clang analyzer will report a potential memory leak for the call #define _sfons_def(val, def) (((val) == 0) ? (def) : (val))
// 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
SOKOL_API_IMPL FONScontext* sfons_create(int width, int height, int flags) { static sfons_desc_t _sfons_desc_defaults(const sfons_desc_t* desc) {
SOKOL_ASSERT((width > 0) && (height > 0)); 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; FONSparams params;
_sfons_t* sfons = (_sfons_t*) SOKOL_MALLOC(sizeof(_sfons_t)); _sfons_clear(&params, sizeof(params));
memset(sfons, 0, sizeof(_sfons_t)); params.width = sfons->desc.width;
memset(&params, 0, sizeof(params)); params.height = sfons->desc.height;
params.width = width; params.flags = FONS_ZERO_TOPLEFT;
params.height = height;
params.flags = (unsigned char) flags;
params.renderCreate = _sfons_render_create; params.renderCreate = _sfons_render_create;
params.renderResize = _sfons_render_resize; params.renderResize = _sfons_render_resize;
params.renderUpdate = _sfons_render_update; params.renderUpdate = _sfons_render_update;
params.renderDraw = _sfons_render_draw; params.renderDraw = _sfons_render_draw;
params.renderDelete = _sfons_render_delete; params.renderDelete = _sfons_render_delete;
params.userPtr = sfons; params.userPtr = sfons;
FONScontext* ctx = fonsCreateInternal(&params); return fonsCreateInternal(&params);
_SFONS_CLANG_ANALYZER_SILENCE_POTENTIAL_LEAK_FALSE_POSITIVE(sfons);
return ctx;
} }
SOKOL_API_IMPL void sfons_destroy(FONScontext* ctx) { SOKOL_API_IMPL void sfons_destroy(FONScontext* ctx) {
SOKOL_ASSERT(ctx); SOKOL_ASSERT(ctx);
_sfons_t* sfons = (_sfons_t*) ctx->params.userPtr;
fonsDeleteInternal(ctx); fonsDeleteInternal(ctx);
const sfons_allocator_t allocator = sfons->desc.allocator;
_sfons_free(&allocator, sfons);
} }
SOKOL_API_IMPL void sfons_flush(FONScontext* ctx) { SOKOL_API_IMPL void sfons_flush(FONScontext* ctx) {
@ -1803,9 +1885,9 @@ SOKOL_API_IMPL void sfons_flush(FONScontext* ctx) {
if (sfons->img_dirty) { if (sfons->img_dirty) {
sfons->img_dirty = false; sfons->img_dirty = false;
sg_image_data data; 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].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); sg_update_image(sfons->img, &data);
} }
} }

View File

@ -27,12 +27,9 @@
...optionally provide the following macros to override defaults: ...optionally provide the following macros to override defaults:
SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) 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_GL_API_DECL - public function declaration prefix (default: extern)
SOKOL_API_DECL - same as SOKOL_GL_API_DECL SOKOL_API_DECL - same as SOKOL_GL_API_DECL
SOKOL_API_IMPL - public function implementation prefix (default: -) 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)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false))
If sokol_gl.h is compiled as a DLL, define the following before 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, ...if sokol-gl is in an error-state, sgl_draw() will skip any rendering,
and reset the error code to SGL_NO_ERROR. 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: WORKING WITH CONTEXTS:
====================== ======================
@ -466,7 +489,7 @@
The only functions which call into sokol_gfx.h are: The only functions which call into sokol_gfx.h are:
- sgl_setup() - sgl_setup()
- sgl_shutdown() - sgl_shutdown()
- sgl_draw() - sgl_draw() (and variants)
sgl_setup() must be called after initializing sokol-gfx. sgl_setup() must be called after initializing sokol-gfx.
sgl_shutdown() must be called before shutting down sokol-gfx. sgl_shutdown() must be called before shutting down sokol-gfx.
@ -511,10 +534,13 @@
all pipeline objects) are destroyed all pipeline objects) are destroyed
- the 3 memory buffers are freed - the 3 memory buffers are freed
sgl_draw(): sgl_draw() (and variants)
- copy all recorded vertex data into the dynamic sokol-gfx buffer - copy all recorded vertex data into the dynamic sokol-gfx buffer
via a call to sg_update_buffer() via a call to sg_update_buffer()
- for each recorded command: - 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 viewport command, call sg_apply_viewport()
- if it's a scissor-rect command, call sg_apply_scissor_rect() - if it's a scissor-rect command, call sg_apply_scissor_rect()
- if it's a draw command: - if it's a draw command:
@ -536,10 +562,11 @@
A draw command will be merged with the previous command if "no relevant A draw command will be merged with the previous command if "no relevant
state has changed" since the last sgl_end(), meaning: 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 hasn't changed
- the primitive type isn't a 'strip type' (no line or triangle strip) - the primitive type isn't a 'strip type' (no line or triangle strip)
- the pipeline state object hasn't changed - the pipeline state object hasn't changed
- the current layer hasn't changed
- none of the matrices has changed - none of the matrices has changed
- none of the texture state 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 to render in the previous draw command will be incremented by the
number of vertices in the new draw command. 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 LICENSE
======= =======
zlib/libpng license zlib/libpng license
@ -575,6 +651,7 @@
#define SOKOL_GL_INCLUDED (1) #define SOKOL_GL_INCLUDED (1)
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h> // size_t, offsetof
#if !defined(SOKOL_GFX_INCLUDED) #if !defined(SOKOL_GFX_INCLUDED)
#error "Please include sokol_gfx.h before sokol_gl.h" #error "Please include sokol_gfx.h before sokol_gl.h"
@ -634,6 +711,31 @@ typedef struct sgl_context_desc_t {
int sample_count; int sample_count;
} sgl_context_desc_t; } 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 { typedef struct sgl_desc_t {
int max_vertices; // default: 64k int max_vertices; // default: 64k
int max_commands; // default: 16k int max_commands; // default: 16k
@ -643,6 +745,8 @@ typedef struct sgl_desc_t {
sg_pixel_format depth_format; sg_pixel_format depth_format;
int sample_count; int sample_count;
sg_face_winding face_winding; // default: SG_FACEWINDING_CCW 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; } sgl_desc_t;
/* the default context handle */ /* 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_get_context(void);
SOKOL_GL_API_DECL sgl_context sgl_default_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 */ /* 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_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); 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_enable_texture(void);
SOKOL_GL_API_DECL void sgl_disable_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_texture(sg_image img);
SOKOL_GL_API_DECL void sgl_layer(int layer_id);
/* pipeline stack functions */ /* pipeline stack functions */
SOKOL_GL_API_DECL void sgl_load_default_pipeline(void); 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_v3f_t2f_c1i(float x, float y, float z, float u, float v, uint32_t rgba);
SOKOL_GL_API_DECL void sgl_end(void); 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 #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */
@ -764,9 +871,13 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline
#ifdef SOKOL_GL_IMPL #ifdef SOKOL_GL_IMPL
#define SOKOL_GL_IMPL_INCLUDED (1) #define SOKOL_GL_IMPL_INCLUDED (1)
#include <stddef.h> /* offsetof */ #if defined(SOKOL_MALLOC) || defined(SOKOL_CALLOC) || defined(SOKOL_FREE)
#include <string.h> /* memset */ #error "SOKOL_MALLOC/CALLOC/FREE macros are no longer supported, please use sgl_desc_t.allocator to override memory allocation functions"
#include <math.h> /* M_PI, sqrtf, sinf, cosf */ #endif
#include <stdlib.h> // malloc/free
#include <string.h> // memset
#include <math.h> // M_PI, sqrtf, sinf, cosf
#ifndef M_PI #ifndef M_PI
#define M_PI 3.14159265358979323846264338327 #define M_PI 3.14159265358979323846264338327
@ -777,29 +888,28 @@ inline sgl_pipeline sgl_context_make_pipeline(sgl_context ctx, const sg_pipeline
#endif #endif
#ifndef SOKOL_DEBUG #ifndef SOKOL_DEBUG
#ifndef NDEBUG #ifndef NDEBUG
#define SOKOL_DEBUG (1) #define SOKOL_DEBUG
#endif #endif
#endif #endif
#ifndef SOKOL_ASSERT #ifndef SOKOL_ASSERT
#include <assert.h> #include <assert.h>
#define SOKOL_ASSERT(c) assert(c) #define SOKOL_ASSERT(c) assert(c)
#endif #endif
#ifndef SOKOL_MALLOC
#include <stdlib.h> #if !defined(SOKOL_DEBUG)
#define SOKOL_MALLOC(s) malloc(s) #define SGL_LOG(s)
#define SOKOL_FREE(p) free(p) #else
#endif #define SGL_LOG(s) _sgl_log(s)
#ifndef SOKOL_LOG #ifndef SOKOL_LOG
#ifdef SOKOL_DEBUG #if defined(__ANDROID__)
#include <stdio.h> #include <android/log.h>
#define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } #define SOKOL_LOG(s) __android_log_write(ANDROID_LOG_INFO, "SOKOL_GL", s)
#else #else
#define SOKOL_LOG(s) #include <stdio.h>
#define SOKOL_LOG(s) puts(s)
#endif
#endif #endif
#endif #endif
#ifndef SOKOL_UNREACHABLE
#define SOKOL_UNREACHABLE SOKOL_ASSERT(false)
#endif
#define _sgl_def(val, def) (((val) == 0) ? (def) : (val)) #define _sgl_def(val, def) (((val) == 0) ? (def) : (val))
#define _SGL_INIT_COOKIE (0xABCDABCD) #define _SGL_INIT_COOKIE (0xABCDABCD)
@ -2192,6 +2302,7 @@ typedef union {
typedef struct { typedef struct {
_sgl_command_type_t cmd; _sgl_command_type_t cmd;
int layer_id;
_sgl_args_t args; _sgl_args_t args;
} _sgl_command_t; } _sgl_command_t;
@ -2208,22 +2319,30 @@ typedef struct {
typedef struct { typedef struct {
_sgl_slot_t slot; _sgl_slot_t slot;
sgl_context_desc_t desc; sgl_context_desc_t desc;
uint32_t frame_id;
int num_vertices; uint32_t update_frame_id;
int num_uniforms; struct {
int num_commands; int cap;
int cur_vertex; int next;
int cur_uniform; _sgl_vertex_t* ptr;
int cur_command; } vertices;
_sgl_vertex_t* vertices; struct {
_sgl_uniform_t* uniforms; int cap;
_sgl_command_t* commands; int next;
_sgl_uniform_t* ptr;
} uniforms;
struct {
int cap;
int next;
_sgl_command_t* ptr;
} commands;
/* state tracking */ /* state tracking */
int base_vertex; int base_vertex;
int vtx_count; /* number of times vtx function has been called, used for non-triangle primitives */ int vtx_count; /* number of times vtx function has been called, used for non-triangle primitives */
sgl_error_t error; sgl_error_t error;
bool in_begin; bool in_begin;
int layer_id;
float u, v; float u, v;
uint32_t rgba; uint32_t rgba;
float point_size; float point_size;
@ -2266,6 +2385,49 @@ typedef struct {
static _sgl_t _sgl; static _sgl_t _sgl;
/*== PRIVATE FUNCTIONS =======================================================*/ /*== 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) { static void _sgl_init_pool(_sgl_pool_t* pool, int num) {
SOKOL_ASSERT(pool && (num >= 1)); SOKOL_ASSERT(pool && (num >= 1));
@ -2274,12 +2436,9 @@ static void _sgl_init_pool(_sgl_pool_t* pool, int num) {
pool->queue_top = 0; pool->queue_top = 0;
/* generation counters indexable by pool slot index, slot 0 is reserved */ /* generation counters indexable by pool slot index, slot 0 is reserved */
size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size;
pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size); pool->gen_ctrs = (uint32_t*) _sgl_malloc_clear(gen_ctrs_size);
SOKOL_ASSERT(pool->gen_ctrs);
memset(pool->gen_ctrs, 0, gen_ctrs_size);
/* it's not a bug to only reserve 'num' here */ /* it's not a bug to only reserve 'num' here */
pool->free_queue = (int*) SOKOL_MALLOC(sizeof(int) * (size_t)num); pool->free_queue = (int*) _sgl_malloc_clear(sizeof(int) * (size_t)num);
SOKOL_ASSERT(pool->free_queue);
/* never allocate the zero-th pool item since the invalid id is 0 */ /* never allocate the zero-th pool item since the invalid id is 0 */
for (int i = pool->size-1; i >= 1; i--) { for (int i = pool->size-1; i >= 1; i--) {
pool->free_queue[pool->queue_top++] = 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) { static void _sgl_discard_pool(_sgl_pool_t* pool) {
SOKOL_ASSERT(pool); SOKOL_ASSERT(pool);
SOKOL_ASSERT(pool->free_queue); SOKOL_ASSERT(pool->free_queue);
SOKOL_FREE(pool->free_queue); _sgl_free(pool->free_queue);
pool->free_queue = 0; pool->free_queue = 0;
SOKOL_ASSERT(pool->gen_ctrs); SOKOL_ASSERT(pool->gen_ctrs);
SOKOL_FREE(pool->gen_ctrs); _sgl_free(pool->gen_ctrs);
pool->gen_ctrs = 0; pool->gen_ctrs = 0;
pool->size = 0; pool->size = 0;
pool->queue_top = 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) { static void _sgl_reset_context(_sgl_context_t* ctx) {
SOKOL_ASSERT(ctx); SOKOL_ASSERT(ctx);
SOKOL_ASSERT(0 == ctx->vertices); SOKOL_ASSERT(0 == ctx->vertices.ptr);
SOKOL_ASSERT(0 == ctx->uniforms); SOKOL_ASSERT(0 == ctx->uniforms.ptr);
SOKOL_ASSERT(0 == ctx->commands); SOKOL_ASSERT(0 == ctx->commands.ptr);
memset(ctx, 0, sizeof(_sgl_context_t)); _sgl_clear(ctx, sizeof(_sgl_context_t));
} }
static void _sgl_setup_context_pool(int pool_size) { 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)); SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE));
_sgl_init_pool(&_sgl.context_pool.pool, 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; 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); _sgl.context_pool.contexts = (_sgl_context_t*) _sgl_malloc_clear(pool_byte_size);
SOKOL_ASSERT(_sgl.context_pool.contexts);
memset(_sgl.context_pool.contexts, 0, pool_byte_size);
} }
static void _sgl_discard_context_pool(void) { static void _sgl_discard_context_pool(void) {
SOKOL_ASSERT(0 != _sgl.context_pool.contexts); 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); _sgl_discard_pool(&_sgl.context_pool.pool);
} }
static void _sgl_reset_pipeline(_sgl_pipeline_t* pip) { static void _sgl_reset_pipeline(_sgl_pipeline_t* pip) {
SOKOL_ASSERT(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) { 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)); SOKOL_ASSERT((pool_size > 0) && (pool_size < _SGL_MAX_POOL_SIZE));
_sgl_init_pool(&_sgl.pip_pool.pool, 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; 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); _sgl.pip_pool.pips = (_sgl_pipeline_t*) _sgl_malloc_clear(pool_byte_size);
SOKOL_ASSERT(_sgl.pip_pool.pips);
memset(_sgl.pip_pool.pips, 0, pool_byte_size);
} }
static void _sgl_discard_pipeline_pool(void) { static void _sgl_discard_pipeline_pool(void) {
SOKOL_ASSERT(0 != _sgl.pip_pool.pips); 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); _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 { else {
pip->pip[i] = sg_make_pipeline(&desc); pip->pip[i] = sg_make_pipeline(&desc);
if (pip->pip[i].id == SG_INVALID_ID) { 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; 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); _sgl_init_pipeline(pip_id, desc, ctx_desc);
} }
else { else {
SOKOL_LOG("sokol_gl.h: pipeline pool exhausted!"); SGL_LOG("sokol_gl.h: pipeline pool exhausted!");
} }
return pip_id; 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 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) { 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); SOKOL_ASSERT((ctx_id.id != SG_INVALID_ID) && in_desc);
_sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
SOKOL_ASSERT(ctx); SOKOL_ASSERT(ctx);
ctx->desc = _sgl_context_desc_defaults(in_desc); 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; ctx->cur_img = _sgl.def_img;
// allocate buffers and pools // allocate buffers and pools
ctx->num_vertices = ctx->desc.max_vertices; ctx->vertices.cap = ctx->desc.max_vertices;
ctx->num_commands = ctx->num_uniforms = ctx->desc.max_commands; ctx->commands.cap = ctx->uniforms.cap = ctx->desc.max_commands;
ctx->vertices = (_sgl_vertex_t*) SOKOL_MALLOC((size_t)ctx->num_vertices * sizeof(_sgl_vertex_t)); ctx->vertices.ptr = (_sgl_vertex_t*) _sgl_malloc((size_t)ctx->vertices.cap * sizeof(_sgl_vertex_t));
SOKOL_ASSERT(ctx->vertices); ctx->uniforms.ptr = (_sgl_uniform_t*) _sgl_malloc((size_t)ctx->uniforms.cap * sizeof(_sgl_uniform_t));
ctx->uniforms = (_sgl_uniform_t*) SOKOL_MALLOC((size_t)ctx->num_uniforms * sizeof(_sgl_uniform_t)); ctx->commands.ptr = (_sgl_command_t*) _sgl_malloc((size_t)ctx->commands.cap * sizeof(_sgl_command_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);
// create sokol-gfx resource objects // create sokol-gfx resource objects
sg_push_debug_group("sokol-gl"); sg_push_debug_group("sokol-gl");
sg_buffer_desc vbuf_desc; sg_buffer_desc vbuf_desc;
memset(&vbuf_desc, 0, sizeof(vbuf_desc)); _sgl_clear(&vbuf_desc, sizeof(vbuf_desc));
vbuf_desc.size = (size_t)ctx->num_vertices * sizeof(_sgl_vertex_t); vbuf_desc.size = (size_t)ctx->vertices.cap * sizeof(_sgl_vertex_t);
vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER; vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER;
vbuf_desc.usage = SG_USAGE_STREAM; vbuf_desc.usage = SG_USAGE_STREAM;
vbuf_desc.label = "sgl-vertex-buffer"; vbuf_desc.label = "sgl-vertex-buffer";
ctx->vbuf = sg_make_buffer(&vbuf_desc); ctx->vbuf = sg_make_buffer(&vbuf_desc);
SOKOL_ASSERT(SG_INVALID_ID != ctx->vbuf.id); SOKOL_ASSERT(SG_INVALID_ID != ctx->vbuf.id);
ctx->bind.vertex_buffers[0] = ctx->vbuf;
sg_pipeline_desc def_pip_desc; 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; def_pip_desc.depth.write_enabled = true;
ctx->def_pip = _sgl_make_pipeline(&def_pip_desc, &ctx->desc); 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(); sg_pop_debug_group();
// default state // default state
@ -2654,7 +2815,7 @@ static sgl_context _sgl_make_context(const sgl_context_desc_t* desc) {
_sgl_init_context(ctx_id, desc); _sgl_init_context(ctx_id, desc);
} }
else { else {
SOKOL_LOG("sokol_gl.h: context pool exhausted!"); SGL_LOG("sokol_gl.h: context pool exhausted!");
} }
return ctx_id; 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) { static void _sgl_destroy_context(sgl_context ctx_id) {
_sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
if (ctx) { if (ctx) {
SOKOL_ASSERT(ctx->vertices); SOKOL_ASSERT(ctx->vertices.ptr);
SOKOL_ASSERT(ctx->uniforms); SOKOL_ASSERT(ctx->uniforms.ptr);
SOKOL_ASSERT(ctx->commands); SOKOL_ASSERT(ctx->commands.ptr);
SOKOL_FREE(ctx->vertices); _sgl_free(ctx->vertices.ptr);
SOKOL_FREE(ctx->uniforms); _sgl_free(ctx->uniforms.ptr);
SOKOL_FREE(ctx->commands); _sgl_free(ctx->commands.ptr);
ctx->vertices.ptr = 0;
ctx->vertices = 0; ctx->uniforms.ptr = 0;
ctx->uniforms = 0; ctx->commands.ptr = 0;
ctx->commands = 0;
sg_push_debug_group("sokol-gl"); sg_push_debug_group("sokol-gl");
sg_destroy_buffer(ctx->vbuf); sg_destroy_buffer(ctx->vbuf);
_sgl_destroy_pipeline(ctx->def_pip); _sgl_destroy_pipeline(ctx->def_pip);
sg_remove_commit_listener(_sgl_make_commit_listener(ctx));
sg_pop_debug_group(); sg_pop_debug_group();
_sgl_reset_context(ctx); _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->in_begin = true;
ctx->base_vertex = ctx->cur_vertex; ctx->base_vertex = ctx->vertices.next;
ctx->vtx_count = 0; ctx->vtx_count = 0;
ctx->cur_prim_type = mode; ctx->cur_prim_type = mode;
} }
static void _sgl_rewind(_sgl_context_t* ctx) { 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->base_vertex = 0;
ctx->cur_vertex = 0;
ctx->cur_uniform = 0;
ctx->cur_command = 0;
ctx->error = SGL_NO_ERROR; ctx->error = SGL_NO_ERROR;
ctx->layer_id = 0;
ctx->matrix_dirty = true; ctx->matrix_dirty = true;
} }
static inline _sgl_vertex_t* _sgl_next_vertex(_sgl_context_t* ctx) { // called from inside sokol-gfx sg_commit()
if (ctx->cur_vertex < ctx->num_vertices) { static void _sgl_commit_listener(void* userdata) {
return &ctx->vertices[ctx->cur_vertex++]; _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 { else {
ctx->error = SGL_ERROR_VERTICES_FULL; 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) { static _sgl_uniform_t* _sgl_next_uniform(_sgl_context_t* ctx) {
if (ctx->cur_uniform < ctx->num_uniforms) { if (ctx->uniforms.next < ctx->uniforms.cap) {
return &ctx->uniforms[ctx->cur_uniform++]; return &ctx->uniforms.ptr[ctx->uniforms.next++];
} }
else { else {
ctx->error = SGL_ERROR_UNIFORMS_FULL; 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) { static _sgl_command_t* _sgl_cur_command(_sgl_context_t* ctx) {
if (ctx->cur_command > 0) { if (ctx->commands.next > 0) {
return &ctx->commands[ctx->cur_command - 1]; return &ctx->commands.ptr[ctx->commands.next - 1];
} }
else { else {
return 0; return 0;
} }
} }
static inline _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) { static _sgl_command_t* _sgl_next_command(_sgl_context_t* ctx) {
if (ctx->cur_command < ctx->num_commands) { if (ctx->commands.next < ctx->commands.cap) {
return &ctx->commands[ctx->cur_command++]; return &ctx->commands.ptr[ctx->commands.next++];
} }
else { else {
ctx->error = SGL_ERROR_COMMANDS_FULL; 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); 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; if (v < lo) return lo;
else if (v > hi) return hi; else if (v > hi) return hi;
else return v; 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 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 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); 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); 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); SOKOL_ASSERT(ctx->in_begin);
_sgl_vertex_t* vtx; _sgl_vertex_t* vtx;
/* handle non-native primitive types */ /* handle non-native primitive types */
@ -2971,27 +3147,28 @@ static void _sgl_lookat(_sgl_matrix_t* dst,
} }
/* current top-of-stack projection matrix */ /* 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]]; return &ctx->matrix_stack[SGL_MATRIXMODE_PROJECTION][ctx->matrix_tos[SGL_MATRIXMODE_PROJECTION]];
} }
/* get top-of-stack modelview matrix */ /* 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]]; return &ctx->matrix_stack[SGL_MATRIXMODE_MODELVIEW][ctx->matrix_tos[SGL_MATRIXMODE_MODELVIEW]];
} }
/* get top-of-stack texture matrix */ /* 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]]; return &ctx->matrix_stack[SGL_MATRIXMODE_TEXTURE][ctx->matrix_tos[SGL_MATRIXMODE_TEXTURE]];
} }
/* get pointer to current top-of-stack of current matrix mode */ /* 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 &ctx->matrix_stack[ctx->cur_matrix_mode][ctx->matrix_tos[ctx->cur_matrix_mode]];
} }
// return sg_context_desc_t with patched defaults // return sg_context_desc_t with patched defaults
static sgl_desc_t _sgl_desc_defaults(const sgl_desc_t* desc) { 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; sgl_desc_t res = *desc;
res.max_vertices = _sgl_def(desc->max_vertices, _SGL_DEFAULT_MAX_VERTICES); res.max_vertices = _sgl_def(desc->max_vertices, _SGL_DEFAULT_MAX_VERTICES);
res.max_commands = _sgl_def(desc->max_commands, _SGL_DEFAULT_MAX_COMMANDS); 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; pixels[i] = 0xFFFFFFFF;
} }
sg_image_desc img_desc; 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.type = SG_IMAGETYPE_2D;
img_desc.width = 8; img_desc.width = 8;
img_desc.height = 8; img_desc.height = 8;
@ -3025,7 +3202,7 @@ static void _sgl_setup_common(void) {
// one shader for all contexts // one shader for all contexts
sg_shader_desc shd_desc; 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[0].name = "position";
shd_desc.attrs[1].name = "texcoord0"; shd_desc.attrs[1].name = "texcoord0";
shd_desc.attrs[2].name = "color0"; 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; 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); 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_pip_id = SG_INVALID_ID;
uint32_t cur_img_id = SG_INVALID_ID; uint32_t cur_img_id = SG_INVALID_ID;
int cur_uniform_index = -1; 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) }; if (ctx->update_frame_id != ctx->frame_id) {
sg_update_buffer(ctx->vbuf, &range); ctx->update_frame_id = ctx->frame_id;
ctx->bind.vertex_buffers[0] = ctx->vbuf; const sg_range range = { ctx->vertices.ptr, (size_t)ctx->vertices.next * sizeof(_sgl_vertex_t) };
for (int i = 0; i < ctx->cur_command; i++) { sg_update_buffer(ctx->vbuf, &range);
const _sgl_command_t* cmd = &ctx->commands[i]; }
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) { switch (cmd->cmd) {
case SGL_COMMAND_VIEWPORT: case SGL_COMMAND_VIEWPORT:
{ {
@ -3138,7 +3323,7 @@ static void _sgl_draw(_sgl_context_t* ctx) {
cur_img_id = args->img.id; cur_img_id = args->img.id;
} }
if (cur_uniform_index != args->uniform_index) { 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); sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &ub_range);
cur_uniform_index = args->uniform_index; cur_uniform_index = args->uniform_index;
} }
@ -3152,12 +3337,11 @@ static void _sgl_draw(_sgl_context_t* ctx) {
} }
sg_pop_debug_group(); sg_pop_debug_group();
} }
_sgl_rewind(ctx);
} }
static sgl_context_desc_t _sgl_as_context_desc(const sgl_desc_t* desc) { static sgl_context_desc_t _sgl_as_context_desc(const sgl_desc_t* desc) {
sgl_context_desc_t ctx_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_vertices = desc->max_vertices;
ctx_desc.max_commands = desc->max_commands; ctx_desc.max_commands = desc->max_commands;
ctx_desc.color_format = desc->color_format; 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 ========================================================*/ /*== PUBLIC FUNCTIONS ========================================================*/
SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) { SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) {
SOKOL_ASSERT(desc); SOKOL_ASSERT(desc);
memset(&_sgl, 0, sizeof(_sgl)); _sgl_clear(&_sgl, sizeof(_sgl));
_sgl.init_cookie = _SGL_INIT_COOKIE; _sgl.init_cookie = _SGL_INIT_COOKIE;
_sgl.desc = _sgl_desc_defaults(desc); _sgl.desc = _sgl_desc_defaults(desc);
_sgl_setup_pipeline_pool(_sgl.desc.pipeline_pool_size); _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_API_IMPL void sgl_destroy_context(sgl_context ctx_id) {
SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
if (_sgl_is_default_context(ctx_id)) { 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; return;
} }
_sgl_destroy_context(ctx_id); _sgl_destroy_context(ctx_id);
@ -3360,6 +3544,16 @@ SOKOL_API_IMPL void sgl_defaults(void) {
ctx->matrix_dirty = true; 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_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); SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
_sgl_context_t* ctx = _sgl.cur_ctx; _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); _sgl_command_t* cmd = _sgl_next_command(ctx);
if (cmd) { if (cmd) {
cmd->cmd = SGL_COMMAND_VIEWPORT; cmd->cmd = SGL_COMMAND_VIEWPORT;
cmd->layer_id = ctx->layer_id;
cmd->args.viewport.x = x; cmd->args.viewport.x = x;
cmd->args.viewport.y = y; cmd->args.viewport.y = y;
cmd->args.viewport.w = w; 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); _sgl_command_t* cmd = _sgl_next_command(ctx);
if (cmd) { if (cmd) {
cmd->cmd = SGL_COMMAND_SCISSOR_RECT; cmd->cmd = SGL_COMMAND_SCISSOR_RECT;
cmd->layer_id = ctx->layer_id;
cmd->args.scissor_rect.x = x; cmd->args.scissor_rect.x = x;
cmd->args.scissor_rect.y = y; cmd->args.scissor_rect.y = y;
cmd->args.scissor_rect.w = w; cmd->args.scissor_rect.w = w;
@ -3506,7 +3702,7 @@ SOKOL_API_IMPL void sgl_end(void) {
return; return;
} }
SOKOL_ASSERT(ctx->in_begin); 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; ctx->in_begin = false;
bool matrix_dirty = ctx->matrix_dirty; bool matrix_dirty = ctx->matrix_dirty;
if (matrix_dirty) { if (matrix_dirty) {
@ -3517,37 +3713,39 @@ SOKOL_API_IMPL void sgl_end(void) {
uni->tm = *_sgl_matrix_texture(ctx); 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_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; 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; bool merge_cmd = false;
if (prev_cmd) { if (cur_cmd) {
if ((prev_cmd->cmd == SGL_COMMAND_DRAW) && 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_LINE_STRIP) &&
(ctx->cur_prim_type != SGL_PRIMITIVETYPE_TRIANGLE_STRIP) && (ctx->cur_prim_type != SGL_PRIMITIVETYPE_TRIANGLE_STRIP) &&
!matrix_dirty && !matrix_dirty &&
(prev_cmd->args.draw.img.id == img.id) && (cur_cmd->args.draw.img.id == img.id) &&
(prev_cmd->args.draw.pip.id == pip.id)) (cur_cmd->args.draw.pip.id == pip.id))
{ {
merge_cmd = true; merge_cmd = true;
} }
} }
if (merge_cmd) { if (merge_cmd) {
/* draw command can be merged with the previous command */ /* 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 { else {
/* append a new draw command */ /* append a new draw command */
_sgl_command_t* cmd = _sgl_next_command(ctx); _sgl_command_t* cmd = _sgl_next_command(ctx);
if (cmd) { if (cmd) {
SOKOL_ASSERT(ctx->cur_uniform > 0); SOKOL_ASSERT(ctx->uniforms.next > 0);
cmd->cmd = SGL_COMMAND_DRAW; cmd->cmd = SGL_COMMAND_DRAW;
cmd->layer_id = ctx->layer_id;
cmd->args.draw.img = img; 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.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.base_vertex = ctx->base_vertex;
cmd->args.draw.num_vertices = ctx->cur_vertex - ctx->base_vertex; cmd->args.draw.num_vertices = ctx->vertices.next - ctx->base_vertex;
cmd->args.draw.uniform_index = ctx->cur_uniform - 1; 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); SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
_sgl_context_t* ctx = _sgl.cur_ctx; _sgl_context_t* ctx = _sgl.cur_ctx;
if (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); SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie);
_sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id); _sgl_context_t* ctx = _sgl_lookup_context(ctx_id.id);
if (ctx) { 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 module audio
import sokol.memory
$if linux { $if linux {
// provide a nicer error for the user that does not have ALSA installed // provide a nicer error for the user that does not have ALSA installed
#include <alsa/asoundlib.h> # Please install the `libasound2-dev` package #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)} }' 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 // only one of `stream_cb` or `stream_userdata_cb` should be used
// //
// default values (internal to sokol C library): // 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 | // | 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 | // | 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 | // | num_packets | 64 | for push model only, number of packets in the backend ringbuffer |
[typedef]
pub struct C.saudio_desc { pub struct C.saudio_desc {
sample_rate int sample_rate int
num_channels int num_channels int
@ -72,7 +90,10 @@ pub struct C.saudio_desc {
num_packets int num_packets int
stream_cb FNStreamingCB stream_cb FNStreamingCB
stream_userdata_cb FnStreamingCBWithUserData 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) 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 // setup - setup sokol-audio
pub fn setup(desc &C.saudio_desc) { 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) C.saudio_setup(desc)
} }

View File

@ -1,8 +1,6 @@
module c module c
pub const ( pub const used_import = 1
used_import = 1
)
#flag -I @VEXEROOT/thirdparty/sokol #flag -I @VEXEROOT/thirdparty/sokol
#flag -I @VEXEROOT/thirdparty/sokol/util #flag -I @VEXEROOT/thirdparty/sokol/util
@ -58,13 +56,6 @@ $if emscripten ? {
#flag linux -ldl #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 // To allow for thirdparty initializing window / acceleration contexts
// but still be able to use sokol.gfx e.g. SDL+sokol_gfx // but still be able to use sokol.gfx e.g. SDL+sokol_gfx
$if !no_sokol_app ? { $if !no_sokol_app ? {

View File

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

View File

@ -1,19 +1,30 @@
module gfx module gfx
import sokol.c import sokol.c
import sokol.memory
pub const ( pub const version = 1
version = 1
used_import = c.used_import
)
// setup and misc functions pub const used_import = c.used_import
[inline]
// setup initialises the SOKOL's gfx library, based on the information passed in `desc`
pub fn setup(desc &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) C.sg_setup(desc)
} }
[inline] // shutdown tells the SOKOL's gfx library to shutdown, and release the resources it is using
pub fn shutdown() { pub fn shutdown() {
C.sg_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 module gfx
struct C.sg_desc { // C.sg_desc describes
pub struct C.sg_desc {
pub mut: pub mut:
_start_canary u32 _start_canary u32
buffer_pool_size int buffer_pool_size int
image_pool_size int image_pool_size int
shader_pool_size int shader_pool_size int
pipeline_pool_size int pipeline_pool_size int
pass_pool_size int pass_pool_size int
context_pool_size int context_pool_size int
context ContextDesc uniform_buffer_size int
/* staging_buffer_size int
// GL specific sampler_cache_size int
gl_force_gles2 bool max_commit_listeners int
// Metal-specific disable_validation bool // disable validation layer even in debug mode, useful for tests
mtl_device voidptr //
mtl_renderpass_descriptor_cb fn() voidptr allocator C.sg_allocator
mtl_drawable_cb fn() voidptr logger C.sg_logger
mtl_global_uniform_buffer_size int //
mtl_sampler_cache_size int context ContextDesc
// D3D11-specific
d3d11_device voidptr
d3d11_device_context voidptr
d3d11_render_target_view_cb fn() voidptr
d3d11_depth_stencil_view_cb fn() voidptr
*/
_end_canary u32 _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 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 { pub enum TouchToolType {
unknown unknown
finger finger

View File

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

View File

@ -2,13 +2,27 @@ module sfons
import fontstash import fontstash
import sokol.f import sokol.f
import sokol.memory
// keep v from warning about unused imports // keep v from warning about unused imports
const used_import = f.used_import + fontstash.used_import + 1 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] [inline]
pub fn create(width int, height int, flags int) &fontstash.Context { 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] [inline]
@ -25,3 +39,7 @@ pub fn rgba(r u8, g u8, b u8, a u8) u32 {
pub fn flush(ctx &fontstash.Context) { pub fn flush(ctx &fontstash.Context) {
C.sfons_flush(ctx) 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 module sfons
import fontstash 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_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_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 module sgl
import sokol.gfx import sokol.gfx
import sokol.memory
pub const ( pub const version = gfx.version + 1
version = gfx.version + 1
context = Context{0x00010001} // C.SGL_DEFAULT_CONTEXT = { 0x00010001 } pub const context = Context{0x00010001} // C.SGL_DEFAULT_CONTEXT = { 0x00010001 }
)
// setup/shutdown/misc // setup/shutdown/misc
[inline]
pub fn setup(desc &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(0x10000561)
}
}
if desc.logger.log_cb == unsafe { nil } {
unsafe {
desc.logger.log_cb = memory.slog
}
}
C.sgl_setup(desc) C.sgl_setup(desc)
} }
[inline]
pub fn shutdown() { pub fn shutdown() {
C.sgl_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 pub type ContextDesc = C.sgl_context_desc_t
[typedef] [typedef]
struct C.sgl_context_desc_t { pub struct C.sgl_context_desc_t {
max_vertices int // default: 64k max_vertices int // default: 64k
max_commands int // default: 16k max_commands int // default: 16k
color_format gfx.PixelFormat // C.sg_pixel_format 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 pub type Desc = C.sgl_desc_t
[typedef] [typedef]
struct C.sgl_desc_t { pub struct C.sgl_desc_t {
pub:
max_vertices int // size for vertex buffer max_vertices int // size for vertex buffer
max_commands int // size of uniform- and command-buffers max_commands int // size of uniform- and command-buffers
context_pool_size int // max number of contexts (including default context), default: 4 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 depth_format gfx.PixelFormat // C.sg_pixel_format
sample_count int sample_count int
face_winding gfx.FaceWinding // C.sg_face_winding // default front face winding is CCW 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 1 | module module_with_redeclaration
| ^ | ^
2 | 2 |
3 | pub const used = 1 3 | import sokol.memory

View File

@ -1,6 +1,27 @@
module module_with_redeclaration module module_with_redeclaration
import sokol.memory
pub const used = 1 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` 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`
84 | fn C.saudio_userdata() voidptr 105 | fn C.saudio_userdata() voidptr
85 | 106 |
86 | fn C.saudio_query_desc() C.saudio_desc 107 | fn C.saudio_query_desc() C.saudio_desc
| ~~~~~~~~~~~~~ | ~~~~~~~~~~~~~
87 | 108 |
88 | fn C.saudio_sample_rate() int 109 | 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` 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`
99 | 120 |
100 | // setup - setup sokol-audio 121 | // setup - setup sokol-audio
101 | pub fn setup(desc &C.saudio_desc) { 122 | pub fn setup(desc &C.saudio_desc) {
| ~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~
102 | C.saudio_setup(desc) 123 | if desc.allocator.alloc == unsafe { nil } && desc.allocator.free == unsafe { nil } {
103 | } 124 | unsafe {
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` 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`
119 | 123 | if desc.allocator.alloc == unsafe { nil } && desc.allocator.free == unsafe { nil } {
120 | // query - return a copy of the original saudio_desc struct 124 | unsafe {
121 | pub fn query() C.saudio_desc { 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() 155 | return C.saudio_query_desc()
123 | } 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` 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 | 5 |
6 | fn main() { 6 | fn main() {