sokol: update to floooh/sokol from 27-Dec-2021 (4ff3ed7) (#13044)

pull/13052/head
Larpon 2022-01-05 17:23:14 +01:00 committed by GitHub
parent 70a0aab72b
commit 3ae4513e2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 2611 additions and 2147 deletions

View File

@ -252,6 +252,10 @@
may help to prevent casting back and forth between int and float
in more strongly typed languages than C and C++.
double sapp_frame_duration(void)
Returns the frame duration in seconds averaged over a number of
frames to smooth out any jittering spikes.
int sapp_color_format(void)
int sapp_depth_format(void)
The color and depth-stencil pixelformats of the default framebuffer,
@ -411,7 +415,7 @@
- SAPP_EVENTTYPE_MOUSE_DOWN
- SAPP_EVENTTYPE_MOUSE_UP
- SAPP_EVENTTYPE_MOUSE_SCROLL
- SAPP_EVENTYTPE_KEY_UP
- SAPP_EVENTTYPE_KEY_UP
- SAPP_EVENTTYPE_KEY_DOWN
- The mouse lock/unlock action on the web platform is asynchronous,
this means that sapp_mouse_locked() won't immediately return
@ -1378,7 +1382,7 @@ typedef struct sapp_desc {
bool html5_ask_leave_site; // initial state of the internal html5_ask_leave_site flag (see sapp_html5_ask_leave_site())
bool ios_keyboard_resizes_canvas; // if true, showing the iOS keyboard shrinks the canvas
// __v_ start
bool __v_native_render; /* V patch to allow for native rendering */
bool __v_native_render; // V patch to allow for native rendering
// __v_ end
} sapp_desc;
@ -1443,7 +1447,7 @@ SOKOL_APP_API_DECL void sapp_toggle_fullscreen(void);
/* show or hide the mouse cursor */
SOKOL_APP_API_DECL void sapp_show_mouse(bool show);
/* show or hide the mouse cursor */
SOKOL_APP_API_DECL bool sapp_mouse_shown();
SOKOL_APP_API_DECL bool sapp_mouse_shown(void);
/* enable/disable mouse-pointer-lock mode */
SOKOL_APP_API_DECL void sapp_lock_mouse(bool lock);
/* return true if in mouse-pointer-lock mode (this may toggle a few frames later) */
@ -1462,6 +1466,8 @@ SOKOL_APP_API_DECL void sapp_quit(void);
SOKOL_APP_API_DECL void sapp_consume_event(void);
/* get the current frame counter (for comparison with sapp_event.frame_count) */
SOKOL_APP_API_DECL uint64_t sapp_frame_count(void);
/* get an averaged/smoothed frame duration in seconds */
SOKOL_APP_API_DECL double sapp_frame_duration(void);
/* write string into clipboard */
SOKOL_APP_API_DECL void sapp_set_clipboard_string(const char* str);
/* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) */
@ -1685,6 +1691,8 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
#import <GLKit/GLKit.h>
#endif
#endif
#include <AvailabilityMacros.h>
#include <mach/mach_time.h>
#elif defined(_SAPP_EMSCRIPTEN)
#if defined(SOKOL_WGPU)
#include <webgpu/webgpu.h>
@ -1774,6 +1782,7 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
#elif defined(_SAPP_ANDROID)
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include <android/native_activity.h>
#include <android/looper.h>
#include <EGL/egl.h>
@ -1791,8 +1800,209 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
#include <dlfcn.h> /* dlopen, dlsym, dlclose */
#include <limits.h> /* LONG_MAX */
#include <pthread.h> /* only used a linker-guard, search for _sapp_linux_run() and see first comment */
#include <time.h>
#endif
/*== frame timing helpers ===================================================*/
#define _SAPP_RING_NUM_SLOTS (256)
typedef struct {
int head;
int tail;
double buf[_SAPP_RING_NUM_SLOTS];
} _sapp_ring_t;
_SOKOL_PRIVATE int _sapp_ring_idx(int i) {
return i % _SAPP_RING_NUM_SLOTS;
}
_SOKOL_PRIVATE void _sapp_ring_init(_sapp_ring_t* ring) {
ring->head = 0;
ring->tail = 0;
}
_SOKOL_PRIVATE bool _sapp_ring_full(_sapp_ring_t* ring) {
return _sapp_ring_idx(ring->head + 1) == ring->tail;
}
_SOKOL_PRIVATE bool _sapp_ring_empty(_sapp_ring_t* ring) {
return ring->head == ring->tail;
}
_SOKOL_PRIVATE int _sapp_ring_count(_sapp_ring_t* ring) {
int count;
if (ring->head >= ring->tail) {
count = ring->head - ring->tail;
}
else {
count = (ring->head + _SAPP_RING_NUM_SLOTS) - ring->tail;
}
SOKOL_ASSERT((count >= 0) && (count < _SAPP_RING_NUM_SLOTS));
return count;
}
_SOKOL_PRIVATE void _sapp_ring_enqueue(_sapp_ring_t* ring, double val) {
SOKOL_ASSERT(!_sapp_ring_full(ring));
ring->buf[ring->head] = val;
ring->head = _sapp_ring_idx(ring->head + 1);
}
_SOKOL_PRIVATE double _sapp_ring_dequeue(_sapp_ring_t* ring) {
SOKOL_ASSERT(!_sapp_ring_empty(ring));
double val = ring->buf[ring->tail];
ring->tail = _sapp_ring_idx(ring->tail + 1);
return val;
}
/*
NOTE:
Q: Why not use CAMetalDrawable.presentedTime on macOS and iOS?
A: The value appears to be highly unstable during the first few
seconds, sometimes several frames are dropped in sequence, or
switch between 120 and 60 Hz for a few frames. Simply measuring
and averaging the frame time yielded a more stable frame duration.
Maybe switching to CVDisplayLink would yield better results.
Until then just measure the time.
*/
typedef struct {
#if defined(_SAPP_APPLE)
struct {
mach_timebase_info_data_t timebase;
uint64_t start;
} mach;
#elif defined(_SAPP_EMSCRIPTEN)
// empty
#elif defined(_SAPP_WIN32) || defined(_SAPP_UWP)
struct {
LARGE_INTEGER freq;
LARGE_INTEGER start;
} win;
#else // Linux, Android, ...
#ifdef CLOCK_MONOTONIC
#define _SAPP_CLOCK_MONOTONIC CLOCK_MONOTONIC
#else
// on some embedded platforms, CLOCK_MONOTONIC isn't defined
#define _SAPP_CLOCK_MONOTONIC (1)
#endif
struct {
uint64_t start;
} posix;
#endif
} _sapp_timestamp_t;
_SOKOL_PRIVATE int64_t _sapp_int64_muldiv(int64_t value, int64_t numer, int64_t denom) {
int64_t q = value / denom;
int64_t r = value % denom;
return q * numer + r * numer / denom;
}
_SOKOL_PRIVATE void _sapp_timestamp_init(_sapp_timestamp_t* ts) {
#if defined(_SAPP_APPLE)
mach_timebase_info(&ts->mach.timebase);
ts->mach.start = mach_absolute_time();
#elif defined(_SAPP_EMSCRIPTEN)
(void)ts;
#elif defined(_SAPP_WIN32) || defined(_SAPP_UWP)
QueryPerformanceFrequency(&ts->win.freq);
QueryPerformanceCounter(&ts->win.start);
#else
struct timespec tspec;
clock_gettime(_SAPP_CLOCK_MONOTONIC, &tspec);
ts->posix.start = (uint64_t)tspec.tv_sec*1000000000 + (uint64_t)tspec.tv_nsec;
#endif
}
_SOKOL_PRIVATE double _sapp_timestamp_now(_sapp_timestamp_t* ts) {
#if defined(_SAPP_APPLE)
const uint64_t traw = mach_absolute_time() - ts->mach.start;
const uint64_t now = (uint64_t) _sapp_int64_muldiv((int64_t)traw, (int64_t)ts->mach.timebase.numer, (int64_t)ts->mach.timebase.denom);
return (double)now / 1000000000.0;
#elif defined(_SAPP_EMSCRIPTEN)
(void)ts;
SOKOL_ASSERT(false);
return 0.0;
#elif defined(_SAPP_WIN32) || defined(_SAPP_UWP)
LARGE_INTEGER qpc;
QueryPerformanceCounter(&qpc);
const uint64_t now = (uint64_t)_sapp_int64_muldiv(qpc.QuadPart - ts->win.start.QuadPart, 1000000000, ts->win.freq.QuadPart);
return (double)now / 1000000000.0;
#else
struct timespec tspec;
clock_gettime(_SAPP_CLOCK_MONOTONIC, &tspec);
const uint64_t now = ((uint64_t)tspec.tv_sec*1000000000 + (uint64_t)tspec.tv_nsec) - ts->posix.start;
return (double)now / 1000000000.0;
#endif
}
typedef struct {
double last;
double accum;
double avg;
int num;
_sapp_timestamp_t timestamp;
_sapp_ring_t ring;
} _sapp_timing_t;
_SOKOL_PRIVATE void _sapp_timing_init(_sapp_timing_t* t) {
t->last = 0.0;
t->accum = 0.0;
// dummy value until first actual value is available
t->avg = 1.0 / 60.0;
t->num = 0;
_sapp_timestamp_init(&t->timestamp);
_sapp_ring_init(&t->ring);
}
_SOKOL_PRIVATE void _sapp_timing_put(_sapp_timing_t* t, double dur) {
// arbitrary upper limit to ignore outliers (e.g. during window resizing, or debugging)
double min_dur = 0.0;
double max_dur = 0.1;
// if we have enough samples for a useful average, use a much tighter 'valid window'
if (_sapp_ring_full(&t->ring)) {
min_dur = t->avg * 0.8;
max_dur = t->avg * 1.2;
}
if ((dur < min_dur) || (dur > max_dur)) {
return;
}
if (_sapp_ring_full(&t->ring)) {
double old_val = _sapp_ring_dequeue(&t->ring);
t->accum -= old_val;
t->num -= 1;
}
_sapp_ring_enqueue(&t->ring, dur);
t->accum += dur;
t->num += 1;
SOKOL_ASSERT(t->num > 0);
t->avg = t->accum / t->num;
}
_SOKOL_PRIVATE void _sapp_timing_measure(_sapp_timing_t* t) {
const double now = _sapp_timestamp_now(&t->timestamp);
if (t->last > 0.0) {
double dur = now - t->last;
_sapp_timing_put(t, dur);
}
t->last = now;
}
// call this if the external timing had been disrupted somehow
_SOKOL_PRIVATE void _sapp_timing_external_reset(_sapp_timing_t* t) {
t->last = 0.0;
}
_SOKOL_PRIVATE void _sapp_timing_external(_sapp_timing_t* t, double now) {
if (t->last > 0.0) {
double dur = now - t->last;
_sapp_timing_put(t, dur);
}
t->last = now;
}
_SOKOL_PRIVATE double _sapp_timing_get_avg(_sapp_timing_t* t) {
return t->avg;
}
/*== MACOS DECLARATIONS ======================================================*/
#if defined(_SAPP_MACOS)
// __v_ start
@ -1830,9 +2040,8 @@ typedef struct {
uint32_t flags_changed_store;
uint8_t mouse_buttons;
// __v_ start
// NSWindow* window;
// SokolWindow* window; // __v_
_sapp_macos_window* window; // __v_
//NSWindow* window; // __v_ removed
_sapp_macos_window* window; // __v_ added
// __v_ end
NSTrackingArea* tracking_area;
_sapp_macos_app_delegate* app_dlg;
@ -1922,6 +2131,7 @@ typedef struct {
ID3D11DepthStencilView* dsv;
DXGI_SWAP_CHAIN_DESC swap_chain_desc;
IDXGISwapChain* swap_chain;
UINT sync_refresh_count;
} _sapp_d3d11_t;
#endif
@ -2300,6 +2510,7 @@ typedef struct {
int swap_interval;
float dpi_scale;
uint64_t frame_count;
_sapp_timing_t timing;
sapp_event event;
_sapp_mouse_t mouse;
_sapp_clipboard_t clipboard;
@ -2334,7 +2545,6 @@ typedef struct {
char window_title[_SAPP_MAX_TITLE_LENGTH]; /* UTF-8 */
wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; /* UTF-32 or UCS-2 */
sapp_keycode keycodes[SAPP_MAX_KEYCODES];
// __v_ start
bool __v_native_render; /* V patch to allow for native rendering */
// __v_ end
@ -2409,7 +2619,6 @@ _SOKOL_PRIVATE void _sapp_call_cleanup(void) {
}
_SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) {
//puts("_sapp_call_event start");
if (!_sapp.cleanup_called) {
if (_sapp.desc.event_cb) {
_sapp.desc.event_cb(e);
@ -2418,7 +2627,6 @@ _SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) {
_sapp.desc.event_userdata_cb(e, _sapp.desc.user_data);
}
}
//puts("_sapp_call_event end");
if (_sapp.event_consumed) {
_sapp.event_consumed = false;
return true;
@ -2481,6 +2689,14 @@ _SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* in_desc) {
}
_SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) {
SOKOL_ASSERT(desc);
SOKOL_ASSERT(desc->width >= 0);
SOKOL_ASSERT(desc->height >= 0);
SOKOL_ASSERT(desc->sample_count >= 0);
SOKOL_ASSERT(desc->swap_interval >= 0);
SOKOL_ASSERT(desc->clipboard_size >= 0);
SOKOL_ASSERT(desc->max_dropped_files >= 0);
SOKOL_ASSERT(desc->max_dropped_file_path_length >= 0);
_SAPP_CLEAR(_sapp_t, _sapp);
_sapp.desc = _sapp_desc_defaults(desc);
_sapp.first_frame = true;
@ -2511,6 +2727,7 @@ _SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) {
_sapp.dpi_scale = 1.0f;
_sapp.fullscreen = _sapp.desc.fullscreen;
_sapp.mouse.shown = true;
_sapp_timing_init(&_sapp.timing);
// __v_ start
_sapp.__v_native_render = _sapp.desc.__v_native_render;
// __v_end
@ -3034,14 +3251,6 @@ _SOKOL_PRIVATE void _sapp_macos_update_window_title(void) {
[_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]];
}
_SOKOL_PRIVATE void _sapp_macos_resize_window(int width, height) {
[_sapp.macos.window setFrame:NSMakeRect(width, height, width, height) display:YES animate:YES];
//NSRect frame = [window frame];
//frame.size = ;
//[window setFrame: frame display: YES animate: NO];
}
_SOKOL_PRIVATE void _sapp_macos_update_mouse(NSEvent* event) {
if (!_sapp.mouse.locked) {
const NSPoint mouse_pos = event.locationInWindow;
@ -3175,39 +3384,19 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
_sapp.macos.window.acceptsMouseMovedEvents = YES;
_sapp.macos.window.restorable = YES;
// _v__ start
_sapp.macos.window.backgroundColor = [NSColor whiteColor];
// Quit menu
NSMenu* menu_bar = [[NSMenu alloc] init];
NSMenuItem* app_menu_item = [[NSMenuItem alloc] init];
[menu_bar addItem:app_menu_item];
NSApp.mainMenu = menu_bar;
NSMenu* app_menu = [[NSMenu alloc] init];
NSString* window_title_as_nsstring = [NSString stringWithUTF8String:_sapp.window_title];
// `quit_title` memory will be owned by the NSMenuItem, so no need to release it ourselves
NSString* quit_title = [@"Quit " stringByAppendingString:window_title_as_nsstring];
NSMenuItem* quit_menu_item = [[NSMenuItem alloc]
initWithTitle:quit_title
action:@selector(terminate:)
keyEquivalent:@"q"];
[app_menu addItem:quit_menu_item];
app_menu_item.submenu = app_menu;
_SAPP_OBJC_RELEASE( window_title_as_nsstring );
_SAPP_OBJC_RELEASE( app_menu );
_SAPP_OBJC_RELEASE( app_menu_item );
_SAPP_OBJC_RELEASE( menu_bar );
// _v__ end
_sapp.macos.win_dlg = [[_sapp_macos_window_delegate alloc] init];
_sapp.macos.window.delegate = _sapp.macos.win_dlg;
#if defined(SOKOL_METAL)
NSInteger max_fps = 60;
#if (__MAC_OS_X_VERSION_MAX_ALLOWED >= 120000)
if (@available(macOS 12.0, *)) {
max_fps = NSScreen.mainScreen.maximumFramesPerSecond;
}
#endif
_sapp.macos.mtl_device = MTLCreateSystemDefaultDevice();
_sapp.macos.view = [[_sapp_macos_view alloc] init];
[_sapp.macos.view updateTrackingAreas];
_sapp.macos.view.preferredFramesPerSecond = 60 / _sapp.swap_interval;
_sapp.macos.view.preferredFramesPerSecond = max_fps / _sapp.swap_interval;
_sapp.macos.view.device = _sapp.macos.mtl_device;
_sapp.macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
_sapp.macos.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
@ -3300,14 +3489,11 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
[ contentView addSubview:g_view];
//[ _sapp.macos.window addChildWindow:overlayWindow ordered:NSWindowAbove];
[_sapp.macos.window center];
}
//////////////////////////////////
// __v_ end
[_sapp.macos.window makeKeyAndOrderFront:nil];
_sapp_macos_update_dimensions();
// __v_ start
// [NSEvent setMouseCoalescingEnabled:NO];
// __v_ end
@ -3520,6 +3706,7 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() {
- (void)drawRect:(NSRect)rect {
_SOKOL_UNUSED(rect);
_sapp_timing_measure(&_sapp.timing);
/* Catch any last-moment input events */
_sapp_macos_poll_input_events();
@autoreleasepool {
@ -3656,8 +3843,6 @@ _SOKOL_PRIVATE void _sapp_macos_poll_input_events() {
}
}
- (void)keyDown:(NSEvent*)event {
//puts("-keyDown()");
//NSLog(@"%@", event);
if (_sapp_events_enabled()) {
const uint32_t mods = _sapp_macos_mods(event);
/* NOTE: macOS doesn't send keyUp events while the Cmd key is pressed,
@ -3881,10 +4066,11 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) {
}
_sapp.framebuffer_width = _sapp.window_width * _sapp.dpi_scale;
_sapp.framebuffer_height = _sapp.window_height * _sapp.dpi_scale;
NSInteger max_fps = UIScreen.mainScreen.maximumFramesPerSecond;
#if defined(SOKOL_METAL)
_sapp.ios.mtl_device = MTLCreateSystemDefaultDevice();
_sapp.ios.view = [[_sapp_ios_view alloc] init];
_sapp.ios.view.preferredFramesPerSecond = 60 / _sapp.swap_interval;
_sapp.ios.view.preferredFramesPerSecond = max_fps / _sapp.swap_interval;
_sapp.ios.view.device = _sapp.ios.mtl_device;
_sapp.ios.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm;
_sapp.ios.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8;
@ -3924,14 +4110,14 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) {
_sapp.ios.view.multipleTouchEnabled = YES;
// on GLKView, contentScaleFactor appears to work just fine!
if (_sapp.desc.high_dpi) {
_sapp.ios.view.contentScaleFactor = 2.0;
_sapp.ios.view.contentScaleFactor = _sapp.dpi_scale;
}
else {
_sapp.ios.view.contentScaleFactor = 1.0;
}
_sapp.ios.view_ctrl = [[GLKViewController alloc] init];
_sapp.ios.view_ctrl.view = _sapp.ios.view;
_sapp.ios.view_ctrl.preferredFramesPerSecond = 60 / _sapp.swap_interval;
_sapp.ios.view_ctrl.preferredFramesPerSecond = max_fps / _sapp.swap_interval;
_sapp.ios.window.rootViewController = _sapp.ios.view_ctrl;
#endif
[_sapp.ios.window makeKeyAndVisible];
@ -4043,6 +4229,7 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) {
@implementation _sapp_ios_view
- (void)drawRect:(CGRect)rect {
_SOKOL_UNUSED(rect);
_sapp_timing_measure(&_sapp.timing);
@autoreleasepool {
_sapp_ios_frame();
}
@ -5172,8 +5359,8 @@ _SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() {
}
_SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) {
_SOKOL_UNUSED(time);
_SOKOL_UNUSED(userData);
_sapp_timing_external(&_sapp.timing, time / 1000.0);
#if defined(SOKOL_WGPU)
/*
@ -5591,6 +5778,14 @@ static inline HRESULT _sapp_dxgi_Present(IDXGISwapChain* self, UINT SyncInterval
#endif
}
static inline HRESULT _sapp_dxgi_GetFrameStatistics(IDXGISwapChain* self, DXGI_FRAME_STATISTICS* pStats) {
#if defined(__cplusplus)
return self->GetFrameStatistics(pStats);
#else
return self->lpVtbl->GetFrameStatistics(self, pStats);
#endif
}
_SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) {
DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp.d3d11.swap_chain_desc;
sc_desc->BufferDesc.Width = (UINT)_sapp.framebuffer_width;
@ -6234,6 +6429,33 @@ _SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) {
}
}
_SOKOL_PRIVATE void _sapp_win32_timing_measure(void) {
#if defined(SOKOL_D3D11)
// on D3D11, use the more precise DXGI timestamp
DXGI_FRAME_STATISTICS dxgi_stats;
_SAPP_CLEAR(DXGI_FRAME_STATISTICS, dxgi_stats);
HRESULT hr = _sapp_dxgi_GetFrameStatistics(_sapp.d3d11.swap_chain, &dxgi_stats);
if (SUCCEEDED(hr)) {
if (dxgi_stats.SyncRefreshCount != _sapp.d3d11.sync_refresh_count) {
if ((_sapp.d3d11.sync_refresh_count + 1) != dxgi_stats.SyncRefreshCount) {
_sapp_timing_external_reset(&_sapp.timing);
}
_sapp.d3d11.sync_refresh_count = dxgi_stats.SyncRefreshCount;
LARGE_INTEGER qpc = dxgi_stats.SyncQPCTime;
const uint64_t now = (uint64_t)_sapp_int64_muldiv(qpc.QuadPart - _sapp.timing.timestamp.win.start.QuadPart, 1000000000, _sapp.timing.timestamp.win.freq.QuadPart);
_sapp_timing_external(&_sapp.timing, (double)now / 1000000000.0);
}
}
else {
// fallback if GetFrameStats doesn't work for some reason
_sapp_timing_measure(&_sapp.timing);
}
#endif
#if defined(SOKOL_GLCORE33)
_sapp_timing_measure(&_sapp.timing);
#endif
}
_SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (!_sapp.win32.in_create_window) {
switch (uMsg) {
@ -6417,6 +6639,7 @@ _SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM
KillTimer(_sapp.win32.hwnd, 1);
break;
case WM_TIMER:
_sapp_win32_timing_measure();
_sapp_frame();
#if defined(SOKOL_D3D11)
_sapp_d3d11_present();
@ -6789,6 +7012,7 @@ _SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) {
bool done = false;
while (!(done || _sapp.quit_ordered)) {
_sapp_win32_timing_measure();
MSG msg;
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
if (WM_QUIT == msg.message) {
@ -7729,6 +7953,7 @@ void App::Run() {
// NOTE: UWP will simply terminate an application, it's not possible to detect when an application is being closed
while (true) {
if (m_windowVisible) {
_sapp_timing_measure(&_sapp.timing);
winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent);
_sapp_frame();
m_deviceResources->Present();
@ -8108,6 +8333,7 @@ _SOKOL_PRIVATE void _sapp_android_frame(void) {
SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY);
SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT);
SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE);
_sapp_timing_measure(&_sapp.timing);
_sapp_android_update_dimensions(_sapp.android.current.window, false);
_sapp_frame();
eglSwapBuffers(_sapp.android.display, _sapp.android.surface);
@ -10718,6 +10944,7 @@ _SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) {
_sapp_glx_swapinterval(_sapp.swap_interval);
XFlush(_sapp.x11.display);
while (!_sapp.quit_ordered) {
_sapp_timing_measure(&_sapp.timing);
_sapp_glx_make_current();
int count = XPending(_sapp.x11.display);
while (count--) {
@ -10807,6 +11034,10 @@ SOKOL_API_IMPL uint64_t sapp_frame_count(void) {
return _sapp.frame_count;
}
SOKOL_API_IMPL double sapp_frame_duration(void) {
return _sapp_timing_get_avg(&_sapp.timing);
}
SOKOL_API_IMPL int sapp_width(void) {
return (_sapp.framebuffer_width > 0) ? _sapp.framebuffer_width : 1;
}
@ -10995,19 +11226,6 @@ SOKOL_API_IMPL void sapp_set_window_title(const char* title) {
#endif
}
SOKOL_API_IMPL void sapp_resize_window(int width, int height) {
/*
#if defined(_SAPP_MACOS)
_sapp_macos_resize_window(width, height);
#elif defined(_SAPP_WIN32)
_sapp_win32_resize_window();
#elif defined(_SAPP_LINUX)
_sapp_x11_resize_window();
#endif
*/
}
SOKOL_API_IMPL void sapp_set_icon(const sapp_icon_desc* desc) {
SOKOL_ASSERT(desc);
if (desc->sokol_default) {

View File

@ -439,6 +439,8 @@ SOKOL_AUDIO_API_DECL int saudio_sample_rate(void);
SOKOL_AUDIO_API_DECL int saudio_buffer_frames(void);
/* actual number of channels */
SOKOL_AUDIO_API_DECL int saudio_channels(void);
/* return true if audio context is currently suspended (only in WebAudio backend, all other backends return false) */
SOKOL_AUDIO_API_DECL bool saudio_suspended(void);
/* get current number of frames to fill packet queue */
SOKOL_AUDIO_API_DECL int saudio_expect(void);
/* push sample frames from main thread, returns number of frames actually pushed */
@ -562,6 +564,7 @@ inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc);
static const IID _saudio_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483, {0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2} };
static const IID _saudio_IID_Devinterface_Audio_Render = { 0xe6327cad, 0xdcec, 0x4949, {0xae, 0x8a, 0x99, 0x1e, 0x97, 0x6a, 0x79, 0xd2} };
static const IID _saudio_IID_IActivateAudioInterface_Completion_Handler = { 0x94ea2b94, 0xe9cc, 0x49e0, {0xc0, 0xff, 0xee, 0x64, 0xca, 0x8f, 0x5b, 0x90} };
static const GUID _saudio_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} };
#if defined(__cplusplus)
#define _SOKOL_AUDIO_WIN32COM_ID(x) (x)
#else
@ -821,7 +824,6 @@ typedef struct {
#endif
IAudioClient* audio_client;
IAudioRenderClient* render_client;
int si16_bytes_per_frame;
_saudio_wasapi_thread_data_t thread;
} _saudio_backend_t;
@ -1422,6 +1424,10 @@ _SOKOL_PRIVATE void _saudio_wasapi_fill_buffer(void) {
}
}
_SOKOL_PRIVATE int _saudio_wasapi_min(int a, int b) {
return (a < b) ? a : b;
}
_SOKOL_PRIVATE void _saudio_wasapi_submit_buffer(int num_frames) {
BYTE* wasapi_buffer = 0;
if (FAILED(IAudioRenderClient_GetBuffer(_saudio.backend.render_client, num_frames, &wasapi_buffer))) {
@ -1429,24 +1435,34 @@ _SOKOL_PRIVATE void _saudio_wasapi_submit_buffer(int num_frames) {
}
SOKOL_ASSERT(wasapi_buffer);
/* convert float samples to int16_t, refill float buffer if needed */
const int num_samples = num_frames * _saudio.num_channels;
int16_t* dst = (int16_t*) wasapi_buffer;
/* copy samples to WASAPI buffer, refill source buffer if needed */
int num_remaining_samples = num_frames * _saudio.num_channels;
int buffer_pos = _saudio.backend.thread.src_buffer_pos;
const int buffer_float_size = _saudio.backend.thread.src_buffer_byte_size / (int)sizeof(float);
float* src = _saudio.backend.thread.src_buffer;
for (int i = 0; i < num_samples; i++) {
const int buffer_size_in_samples = _saudio.backend.thread.src_buffer_byte_size / (int)sizeof(float);
float* dst = (float*)wasapi_buffer;
const float* dst_end = dst + num_remaining_samples;
_SOKOL_UNUSED(dst_end); // suppress unused warning in release mode
const float* src = _saudio.backend.thread.src_buffer;
while (num_remaining_samples > 0) {
if (0 == buffer_pos) {
_saudio_wasapi_fill_buffer();
}
dst[i] = (int16_t) (src[buffer_pos] * 0x7FFF);
buffer_pos += 1;
if (buffer_pos == buffer_float_size) {
const int samples_to_copy = _saudio_wasapi_min(num_remaining_samples, buffer_size_in_samples - buffer_pos);
SOKOL_ASSERT((buffer_pos + samples_to_copy) <= buffer_size_in_samples);
SOKOL_ASSERT((dst + samples_to_copy) <= dst_end);
memcpy(dst, &src[buffer_pos], (size_t)samples_to_copy * sizeof(float));
num_remaining_samples -= samples_to_copy;
SOKOL_ASSERT(num_remaining_samples >= 0);
buffer_pos += samples_to_copy;
dst += samples_to_copy;
SOKOL_ASSERT(buffer_pos <= buffer_size_in_samples);
if (buffer_pos == buffer_size_in_samples) {
buffer_pos = 0;
}
}
_saudio.backend.thread.src_buffer_pos = buffer_pos;
IAudioRenderClient_ReleaseBuffer(_saudio.backend.render_client, num_frames, 0);
}
@ -1584,20 +1600,25 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
goto error;
}
#endif
WAVEFORMATEX fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.nChannels = (WORD)_saudio.num_channels;
fmt.nSamplesPerSec = (DWORD)_saudio.sample_rate;
fmt.wFormatTag = WAVE_FORMAT_PCM;
fmt.wBitsPerSample = 16;
fmt.nBlockAlign = (fmt.nChannels * fmt.wBitsPerSample) / 8;
fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
WAVEFORMATEXTENSIBLE fmtex;
memset(&fmtex, 0, sizeof(fmtex));
fmtex.Format.nChannels = (WORD)_saudio.num_channels;
fmtex.Format.nSamplesPerSec = (DWORD)_saudio.sample_rate;
fmtex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
fmtex.Format.wBitsPerSample = 32;
fmtex.Format.nBlockAlign = (fmtex.Format.nChannels * fmtex.Format.wBitsPerSample) / 8;
fmtex.Format.nAvgBytesPerSec = fmtex.Format.nSamplesPerSec * fmtex.Format.nBlockAlign;
fmtex.Format.cbSize = 22; /* WORD + DWORD + GUID */
fmtex.Samples.wValidBitsPerSample = 32;
fmtex.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
fmtex.SubFormat = _saudio_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
dur = (REFERENCE_TIME)
(((double)_saudio.buffer_frames) / (((double)_saudio.sample_rate) * (1.0/10000000.0)));
if (FAILED(IAudioClient_Initialize(_saudio.backend.audio_client,
AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK|AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM|AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY,
dur, 0, &fmt, 0)))
dur, 0, (WAVEFORMATEX*)&fmtex, 0)))
{
SOKOL_LOG("sokol_audio wasapi: audio client initialize failed");
goto error;
@ -1617,7 +1638,6 @@ _SOKOL_PRIVATE bool _saudio_backend_init(void) {
SOKOL_LOG("sokol_audio wasapi: audio client SetEventHandle failed");
goto error;
}
_saudio.backend.si16_bytes_per_frame = _saudio.num_channels * (int)sizeof(int16_t);
_saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float);
_saudio.backend.thread.src_buffer_frames = _saudio.buffer_frames;
_saudio.backend.thread.src_buffer_byte_size = _saudio.backend.thread.src_buffer_frames * _saudio.bytes_per_frame;
@ -1776,6 +1796,18 @@ EM_JS(int, saudio_js_buffer_frames, (void), {
}
});
/* return 1 if the WebAudio context is currently suspended, else 0 */
EM_JS(int, saudio_js_suspended, (void), {
if (Module._saudio_context) {
if (Module._saudio_context.state === 'suspended') {
return 1;
}
else {
return 0;
}
}
});
_SOKOL_PRIVATE bool _saudio_backend_init(void) {
if (saudio_js_init(_saudio.sample_rate, _saudio.num_channels, _saudio.buffer_frames)) {
_saudio.bytes_per_frame = (int)sizeof(float) * _saudio.num_channels;
@ -2113,6 +2145,19 @@ SOKOL_API_IMPL int saudio_channels(void) {
return _saudio.num_channels;
}
SOKOL_API_IMPL bool saudio_suspended(void) {
#if defined(_SAUDIO_EMSCRIPTEN)
if (_saudio.valid) {
return 1 == saudio_js_suspended();
}
else {
return false;
}
#else
return false;
#endif
}
SOKOL_API_IMPL int saudio_expect(void) {
if (_saudio.valid) {
const int num_frames = _saudio_fifo_writable_bytes(&_saudio.fifo) / _saudio.bytes_per_frame;

View File

@ -2976,10 +2976,10 @@ typedef struct {
/* helper macros */
#define _sg_def(val, def) (((val) == 0) ? (def) : (val))
#define _sg_def_flt(val, def) (((val) == 0.0f) ? (def) : (val))
#define _sg_min(a,b) ((a<b)?a:b)
#define _sg_max(a,b) ((a>b)?a:b)
#define _sg_clamp(v,v0,v1) ((v<v0)?(v0):((v>v1)?(v1):(v)))
#define _sg_fequal(val,cmp,delta) (((val-cmp)> -delta)&&((val-cmp)<delta))
#define _sg_min(a,b) (((a)<(b))?(a):(b))
#define _sg_max(a,b) (((a)>(b))?(a):(b))
#define _sg_clamp(v,v0,v1) (((v)<(v0))?(v0):(((v)>(v1))?(v1):(v)))
#define _sg_fequal(val,cmp,delta) ((((val)-(cmp))> -(delta))&&(((val)-(cmp))<(delta)))
typedef struct {
int size;
@ -3098,6 +3098,7 @@ _SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_sh
typedef struct {
sg_shader shader_id;
sg_index_type index_type;
bool use_instanced_draw;
bool vertex_layout_valid[SG_MAX_SHADERSTAGE_BUFFERS];
int color_attachment_count;
sg_pixel_format color_formats[SG_MAX_COLOR_ATTACHMENTS];
@ -3113,6 +3114,7 @@ _SOKOL_PRIVATE void _sg_pipeline_common_init(_sg_pipeline_common_t* cmn, const s
SOKOL_ASSERT((desc->color_count >= 1) && (desc->color_count <= SG_MAX_COLOR_ATTACHMENTS));
cmn->shader_id = desc->shader;
cmn->index_type = desc->index_type;
cmn->use_instanced_draw = false;
for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) {
cmn->vertex_layout_valid[i] = false;
}
@ -3584,6 +3586,7 @@ typedef struct {
void* user_data;
bool in_pass;
bool use_indexed_draw;
bool use_instanced_draw;
int cur_width;
int cur_height;
int num_rtvs;
@ -3912,6 +3915,10 @@ typedef enum {
_SG_VALIDATE_BUFFERDESC_DATA_SIZE,
_SG_VALIDATE_BUFFERDESC_NO_DATA,
/* image data (for image creation and updating) */
_SG_VALIDATE_IMAGEDATA_NODATA,
_SG_VALIDATE_IMAGEDATA_DATA_SIZE,
/* image creation */
_SG_VALIDATE_IMAGEDESC_CANARY,
_SG_VALIDATE_IMAGEDESC_WIDTH,
@ -3922,8 +3929,9 @@ typedef enum {
_SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT,
_SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE,
_SG_VALIDATE_IMAGEDESC_RT_NO_DATA,
_SG_VALIDATE_IMAGEDESC_DATA,
_SG_VALIDATE_IMAGEDESC_NO_DATA,
_SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA,
_SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA,
_SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE,
/* shader creation */
_SG_VALIDATE_SHADERDESC_CANARY,
@ -4019,8 +4027,6 @@ typedef enum {
/* sg_update_image validation */
_SG_VALIDATE_UPDIMG_USAGE,
_SG_VALIDATE_UPDIMG_NOTENOUGHDATA,
_SG_VALIDATE_UPDIMG_SIZE,
_SG_VALIDATE_UPDIMG_COMPRESSED,
_SG_VALIDATE_UPDIMG_ONCE
} _sg_validate_error_t;
@ -4234,7 +4240,21 @@ _SOKOL_PRIVATE int _sg_roundup(int val, int round_to) {
}
/* return row pitch for an image
see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp
For the special PVRTC pitch computation, see:
GL extension requirement (https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt)
Quote:
6) How is the imageSize argument calculated for the CompressedTexImage2D
and CompressedTexSubImage2D functions.
Resolution: For PVRTC 4BPP formats the imageSize is calculated as:
( max(width, 8) * max(height, 8) * 4 + 7) / 8
For PVRTC 2BPP formats the imageSize is calculated as:
( max(width, 16) * max(height, 8) * 2 + 7) / 8
*/
_SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) {
int pitch;
@ -4262,23 +4282,11 @@ _SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align)
break;
case SG_PIXELFORMAT_PVRTC_RGB_4BPP:
case SG_PIXELFORMAT_PVRTC_RGBA_4BPP:
{
const int block_size = 4*4;
const int bpp = 4;
int width_blocks = width / 4;
width_blocks = width_blocks < 2 ? 2 : width_blocks;
pitch = width_blocks * ((block_size * bpp) / 8);
}
pitch = (_sg_max(width, 8) * 4 + 7) / 8;
break;
case SG_PIXELFORMAT_PVRTC_RGB_2BPP:
case SG_PIXELFORMAT_PVRTC_RGBA_2BPP:
{
const int block_size = 8*4;
const int bpp = 2;
int width_blocks = width / 4;
width_blocks = width_blocks < 2 ? 2 : width_blocks;
pitch = width_blocks * ((block_size * bpp) / 8);
}
pitch = (_sg_max(width, 16) * 2 + 7) / 8;
break;
default:
pitch = width * _sg_pixelformat_bytesize(fmt);
@ -4307,11 +4315,19 @@ _SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) {
case SG_PIXELFORMAT_BC6H_RGBF:
case SG_PIXELFORMAT_BC6H_RGBUF:
case SG_PIXELFORMAT_BC7_RGBA:
num_rows = ((height + 3) / 4);
break;
case SG_PIXELFORMAT_PVRTC_RGB_4BPP:
case SG_PIXELFORMAT_PVRTC_RGBA_4BPP:
case SG_PIXELFORMAT_PVRTC_RGB_2BPP:
case SG_PIXELFORMAT_PVRTC_RGBA_2BPP:
num_rows = ((height + 3) / 4);
/* NOTE: this is most likely not correct because it ignores any
PVCRTC block size, but multiplied with _sg_row_pitch()
it gives the correct surface pitch.
See: https://www.khronos.org/registry/OpenGL/extensions/IMG/IMG_texture_compression_pvrtc.txt
*/
num_rows = ((_sg_max(height, 8) + 7) / 8) * 8;
break;
default:
num_rows = height;
@ -6327,7 +6343,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_
gl_img_target = _sg_gl_cubeface_target(face_index);
}
const GLvoid* data_ptr = desc->data.subimage[face_index][mip_index].ptr;
const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size;
int mip_width = img->cmn.width >> mip_index;
if (mip_width == 0) {
mip_width = 1;
@ -6338,6 +6353,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_
}
if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) {
if (is_compressed) {
const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size;
glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format,
mip_width, mip_height, 0, data_size, data_ptr);
}
@ -6357,6 +6373,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_
mip_depth = 1;
}
if (is_compressed) {
const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size;
glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format,
mip_width, mip_height, mip_depth, 0, data_size, data_ptr);
}
@ -6585,6 +6602,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg
}
else {
gl_attr->divisor = (int8_t) step_rate;
pip->cmn.use_instanced_draw = true;
}
SOKOL_ASSERT(l_desc->stride > 0);
gl_attr->stride = (uint8_t) l_desc->stride;
@ -7340,6 +7358,7 @@ _SOKOL_PRIVATE void _sg_gl_apply_uniforms(sg_shader_stage stage_index, int ub_in
}
_SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_instances) {
SOKOL_ASSERT(_sg.gl.cache.cur_pipeline);
const GLenum i_type = _sg.gl.cache.cur_index_type;
const GLenum p_type = _sg.gl.cache.cur_primitive_type;
if (0 != i_type) {
@ -7347,25 +7366,25 @@ _SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_inst
const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4;
const int ib_offset = _sg.gl.cache.cur_ib_offset;
const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset);
if (num_instances == 1) {
glDrawElements(p_type, num_elements, i_type, indices);
}
else {
if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) {
if (_sg.features.instancing) {
glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances);
}
}
else {
glDrawElements(p_type, num_elements, i_type, indices);
}
}
else {
/* non-indexed rendering */
if (num_instances == 1) {
glDrawArrays(p_type, base_element, num_elements);
}
else {
if (_sg.gl.cache.cur_pipeline->cmn.use_instanced_draw) {
if (_sg.features.instancing) {
glDrawArraysInstanced(p_type, base_element, num_elements, num_instances);
}
}
else {
glDrawArrays(p_type, base_element, num_elements);
}
}
}
@ -8242,8 +8261,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, cons
init_data_ptr = &init_data;
}
HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_desc, init_data_ptr, &buf->d3d11.buf);
_SOKOL_UNUSED(hr);
SOKOL_ASSERT(SUCCEEDED(hr) && buf->d3d11.buf);
if (!(SUCCEEDED(hr) && buf->d3d11.buf)) {
SOKOL_LOG("failed to create D3D11 buffer\n");
return SG_RESOURCESTATE_FAILED;
}
}
return SG_RESOURCESTATE_VALID;
}
@ -8289,7 +8310,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const
SOKOL_ASSERT(!img->d3d11.tex2d && !img->d3d11.tex3d && !img->d3d11.texds && !img->d3d11.texmsaa);
SOKOL_ASSERT(!img->d3d11.srv && !img->d3d11.smp);
HRESULT hr;
_SOKOL_UNUSED(hr);
_sg_image_common_init(&img->cmn, desc);
const bool injected = (0 != desc->d3d11_texture) || (0 != desc->d3d11_shader_resource_view);
@ -8316,7 +8336,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const
d3d11_desc.SampleDesc.Count = (UINT)img->cmn.sample_count;
d3d11_desc.SampleDesc.Quality = (UINT) (msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0);
hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_desc, NULL, &img->d3d11.texds);
SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.texds);
if (!(SUCCEEDED(hr) && img->d3d11.texds)) {
SOKOL_LOG("failed to create D3D11 texture 2D\n");
return SG_RESOURCESTATE_FAILED;
}
}
else {
/* create (or inject) color texture and shader-resource-view */
@ -8386,7 +8409,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const
d3d11_tex_desc.MiscFlags = (img->cmn.type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0;
hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex2d);
SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.tex2d);
if (!(SUCCEEDED(hr) && img->d3d11.tex2d)) {
SOKOL_LOG("failed to create D3D11 texture 2D\n");
return SG_RESOURCESTATE_FAILED;
}
}
/* ...and similar, if not injected, create shader-resource-view */
@ -8412,7 +8438,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const
SOKOL_UNREACHABLE; break;
}
hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex2d, &d3d11_srv_desc, &img->d3d11.srv);
SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.srv);
if (!(SUCCEEDED(hr) && img->d3d11.srv)) {
SOKOL_LOG("failed to create D3D11 resource view\n");
return SG_RESOURCESTATE_FAILED;
}
}
}
else {
@ -8459,7 +8488,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const
return SG_RESOURCESTATE_FAILED;
}
hr = _sg_d3d11_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex3d);
SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.tex3d);
if (!(SUCCEEDED(hr) && img->d3d11.tex3d)) {
SOKOL_LOG("failed to create D3D11 texture 3D\n");
return SG_RESOURCESTATE_FAILED;
}
}
if (0 == img->d3d11.srv) {
@ -8469,7 +8501,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const
d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
d3d11_srv_desc.Texture3D.MipLevels = (UINT)img->cmn.num_mipmaps;
hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex3d, &d3d11_srv_desc, &img->d3d11.srv);
SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.srv);
if (!(SUCCEEDED(hr) && img->d3d11.srv)) {
SOKOL_LOG("failed to create D3D11 resource view\n");
return SG_RESOURCESTATE_FAILED;
}
}
}
@ -8488,7 +8523,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const
d3d11_tex_desc.SampleDesc.Count = (UINT)img->cmn.sample_count;
d3d11_tex_desc.SampleDesc.Quality = (UINT)D3D11_STANDARD_MULTISAMPLE_PATTERN;
hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, NULL, &img->d3d11.texmsaa);
SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.texmsaa);
if (!(SUCCEEDED(hr) && img->d3d11.texmsaa)) {
SOKOL_LOG("failed to create D3D11 texture 2D\n");
return SG_RESOURCESTATE_FAILED;
}
}
/* sampler state object, note D3D11 implements an internal shared-pool for sampler objects */
@ -8517,7 +8555,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const
d3d11_smp_desc.MinLOD = desc->min_lod;
d3d11_smp_desc.MaxLOD = desc->max_lod;
hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &img->d3d11.smp);
SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.smp);
if (!(SUCCEEDED(hr) && img->d3d11.smp)) {
SOKOL_LOG("failed to create D3D11 sampler state\n");
return SG_RESOURCESTATE_FAILED;
}
}
return SG_RESOURCESTATE_VALID;
}
@ -8609,7 +8650,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons
SOKOL_ASSERT(shd && desc);
SOKOL_ASSERT(!shd->d3d11.vs && !shd->d3d11.fs && !shd->d3d11.vs_blob);
HRESULT hr;
_SOKOL_UNUSED(hr);
_sg_shader_common_init(&shd->cmn, desc);
@ -8634,7 +8674,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, cons
cb_desc.Usage = D3D11_USAGE_DEFAULT;
cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &d3d11_stage->cbufs[ub_index]);
SOKOL_ASSERT(SUCCEEDED(hr) && d3d11_stage->cbufs[ub_index]);
if (!(SUCCEEDED(hr) && d3d11_stage->cbufs[ub_index])) {
SOKOL_LOG("failed to create D3D11 buffer\n");
return SG_RESOURCESTATE_FAILED;
}
}
}
@ -8722,7 +8765,6 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip,
/* create input layout object */
HRESULT hr;
_SOKOL_UNUSED(hr);
D3D11_INPUT_ELEMENT_DESC d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES];
memset(d3d11_comps, 0, sizeof(d3d11_comps));
int attr_index = 0;
@ -8744,6 +8786,7 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip,
d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func);
if (SG_VERTEXSTEP_PER_INSTANCE == step_func) {
d3d11_comp->InstanceDataStepRate = (UINT)step_rate;
pip->cmn.use_instanced_draw = true;
}
pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true;
}
@ -8763,7 +8806,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip,
shd->d3d11.vs_blob, /* pShaderByteCodeWithInputSignature */
shd->d3d11.vs_blob_length, /* BytecodeLength */
&pip->d3d11.il);
SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.il);
if (!(SUCCEEDED(hr) && pip->d3d11.il)) {
SOKOL_LOG("failed to create D3D11 input layout\n");
return SG_RESOURCESTATE_FAILED;
}
/* create rasterizer state */
D3D11_RASTERIZER_DESC rs_desc;
@ -8779,7 +8825,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip,
rs_desc.MultisampleEnable = desc->sample_count > 1;
rs_desc.AntialiasedLineEnable = FALSE;
hr = _sg_d3d11_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11.rs);
SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.rs);
if (!(SUCCEEDED(hr) && pip->d3d11.rs)) {
SOKOL_LOG("failed to create D3D11 rasterizer state\n");
return SG_RESOURCESTATE_FAILED;
}
/* create depth-stencil state */
D3D11_DEPTH_STENCIL_DESC dss_desc;
@ -8801,7 +8850,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip,
dss_desc.BackFace.StencilPassOp = _sg_d3d11_stencil_op(sb->pass_op);
dss_desc.BackFace.StencilFunc = _sg_d3d11_compare_func(sb->compare);
hr = _sg_d3d11_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11.dss);
SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.dss);
if (!(SUCCEEDED(hr) && pip->d3d11.dss)) {
SOKOL_LOG("failed to create D3D11 depth stencil state\n");
return SG_RESOURCESTATE_FAILED;
}
/* create blend state */
D3D11_BLEND_DESC bs_desc;
@ -8832,7 +8884,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip,
}
}
hr = _sg_d3d11_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11.bs);
SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.bs);
if (!(SUCCEEDED(hr) && pip->d3d11.bs)) {
SOKOL_LOG("failed to create D3D11 blend state\n");
return SG_RESOURCESTATE_FAILED;
}
return SG_RESOURCESTATE_VALID;
}
@ -8910,8 +8965,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pass(_sg_pass_t* pass, _sg_ima
}
SOKOL_ASSERT(d3d11_res);
HRESULT hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, d3d11_res, &d3d11_rtv_desc, &pass->d3d11.color_atts[i].rtv);
_SOKOL_UNUSED(hr);
SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11.color_atts[i].rtv);
if (!(SUCCEEDED(hr) && pass->d3d11.color_atts[i].rtv)) {
SOKOL_LOG("failed to create D3D11 render target view\n");
return SG_RESOURCESTATE_FAILED;
}
}
/* optional depth-stencil image */
@ -8941,8 +8998,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pass(_sg_pass_t* pass, _sg_ima
ID3D11Resource* d3d11_res = (ID3D11Resource*) att_img->d3d11.texds;
SOKOL_ASSERT(d3d11_res);
HRESULT hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, d3d11_res, &d3d11_dsv_desc, &pass->d3d11.ds_att.dsv);
_SOKOL_UNUSED(hr);
SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11.ds_att.dsv);
if (!(SUCCEEDED(hr) && pass->d3d11.ds_att.dsv)) {
SOKOL_LOG("failed to create D3D11 depth stencil view\n");
return SG_RESOURCESTATE_FAILED;
}
}
return SG_RESOURCESTATE_VALID;
}
@ -9124,6 +9183,7 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) {
_sg.d3d11.cur_pipeline = pip;
_sg.d3d11.cur_pipeline_id.id = pip->slot.id;
_sg.d3d11.use_indexed_draw = (pip->d3d11.index_format != DXGI_FORMAT_UNKNOWN);
_sg.d3d11.use_instanced_draw = pip->cmn.use_instanced_draw;
_sg_d3d11_RSSetState(_sg.d3d11.ctx, pip->d3d11.rs);
_sg_d3d11_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11.dss, pip->d3d11.stencil_ref);
@ -9208,20 +9268,20 @@ _SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub
_SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances) {
SOKOL_ASSERT(_sg.d3d11.in_pass);
if (_sg.d3d11.use_indexed_draw) {
if (1 == num_instances) {
_sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, 0);
}
else {
if (_sg.d3d11.use_instanced_draw) {
_sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0);
}
else {
_sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, 0);
}
}
else {
if (1 == num_instances) {
_sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element);
}
else {
if (_sg.d3d11.use_instanced_draw) {
_sg_d3d11_DrawInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0);
}
else {
_sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element);
}
}
}
@ -9235,10 +9295,12 @@ _SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const sg_range* d
SOKOL_ASSERT(buf->d3d11.buf);
D3D11_MAPPED_SUBRESOURCE d3d11_msr;
HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr);
_SOKOL_UNUSED(hr);
SOKOL_ASSERT(SUCCEEDED(hr));
if (SUCCEEDED(hr)) {
memcpy(d3d11_msr.pData, data->ptr, data->size);
_sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0);
} else {
SOKOL_LOG("failed to map buffer while updating!\n");
}
}
_SOKOL_PRIVATE int _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) {
@ -9248,12 +9310,14 @@ _SOKOL_PRIVATE int _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* da
D3D11_MAP map_type = new_frame ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE;
D3D11_MAPPED_SUBRESOURCE d3d11_msr;
HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, map_type, 0, &d3d11_msr);
_SOKOL_UNUSED(hr);
SOKOL_ASSERT(SUCCEEDED(hr));
if (SUCCEEDED(hr)) {
uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData + buf->cmn.append_pos;
memcpy(dst_ptr, data->ptr, data->size);
_sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0);
/* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */
} else {
SOKOL_LOG("failed to map buffer while appending!\n");
}
/* NOTE: this alignment is a requirement from WebGPU, but we want identical behaviour across all backend */
return _sg_roundup((int)data->size, 4);
}
@ -9273,7 +9337,6 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data
const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1;
UINT subres_index = 0;
HRESULT hr;
_SOKOL_UNUSED(hr);
D3D11_MAPPED_SUBRESOURCE d3d11_msr;
for (int face_index = 0; face_index < num_faces; face_index++) {
for (int slice_index = 0; slice_index < num_slices; slice_index++) {
@ -9287,7 +9350,7 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data
const size_t slice_offset = slice_size * (size_t)slice_index;
const uint8_t* slice_ptr = ((const uint8_t*)subimg_data->ptr) + slice_offset;
hr = _sg_d3d11_Map(_sg.d3d11.ctx, d3d11_res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr);
SOKOL_ASSERT(SUCCEEDED(hr));
if (SUCCEEDED(hr)) {
/* FIXME: need to handle difference in depth-pitch for 3D textures as well! */
if (src_pitch == (int)d3d11_msr.RowPitch) {
memcpy(d3d11_msr.pData, slice_ptr, slice_size);
@ -9303,6 +9366,9 @@ _SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data
}
}
_sg_d3d11_Unmap(_sg.d3d11.ctx, d3d11_res, subres_index);
} else {
SOKOL_LOG("failed to map texture!\n");
}
}
}
}
@ -10110,22 +10176,31 @@ _SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unr
const uint8_t* data_ptr = (const uint8_t*)data->subimage[face_index][mip_index].ptr;
const int mip_width = _sg_max(img->cmn.width >> mip_index, 1);
const int mip_height = _sg_max(img->cmn.height >> mip_index, 1);
/* special case PVRTC formats: bytePerRow must be 0 */
/* special case PVRTC formats: bytePerRow and bytesPerImage must be 0 */
int bytes_per_row = 0;
int bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1);
int bytes_per_slice = 0;
if (!_sg_mtl_is_pvrtc(img->cmn.pixel_format)) {
bytes_per_row = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1);
bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1);
}
/* bytesPerImage special case: https://developer.apple.com/documentation/metal/mtltexture/1515679-replaceregion
"Supply a nonzero value only when you copy data to a MTLTextureType3D type texture"
*/
MTLRegion region;
int bytes_per_image;
if (img->cmn.type == SG_IMAGETYPE_3D) {
const int mip_depth = _sg_max(img->cmn.num_slices >> mip_index, 1);
region = MTLRegionMake3D(0, 0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height, (NSUInteger)mip_depth);
bytes_per_image = bytes_per_slice;
/* FIXME: apparently the minimal bytes_per_image size for 3D texture
is 4 KByte... somehow need to handle this */
}
else {
region = MTLRegionMake2D(0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height);
bytes_per_image = 0;
}
for (int slice_index = 0; slice_index < num_slices; slice_index++) {
const int mtl_slice_index = (img->cmn.type == SG_IMAGETYPE_CUBE) ? face_index : slice_index;
const int slice_offset = slice_index * bytes_per_slice;
@ -10135,7 +10210,7 @@ _SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unr
slice:(NSUInteger)mtl_slice_index
withBytes:data_ptr + slice_offset
bytesPerRow:(NSUInteger)bytes_per_row
bytesPerImage:(NSUInteger)bytes_per_slice];
bytesPerImage:(NSUInteger)bytes_per_image];
}
}
}
@ -10431,6 +10506,10 @@ _SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _s
vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_desc->stride;
vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_desc->step_func);
vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_desc->step_rate;
if (SG_VERTEXSTEP_PER_INSTANCE == l_desc->step_func) {
// NOTE: not actually used in _sg_mtl_draw()
pip->cmn.use_instanced_draw = true;
}
}
}
@ -13563,6 +13642,10 @@ _SOKOL_PRIVATE const char* _sg_validate_string(_sg_validate_error_t err) {
case _SG_VALIDATE_BUFFERDESC_DATA_SIZE: return "immutable buffer data size differs from buffer size";
case _SG_VALIDATE_BUFFERDESC_NO_DATA: return "dynamic/stream usage buffers cannot be initialized with data";
/* image data (in image creation and updating) */
case _SG_VALIDATE_IMAGEDATA_NODATA: return "sg_image_data: no data (.ptr and/or .size is zero)";
case _SG_VALIDATE_IMAGEDATA_DATA_SIZE: return "sg_image_data: data size doesn't match expected surface size";
/* image creation validation errros */
case _SG_VALIDATE_IMAGEDESC_CANARY: return "sg_image_desc not initialized";
case _SG_VALIDATE_IMAGEDESC_WIDTH: return "sg_image_desc.width must be > 0";
@ -13573,8 +13656,9 @@ _SOKOL_PRIVATE const char* _sg_validate_string(_sg_validate_error_t err) {
case _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT: return "MSAA not supported for this pixel format";
case _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE: return "render target images must be SG_USAGE_IMMUTABLE";
case _SG_VALIDATE_IMAGEDESC_RT_NO_DATA: return "render target images cannot be initialized with data";
case _SG_VALIDATE_IMAGEDESC_DATA: return "missing or invalid data for immutable image";
case _SG_VALIDATE_IMAGEDESC_NO_DATA: return "dynamic/stream usage images cannot be initialized with data";
case _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA: return "images with injected textures cannot be initialized with data";
case _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA: return "dynamic/stream images cannot be initialized with data";
case _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE: return "compressed images must be immutable";
/* shader creation */
case _SG_VALIDATE_SHADERDESC_CANARY: return "sg_shader_desc not initialized";
@ -13669,9 +13753,6 @@ _SOKOL_PRIVATE const char* _sg_validate_string(_sg_validate_error_t err) {
/* sg_update_image */
case _SG_VALIDATE_UPDIMG_USAGE: return "sg_update_image: cannot update immutable image";
case _SG_VALIDATE_UPDIMG_NOTENOUGHDATA: return "sg_update_image: not enough subimage data provided";
case _SG_VALIDATE_UPDIMG_SIZE: return "sg_update_image: provided subimage data size too big";
case _SG_VALIDATE_UPDIMG_COMPRESSED: return "sg_update_image: cannot update images with compressed format";
case _SG_VALIDATE_UPDIMG_ONCE: return "sg_update_image: only one update allowed per image and frame";
default: return "unknown validation error";
@ -13695,7 +13776,7 @@ _SOKOL_PRIVATE void _sg_validate(bool cond, _sg_validate_error_t err) {
_SOKOL_PRIVATE bool _sg_validate_end(void) {
if (_sg.validate_error != _SG_VALIDATE_SUCCESS) {
#if !defined(SOKOL_VALIDATE_NON_FATAL)
SOKOL_LOG("^^^^ VALIDATION FAILED, TERMINATING ^^^^");
SOKOL_LOG("^^^^ SOKOL-GFX VALIDATION FAILED, TERMINATING ^^^^");
SOKOL_ASSERT(false);
#endif
return false;
@ -13731,6 +13812,31 @@ _SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) {
#endif
}
_SOKOL_PRIVATE void _sg_validate_image_data(const sg_image_data* data, sg_pixel_format fmt, int width, int height, int num_faces, int num_mips, int num_slices) {
#if !defined(SOKOL_DEBUG)
_SOKOL_UNUSED(data);
_SOKOL_UNUSED(fmt);
_SOKOL_UNUSED(width);
_SOKOL_UNUSED(height);
_SOKOL_UNUSED(num_faces);
_SOKOL_UNUSED(num_mips);
_SOKOL_UNUSED(num_slices);
#else
for (int face_index = 0; face_index < num_faces; face_index++) {
for (int mip_index = 0; mip_index < num_mips; mip_index++) {
const bool has_data = data->subimage[face_index][mip_index].ptr != 0;
const bool has_size = data->subimage[face_index][mip_index].size > 0;
SOKOL_VALIDATE(has_data && has_size, _SG_VALIDATE_IMAGEDATA_NODATA);
const int mip_width = _sg_max(width >> mip_index, 1);
const int mip_height = _sg_max(height >> mip_index, 1);
const int bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1);
const int expected_size = bytes_per_slice * num_slices;
SOKOL_VALIDATE(expected_size == (int)data->subimage[face_index][mip_index].size, _SG_VALIDATE_IMAGEDATA_DATA_SIZE);
}
}
#endif
}
_SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) {
#if !defined(SOKOL_DEBUG)
_SOKOL_UNUSED(desc);
@ -13768,24 +13874,33 @@ _SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) {
SOKOL_VALIDATE(desc->sample_count <= 1, _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT);
const bool valid_nonrt_fmt = !_sg_is_valid_rendertarget_depth_format(fmt);
SOKOL_VALIDATE(valid_nonrt_fmt, _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT);
/* FIXME: should use the same "expected size" computation as in _sg_validate_update_image() here */
if (!injected && (usage == SG_USAGE_IMMUTABLE)) {
const int num_faces = desc->type == SG_IMAGETYPE_CUBE ? 6:1;
const int num_mips = desc->num_mipmaps;
for (int face_index = 0; face_index < num_faces; face_index++) {
for (int mip_index = 0; mip_index < num_mips; mip_index++) {
const bool has_data = desc->data.subimage[face_index][mip_index].ptr != 0;
const bool has_size = desc->data.subimage[face_index][mip_index].size > 0;
SOKOL_VALIDATE(has_data && has_size, _SG_VALIDATE_IMAGEDESC_DATA);
}
const bool is_compressed = _sg_is_compressed_pixel_format(desc->pixel_format);
const bool is_immutable = (usage == SG_USAGE_IMMUTABLE);
if (is_compressed) {
SOKOL_VALIDATE(is_immutable, _SG_VALIDATE_IMAGEDESC_COMPRESSED_IMMUTABLE);
}
if (!injected && is_immutable) {
// image desc must have valid data
_sg_validate_image_data(&desc->data,
desc->pixel_format,
desc->width,
desc->height,
(desc->type == SG_IMAGETYPE_CUBE) ? 6 : 1,
desc->num_mipmaps,
desc->num_slices);
}
else {
// image desc must not have data
for (int face_index = 0; face_index < SG_CUBEFACE_NUM; face_index++) {
for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) {
const bool no_data = 0 == desc->data.subimage[face_index][mip_index].ptr;
const bool no_size = 0 == desc->data.subimage[face_index][mip_index].size;
SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_NO_DATA);
if (injected) {
SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_INJECTED_NO_DATA);
}
if (!is_immutable) {
SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_DYNAMIC_NO_DATA);
}
}
}
}
@ -13866,6 +13981,9 @@ _SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) {
#if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3)
SOKOL_VALIDATE((size_t)uniform_offset == ub_desc->size, _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH);
SOKOL_VALIDATE(num_uniforms > 0, _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS);
#else
_SOKOL_UNUSED(uniform_offset);
_SOKOL_UNUSED(num_uniforms);
#endif
}
else {
@ -14233,19 +14351,13 @@ _SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_i
SOKOL_VALIDATE_BEGIN();
SOKOL_VALIDATE(img->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDIMG_USAGE);
SOKOL_VALIDATE(img->cmn.upd_frame_index != _sg.frame_index, _SG_VALIDATE_UPDIMG_ONCE);
SOKOL_VALIDATE(!_sg_is_compressed_pixel_format(img->cmn.pixel_format), _SG_VALIDATE_UPDIMG_COMPRESSED);
const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1;
const int num_mips = img->cmn.num_mipmaps;
for (int face_index = 0; face_index < num_faces; face_index++) {
for (int mip_index = 0; mip_index < num_mips; mip_index++) {
SOKOL_VALIDATE(0 != data->subimage[face_index][mip_index].ptr, _SG_VALIDATE_UPDIMG_NOTENOUGHDATA);
const int mip_width = _sg_max(img->cmn.width >> mip_index, 1);
const int mip_height = _sg_max(img->cmn.height >> mip_index, 1);
const int bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1);
const int expected_size = bytes_per_slice * img->cmn.num_slices;
SOKOL_VALIDATE(data->subimage[face_index][mip_index].size <= (size_t)expected_size, _SG_VALIDATE_UPDIMG_SIZE);
}
}
_sg_validate_image_data(data,
img->cmn.pixel_format,
img->cmn.width,
img->cmn.height,
(img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1,
img->cmn.num_mipmaps,
img->cmn.num_slices);
return SOKOL_VALIDATE_END();
#endif
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -768,7 +768,6 @@ pub fn (ctx &Context) draw_slice_empty(x f32, y f32, r f32, start_angle f32, arc
pub fn (mut ctx Context) resize(width int, height int) {
ctx.width = width
ctx.height = height
// C.sapp_resize_window(width, height)
}
// Draws a line between the points provided

View File

@ -53,6 +53,8 @@ fn C.saudio_buffer_frames() int
fn C.saudio_channels() int
fn C.saudio_suspended() bool
fn C.saudio_expect() int
fn C.saudio_push(frames &f32, num_frames int) int
@ -97,6 +99,12 @@ pub fn channels() int {
return C.saudio_channels()
}
// suspended returns true if audio context is currently suspended
// (only in WebAudio backend, all other backends return false)
pub fn suspended() bool {
return C.saudio_suspended()
}
// audio.expect - get current number of frames to fill packet queue; use in combination with audio.push/2
pub fn expect() int {
return C.saudio_expect()

View File

@ -136,6 +136,12 @@ pub fn frame_count() u64 {
return C.sapp_frame_count()
}
// get an averaged/smoothed frame duration in seconds
[inline]
pub fn frame_duration() f64 {
return C.sapp_frame_duration()
}
// write string into clipboard
[inline]
pub fn set_clipboard_string(str &char) {

View File

@ -56,6 +56,9 @@ fn C.sapp_consume_event()
// get the current frame counter (for comparison with sapp_event.frame_count)
fn C.sapp_frame_count() u64
// get an averaged/smoothed frame duration in seconds
fn C.sapp_frame_duration() f64
// write string into clipboard
fn C.sapp_set_clipboard_string(str &byte)
@ -115,5 +118,3 @@ fn C.sapp_get_num_dropped_files() int
// Get the file path of the droped file
fn C.sapp_get_dropped_file_path(int) &byte
fn C.sapp_resize_window(int, int)

View File

@ -1,6 +1,6 @@
module sgl
// Error is C.sgl_error_t
// SglError is C.sgl_error_t
pub enum SglError {
no_error = C.SGL_NO_ERROR // 0
vertices_full = C.SGL_ERROR_VERTICES_FULL

View File

@ -254,6 +254,11 @@ pub fn c1i(rgba u32) {
C.sgl_c1i(rgba)
}
[inline]
pub fn point_size(s f32) {
C.sgl_point_size(s)
}
// define primitives, each begin/end is one draw command
[inline]
pub fn begin_points() {

View File

@ -62,6 +62,7 @@ fn C.sgl_c4f(r f32, g f32, b f32, a f32)
fn C.sgl_c3b(r byte, g byte, b byte)
fn C.sgl_c4b(r byte, g byte, b byte, a byte)
fn C.sgl_c1i(rgba u32)
fn C.sgl_point_size(s f32)
// define primitives, each begin/end is one draw command
fn C.sgl_begin_points()