From 6a43363539181529cfdc087277477ee5213781cb Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Sat, 11 Jul 2026 23:51:39 -0500 Subject: [PATCH] Optimized --- cmake/targets/knulli.cmake | 1 + cmake/targets/vita.cmake | 1 + src/dusk/animation/easing.c | 7 +- src/dusk/asset/loader/assetloader.h | 19 ++++ .../asset/loader/display/assettilesetloader.c | 14 ++- src/dusk/asset/loader/dmf/assetmeshloader.c | 9 +- src/dusk/asset/loader/json/assetjsonloader.c | 9 +- .../asset/loader/locale/assetlocaleloader.c | 32 +++++++ .../asset/loader/locale/assetlocaleloader.h | 27 ++++++ src/dusk/display/text/text.c | 2 +- src/dusk/entity/component.c | 4 - .../entity/component/display/entityposition.c | 12 ++- src/dusk/physics/physicsworld.c | 60 +++++++++++++ src/dusk/scene/scene.c | 43 ++++++---- src/dusk/scene/scene.h | 4 + src/dusk/script/scriptmanager.c | 33 ++++++- src/dusk/script/scriptmanager.h | 9 +- src/dusk/thread/threadmutex.c | 2 + src/dusk/time/time.c | 8 -- src/dusk/ui/debug/uiconsole.c | 1 + src/dusk/ui/debug/uifps.c | 7 +- src/dusk/ui/widget/uidropdown.c | 9 +- src/dusk/ui/widget/uidropdown.h | 2 + src/dusk/ui/widget/uislider.c | 12 +-- src/dusk/ui/widget/uislider.h | 2 + src/dusk/ui/widget/uitab.c | 6 +- src/dusk/ui/widget/uitab.h | 2 + src/dusk/util/crypt.c | 6 -- src/dusk/util/crypt.h | 9 -- src/dusk/util/memory.c | 9 -- src/dusk/util/memory.h | 8 -- src/dusk/util/random.c | 3 + .../display/framebuffer/framebuffergl.c | 8 +- src/duskgl/display/mesh/meshgl.c | 10 +-- src/duskgl/display/shader/shadergl.c | 17 +++- src/duskgl/display/shader/shadergl.h | 9 ++ src/duskgl/display/shader/shaderunlitgl.c | 38 ++++---- src/dusksdl2/display/displaysdl2.c | 86 +++++++++++++------ src/dusksdl2/display/displaysdl2.h | 2 + test/util/test_memory.c | 34 -------- 40 files changed, 386 insertions(+), 190 deletions(-) diff --git a/cmake/targets/knulli.cmake b/cmake/targets/knulli.cmake index 140aecae..9f1d1fc9 100644 --- a/cmake/targets/knulli.cmake +++ b/cmake/targets/knulli.cmake @@ -43,4 +43,5 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC DUSK_INPUT_POINTER DUSK_INPUT_GAMEPAD DUSK_TIME_DYNAMIC + DUSK_THREAD_PTHREAD ) \ No newline at end of file diff --git a/cmake/targets/vita.cmake b/cmake/targets/vita.cmake index eadf73e1..7ce15093 100644 --- a/cmake/targets/vita.cmake +++ b/cmake/targets/vita.cmake @@ -75,6 +75,7 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC DUSK_OPENGL_LEGACY DUSK_DISPLAY_WIDTH=960 DUSK_DISPLAY_HEIGHT=544 + DUSK_THREAD_PTHREAD ) # Post-build: create SELF from the ELF binary (UNSAFE = homebrew, no signing) diff --git a/src/dusk/animation/easing.c b/src/dusk/animation/easing.c index 19cbf12a..34064af6 100644 --- a/src/dusk/animation/easing.c +++ b/src/dusk/animation/easing.c @@ -5,7 +5,6 @@ #include "easing.h" #include "assert/assert.h" -#include "util/math.h" const easingfn_t EASING_FUNCTIONS[EASING_COUNT] = { easingLinear, @@ -36,15 +35,15 @@ float_t easingLinear(const float_t t) { } float_t easingInSine(const float_t t) { - return 1.0f - cosf(t * MATH_PI * 0.5f); + return 1.0f - cosf(t * EASING_PI * 0.5f); } float_t easingOutSine(const float_t t) { - return sinf(t * MATH_PI * 0.5f); + return sinf(t * EASING_PI * 0.5f); } float_t easingInOutSine(const float_t t) { - return -(cosf(MATH_PI * t) - 1.0f) * 0.5f; + return -(cosf(EASING_PI * t) - 1.0f) * 0.5f; } float_t easingInQuad(const float_t t) { diff --git a/src/dusk/asset/loader/assetloader.h b/src/dusk/asset/loader/assetloader.h index a58319b3..3bfafe9c 100644 --- a/src/dusk/asset/loader/assetloader.h +++ b/src/dusk/asset/loader/assetloader.h @@ -84,6 +84,25 @@ extern assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT]; } \ } +/** + * Like @ref assetLoaderErrorChain, but also frees `_ptr` (via memoryFree) + * before chaining the error if `_expr` failed. Use this for any loader step + * that runs after a buffer has already been allocated, so a later I/O + * failure doesn't leak it. + * + * @param loading The asset loading slot. + * @param _ptr A heap pointer to free if `_expr` fails. + * @param _expr The error return value to check and chain if it's an error. + */ +#define assetLoaderErrorChainFree(loading, _ptr, _expr) {\ + errorret_t _alecf = (_expr); \ + if(errorIsNotOk(_alecf)) { \ + memoryFree(_ptr); \ + (loading)->entry->state = ASSET_ENTRY_STATE_ERROR; \ + errorChain(_alecf); \ + } \ +} + /** * Shorthand method to both throw an error (against the loader state) and to * set the asset entry state to error. diff --git a/src/dusk/asset/loader/display/assettilesetloader.c b/src/dusk/asset/loader/display/assettilesetloader.c index 3e9cba92..5fe9844d 100644 --- a/src/dusk/asset/loader/display/assettilesetloader.c +++ b/src/dusk/asset/loader/display/assettilesetloader.c @@ -27,11 +27,12 @@ errorret_t assetTilesetLoaderAsync(assetloading_t *loading) { assetFileInit(file, loading->entry->name, NULL, NULL) ); - uint8_t *data = memoryAllocate(file->size); assetLoaderErrorChain(loading, assetFileOpen(file)); - assetLoaderErrorChain(loading, assetFileRead(file, data, file->size)); - assetLoaderErrorChain(loading, assetFileClose(file)); - assetLoaderErrorChain(loading, assetFileDispose(file)); + + uint8_t *data = memoryAllocate(file->size); + assetLoaderErrorChainFree(loading, data, assetFileRead(file, data, file->size)); + assetLoaderErrorChainFree(loading, data, assetFileClose(file)); + assetLoaderErrorChainFree(loading, data, assetFileDispose(file)); assertTrue( file->lastRead == file->size, "Failed to read entire tileset file." @@ -102,6 +103,11 @@ errorret_t assetTilesetLoaderSync(assetloading_t *loading) { out->uv[0] = endianLittleToHostFloat(*(float *)(data + 16)); out->uv[1] = endianLittleToHostFloat(*(float *)(data + 20)); + if(out->uv[0] < 0.0f || out->uv[0] > 1.0f) { + memoryFree(data); + assetLoaderErrorThrow(loading, "Invalid u0 value in tileset"); + } + if(out->uv[1] < 0.0f || out->uv[1] > 1.0f) { memoryFree(data); assetLoaderErrorThrow(loading, "Invalid v0 value in tileset"); diff --git a/src/dusk/asset/loader/dmf/assetmeshloader.c b/src/dusk/asset/loader/dmf/assetmeshloader.c index de40a622..9aaf37a3 100644 --- a/src/dusk/asset/loader/dmf/assetmeshloader.c +++ b/src/dusk/asset/loader/dmf/assetmeshloader.c @@ -27,11 +27,12 @@ errorret_t assetMeshLoaderAsync(assetloading_t *loading) { assetFileInit(file, loading->entry->name, NULL, NULL) ); - uint8_t *raw = memoryAllocate(file->size); assetLoaderErrorChain(loading, assetFileOpen(file)); - assetLoaderErrorChain(loading, assetFileRead(file, raw, file->size)); - assetLoaderErrorChain(loading, assetFileClose(file)); - assetLoaderErrorChain(loading, assetFileDispose(file)); + + uint8_t *raw = memoryAllocate(file->size); + assetLoaderErrorChainFree(loading, raw, assetFileRead(file, raw, file->size)); + assetLoaderErrorChainFree(loading, raw, assetFileClose(file)); + assetLoaderErrorChainFree(loading, raw, assetFileDispose(file)); assertTrue(file->lastRead == file->size, "Failed to read entire DMF file."); if(raw[0] != 'D' || raw[1] != 'M' || raw[2] != 'F') { diff --git a/src/dusk/asset/loader/json/assetjsonloader.c b/src/dusk/asset/loader/json/assetjsonloader.c index c8505cca..7f63d276 100644 --- a/src/dusk/asset/loader/json/assetjsonloader.c +++ b/src/dusk/asset/loader/json/assetjsonloader.c @@ -32,13 +32,14 @@ errorret_t assetJsonLoaderAsync(assetloading_t *loading) { assetLoaderErrorThrow(loading, "JSON exceeds maximum allowed size"); } + assetLoaderErrorChain(loading, assetFileOpen(file)); + size_t fileSize = (size_t)file->size; uint8_t *buffer = memoryAllocate(fileSize); - assetLoaderErrorChain(loading, assetFileOpen(file)); - assetLoaderErrorChain(loading, assetFileRead(file, buffer, fileSize)); + assetLoaderErrorChainFree(loading, buffer, assetFileRead(file, buffer, fileSize)); assertTrue(file->lastRead == file->size, "Failed to read entire JSON file."); - assetLoaderErrorChain(loading, assetFileClose(file)); - assetLoaderErrorChain(loading, assetFileDispose(file)); + assetLoaderErrorChainFree(loading, buffer, assetFileClose(file)); + assetLoaderErrorChainFree(loading, buffer, assetFileDispose(file)); loading->loading.json.buffer = buffer; loading->loading.json.size = fileSize; diff --git a/src/dusk/asset/loader/locale/assetlocaleloader.c b/src/dusk/asset/loader/locale/assetlocaleloader.c index c8374eb4..375cfa25 100644 --- a/src/dusk/asset/loader/locale/assetlocaleloader.c +++ b/src/dusk/asset/loader/locale/assetlocaleloader.c @@ -482,6 +482,24 @@ errorret_t assetLocaleGetString( assertTrue(pluralCount >= 0, "Plural index cannot be negative."); assertNotNull(stringBuffer, "String buffer cannot be NULL."); assertTrue(stringBufferSize > 0, "String buffer size must be > 0"); + + // Check the cache before rewinding/rescanning the whole file. + for(uint8_t i = 0; i < ASSET_LOCALE_STRING_CACHE_SIZE; i++) { + assetlocalecacheentry_t *entry = &file->stringCache[i]; + if( + !entry->valid || + entry->pluralCount != pluralCount || + stringCompare(messageId, entry->messageId) != 0 + ) continue; + + size_t valueLen = strlen(entry->value); + if(valueLen >= stringBufferSize) { + errorThrow("String buffer overflow"); + } + memoryCopy(stringBuffer, entry->value, valueLen + 1); + errorOk(); + } + assetfilelinereader_t reader; bool_t msgidFound = false, msgidPluralFound = false, msgstrFound = false; @@ -625,6 +643,20 @@ errorret_t assetLocaleGetString( errorThrow("Failed to find msgstr for message ID: %s", messageId); } + // Cache the resolved string for future lookups, if it fits. + if( + strlen(messageId) < ASSET_LOCALE_CACHE_MESSAGE_ID_MAX && + strlen(stringBuffer) < ASSET_LOCALE_CACHE_VALUE_MAX + ) { + assetlocalecacheentry_t *entry = &file->stringCache[file->stringCacheNext]; + stringCopy(entry->messageId, messageId, sizeof(entry->messageId) - 1); + stringCopy(entry->value, stringBuffer, sizeof(entry->value) - 1); + entry->pluralCount = pluralCount; + entry->valid = true; + file->stringCacheNext = + (file->stringCacheNext + 1) % ASSET_LOCALE_STRING_CACHE_SIZE; + } + errorOk(); } diff --git a/src/dusk/asset/loader/locale/assetlocaleloader.h b/src/dusk/asset/loader/locale/assetlocaleloader.h index 79dfa31e..4b7e9a84 100644 --- a/src/dusk/asset/loader/locale/assetlocaleloader.h +++ b/src/dusk/asset/loader/locale/assetlocaleloader.h @@ -28,6 +28,27 @@ typedef struct { /** Maximum number of distinct plural forms a locale file may declare. */ #define ASSET_LOCALE_FILE_PLURAL_FORM_COUNT 6 +/** Max distinct resolved strings cached per open locale file. */ +#define ASSET_LOCALE_STRING_CACHE_SIZE 16 + +/** Max message ID length (incl. null terminator) storable in the cache. */ +#define ASSET_LOCALE_CACHE_MESSAGE_ID_MAX 128 + +/** Max resolved string length (incl. null terminator) storable in the cache. */ +#define ASSET_LOCALE_CACHE_VALUE_MAX 256 + +/** + * A single cached (messageId, pluralCount) -> resolved string lookup, used by + * @ref assetLocaleGetString to avoid rescanning the whole PO file for + * repeated lookups of the same message. + */ +typedef struct { + bool_t valid; + char_t messageId[ASSET_LOCALE_CACHE_MESSAGE_ID_MAX]; + int32_t pluralCount; + char_t value[ASSET_LOCALE_CACHE_VALUE_MAX]; +} assetlocalecacheentry_t; + /** * Comparison operator used in a plural-form expression. * @@ -98,6 +119,12 @@ typedef struct { /** Form index used when no conditional clause matches. */ uint8_t pluralDefaultIndex; + + /** Ring buffer of recently resolved (messageId, pluralCount) lookups. */ + assetlocalecacheentry_t stringCache[ASSET_LOCALE_STRING_CACHE_SIZE]; + + /** Next slot in @ref stringCache to overwrite. */ + uint8_t stringCacheNext; } assetlocalefile_t; /** Convenience alias - the loaded output type of a locale asset entry. */ diff --git a/src/dusk/display/text/text.c b/src/dusk/display/text/text.c index dde87683..bc3b6534 100644 --- a/src/dusk/display/text/text.c +++ b/src/dusk/display/text/text.c @@ -51,7 +51,7 @@ spritebatchsprite_t textGetSprite( tileIndex = ((int32_t)'@') - TEXT_CHAR_START; } assertTrue( - tileIndex >= 0 && tileIndex <= font->tileset->tileCount, + tileIndex >= 0 && tileIndex < font->tileset->tileCount, "Character is out of bounds for font tiles" ); diff --git a/src/dusk/entity/component.c b/src/dusk/entity/component.c index 935e5d56..415ce2d0 100644 --- a/src/dusk/entity/component.c +++ b/src/dusk/entity/component.c @@ -105,10 +105,6 @@ entityid_t componentGetEntitiesWithComponent( componentGetIndex(i,used) < ENTITY_COUNT_MAX*ENTITY_COMPONENT_COUNT_MAX, "Component index OOB in entitiesWithComponent lookup" ); - assertTrue( - ENTITY_MANAGER.components[componentGetIndex(i,used)].type == type, - "Component type mismatch in entitiesWithComponent lookup" - ); outComponents[written] = used; outEntities[written++] = i; } diff --git a/src/dusk/entity/component/display/entityposition.c b/src/dusk/entity/component/display/entityposition.c index 3c1c0c30..3f5f6b43 100644 --- a/src/dusk/entity/component/display/entityposition.c +++ b/src/dusk/entity/component/display/entityposition.c @@ -220,9 +220,15 @@ entityposition_t *entityPositionGet( void entityPositionRebuild(entityposition_t *pos) { glm_mat4_identity(pos->localTransform); glm_translate(pos->localTransform, pos->position); - glm_rotate_x(pos->localTransform, pos->rotation[0], pos->localTransform); - glm_rotate_y(pos->localTransform, pos->rotation[1], pos->localTransform); - glm_rotate_z(pos->localTransform, pos->rotation[2], pos->localTransform); + if(pos->rotation[0] != 0.0f) { + glm_rotate_x(pos->localTransform, pos->rotation[0], pos->localTransform); + } + if(pos->rotation[1] != 0.0f) { + glm_rotate_y(pos->localTransform, pos->rotation[1], pos->localTransform); + } + if(pos->rotation[2] != 0.0f) { + glm_rotate_z(pos->localTransform, pos->rotation[2], pos->localTransform); + } glm_scale(pos->localTransform, pos->scale); entityPositionMarkDirty(pos); } diff --git a/src/dusk/physics/physicsworld.c b/src/dusk/physics/physicsworld.c index 02e24ec7..9ebbee6b 100644 --- a/src/dusk/physics/physicsworld.c +++ b/src/dusk/physics/physicsworld.c @@ -14,6 +14,35 @@ physicsworld_t PHYSICS_WORLD; +/** Below this squared speed, a grounded body is considered at rest. */ +#define PHYSICS_REST_VELOCITY_EPSILON_SQ 0.0001f + +/** + * Computes a conservative (never-too-small) AABB half-extent for a shape, + * used as a cheap broad-phase pre-filter before the full narrow-phase test. + * Returns false for unbounded shapes (planes), which skip the pre-filter. + */ +static bool_t physicsShapeBroadphaseExtents( + const physicsshape_t shape, vec3 outHalfExtents +) { + switch(shape.type) { + case PHYSICS_SHAPE_CUBE: + glm_vec3_copy((float_t *)shape.data.cube.halfExtents, outHalfExtents); + return true; + case PHYSICS_SHAPE_SPHERE: + outHalfExtents[0] = outHalfExtents[1] = outHalfExtents[2] = + shape.data.sphere.radius; + return true; + case PHYSICS_SHAPE_CAPSULE: + outHalfExtents[0] = outHalfExtents[2] = shape.data.capsule.radius; + outHalfExtents[1] = + shape.data.capsule.radius + shape.data.capsule.halfHeight; + return true; + default: + return false; + } +} + void physicsWorldInit() { memoryZero(&PHYSICS_WORLD, sizeof(physicsworld_t)); @@ -34,6 +63,8 @@ void physicsWorldStep(const float_t dt) { /* Pre-fetch all position and physics pointers once. */ entityposition_t *positions[ENTITY_COUNT_MAX]; entityphysics_t *physBodies[ENTITY_COUNT_MAX]; + vec3 boundsHalfExtents[ENTITY_COUNT_MAX]; + bool_t boundsValid[ENTITY_COUNT_MAX]; for(entityid_t i = 0; i < physCount; i++) { componentid_t posComp = entityGetComponent( physEnts[i], COMPONENT_TYPE_POSITION @@ -42,6 +73,9 @@ void physicsWorldStep(const float_t dt) { ? entityPositionGet(physEnts[i], posComp) : NULL; physBodies[i] = entityPhysicsGet(physEnts[i], physComps[i]); + boundsValid[i] = physicsShapeBroadphaseExtents( + physBodies[i]->shape, boundsHalfExtents[i] + ); } /* Phase 1: integrate dynamic bodies (gravity + velocity → position). @@ -51,8 +85,16 @@ void physicsWorldStep(const float_t dt) { entityphysics_t *phys = physBodies[i]; if(phys->type != PHYSICS_BODY_DYNAMIC) continue; + // A body resting from the previous tick skips gravity/velocity + // integration this tick, but still takes part in phases 2/3 below so it + // correctly notices being pushed or its support disappearing. + bool_t wasResting = phys->onGround && + glm_vec3_norm2(phys->velocity) < PHYSICS_REST_VELOCITY_EPSILON_SQ; + phys->onGround = false; + if(wasResting) continue; + phys->velocity[0] += PHYSICS_WORLD.gravity[0] * phys->gravityScale * dt; phys->velocity[1] += PHYSICS_WORLD.gravity[1] * phys->gravityScale * dt; phys->velocity[2] += PHYSICS_WORLD.gravity[2] * phys->gravityScale * dt; @@ -76,6 +118,15 @@ void physicsWorldStep(const float_t dt) { entityphysics_t *otherPhys = physBodies[j]; if(otherPhys->type == PHYSICS_BODY_DYNAMIC) continue; + if(boundsValid[i] && boundsValid[j]) { + vec3 bpNormal; float_t bpDepth; + if(!physicsTestAabbVsAabb( + pos, boundsHalfExtents[i], + positions[j]->position, boundsHalfExtents[j], + bpNormal, &bpDepth + )) continue; + } + vec3 normal; float_t depth; if(!physicsTestShapeVsShape( pos, phys->shape, @@ -113,6 +164,15 @@ void physicsWorldStep(const float_t dt) { float_t *posB = positions[j]->position; + if(boundsValid[i] && boundsValid[j]) { + vec3 bpNormal; float_t bpDepth; + if(!physicsTestAabbVsAabb( + posA, boundsHalfExtents[i], + posB, boundsHalfExtents[j], + bpNormal, &bpDepth + )) continue; + } + vec3 normal; float_t depth; if(!physicsTestShapeVsShape( posA, physA->shape, posB, physB->shape, normal, &depth diff --git a/src/dusk/scene/scene.c b/src/dusk/scene/scene.c index 72c4e9d9..425a2c21 100644 --- a/src/dusk/scene/scene.c +++ b/src/dusk/scene/scene.c @@ -19,6 +19,16 @@ scene_t SCENE; errorret_t sceneInit(void) { memoryZero(&SCENE, sizeof(scene_t)); + + // Fixed for the lifetime of the scene manager; computed once here. + glm_mat4_identity(SCENE.screenIdentity); + glm_lookat( + (vec3){ 0.0f, 0.0f, 1.0f }, + (vec3){ 0.0f, 0.0f, 0.0f }, + (vec3){ 0.0f, 1.0f, 0.0f }, + SCENE.screenView + ); + errorOk(); } @@ -58,22 +68,25 @@ errorret_t sceneUpdate(void) { } errorret_t sceneRender(void) { - // Setup screen matrices for 3D rendering. - glm_mat4_identity(SCENE.screenIdentity); - - glm_ortho( - 0.0f, (float_t)(SCREEN.width / SCREEN.scaleUi), - (float_t)(SCREEN.height / SCREEN.scaleUi), 0.0f, - 0.1f, 100.0f, - SCENE.screenProj - ); + // Recompute the UI ortho projection only when its inputs actually change. + if( + !SCENE.screenProjValid || + SCENE.cachedScreenWidth != SCREEN.width || + SCENE.cachedScreenHeight != SCREEN.height || + SCENE.cachedScreenScaleUi != SCREEN.scaleUi + ) { + glm_ortho( + 0.0f, (float_t)(SCREEN.width / SCREEN.scaleUi), + (float_t)(SCREEN.height / SCREEN.scaleUi), 0.0f, + 0.1f, 100.0f, + SCENE.screenProj + ); - glm_lookat( - (vec3){ 0.0f, 0.0f, 1.0f }, - (vec3){ 0.0f, 0.0f, 0.0f }, - (vec3){ 0.0f, 1.0f, 0.0f }, - SCENE.screenView - ); + SCENE.cachedScreenWidth = SCREEN.width; + SCENE.cachedScreenHeight = SCREEN.height; + SCENE.cachedScreenScaleUi = SCREEN.scaleUi; + SCENE.screenProjValid = true; + } // Scene rendering if( diff --git a/src/dusk/scene/scene.h b/src/dusk/scene/scene.h index db138699..6c3c978f 100644 --- a/src/dusk/scene/scene.h +++ b/src/dusk/scene/scene.h @@ -16,6 +16,10 @@ typedef struct { mat4 screenProj; mat4 screenView; mat4 screenIdentity; + int32_t cachedScreenWidth; + int32_t cachedScreenHeight; + int32_t cachedScreenScaleUi; + bool_t screenProjValid; } scene_t; extern scene_t SCENE; diff --git a/src/dusk/script/scriptmanager.c b/src/dusk/script/scriptmanager.c index 6f057b23..cd36af13 100644 --- a/src/dusk/script/scriptmanager.c +++ b/src/dusk/script/scriptmanager.c @@ -9,6 +9,7 @@ #include "assert/assert.h" #include "asset/asset.h" #include "util/memory.h" +#include "util/string.h" #include "scriptproto.h" #include "script/module/module.h" @@ -120,13 +121,35 @@ static errorret_t scriptManagerFormatException( return err; } +/** + * Returns a cached jerry_value_t string key for a global function name, + * creating and caching it on first use. Avoids re-allocating a JS string + * every call for names looked up every frame/tick (e.g. "update"). + */ +static jerry_value_t scriptManagerGetGlobalKey(const char_t *name) { + for(uint8_t i = 0; i < SCRIPT_MANAGER.globalKeyCacheCount; i++) { + if(stringCompare(name, SCRIPT_MANAGER.globalKeyCache[i].name) == 0) { + return SCRIPT_MANAGER.globalKeyCache[i].key; + } + } + + jerry_value_t key = jerry_string_sz(name); + assertTrue( + SCRIPT_MANAGER.globalKeyCacheCount < SCRIPT_MANAGER_MAX_CACHED_GLOBAL_KEYS, + "Global function key cache is full." + ); + SCRIPT_MANAGER.globalKeyCache[SCRIPT_MANAGER.globalKeyCacheCount].name = name; + SCRIPT_MANAGER.globalKeyCache[SCRIPT_MANAGER.globalKeyCacheCount].key = key; + SCRIPT_MANAGER.globalKeyCacheCount++; + return key; +} + errorret_t scriptManagerCallGlobal(const char_t *name) { assertNotNull(name, "Function name cannot be NULL"); jerry_value_t global = jerry_current_realm(); - jerry_value_t key = jerry_string_sz(name); + jerry_value_t key = scriptManagerGetGlobalKey(name); jerry_value_t fn = jerry_object_get(global, key); - jerry_value_free(key); jerry_value_free(global); if(!jerry_value_is_function(fn)) { @@ -176,6 +199,12 @@ errorret_t scriptManagerCallGlobal(const char_t *name) { errorret_t scriptManagerDispose(void) { scriptProtoDisposeAll(); + + for(uint8_t i = 0; i < SCRIPT_MANAGER.globalKeyCacheCount; i++) { + jerry_value_free(SCRIPT_MANAGER.globalKeyCache[i].key); + } + SCRIPT_MANAGER.globalKeyCacheCount = 0; + jerry_cleanup(); errorOk(); } diff --git a/src/dusk/script/scriptmanager.h b/src/dusk/script/scriptmanager.h index bd6fd64f..f218a344 100644 --- a/src/dusk/script/scriptmanager.h +++ b/src/dusk/script/scriptmanager.h @@ -12,8 +12,15 @@ #define SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS 64 +/** Max distinct global function names cached by @ref scriptManagerCallGlobal. */ +#define SCRIPT_MANAGER_MAX_CACHED_GLOBAL_KEYS 8 + typedef struct { - void* nothing; + struct { + const char_t *name; + jerry_value_t key; + } globalKeyCache[SCRIPT_MANAGER_MAX_CACHED_GLOBAL_KEYS]; + uint8_t globalKeyCacheCount; } scriptmanager_t; extern scriptmanager_t SCRIPT_MANAGER; diff --git a/src/dusk/thread/threadmutex.c b/src/dusk/thread/threadmutex.c index 366b4bf5..4652b131 100644 --- a/src/dusk/thread/threadmutex.c +++ b/src/dusk/thread/threadmutex.c @@ -23,6 +23,8 @@ void threadMutexLock(threadmutex_t *lock) { bool_t threadMutexTryLock(threadmutex_t *lock) { #ifdef DUSK_THREAD_PTHREAD return pthread_mutex_trylock(&lock->mutex) == 0; + #else + return false; #endif } diff --git a/src/dusk/time/time.c b/src/dusk/time/time.c index ab4f20a5..e3f9d174 100644 --- a/src/dusk/time/time.c +++ b/src/dusk/time/time.c @@ -9,8 +9,6 @@ #include "util/memory.h" #include "assert/assert.h" -#include "console/console.h" - dusktime_t TIME; void timeInit(void) { @@ -48,12 +46,6 @@ void timeUpdate(void) { TIME.delta = DUSK_TIME_STEP; TIME.time += DUSK_TIME_STEP; #endif - - // Print time in UTC style string - dusktimeepoch_t epoch = timeGetEpoch(); - char_t buffer[256]; - timeEpochFormat(epoch, "%Y-%m-%d %H:%M:%S", buffer, sizeof(buffer)); - // consolePrint("Real Time: %s", buffer); } dusktimeepoch_t timeGetEpoch(void) { diff --git a/src/dusk/ui/debug/uiconsole.c b/src/dusk/ui/debug/uiconsole.c index 549cf000..c8a73e16 100644 --- a/src/dusk/ui/debug/uiconsole.c +++ b/src/dusk/ui/debug/uiconsole.c @@ -16,6 +16,7 @@ errorret_t uiConsoleDraw(void) { float_t lineH = (float_t)FONT_DEFAULT.tileset->tileHeight; for(uint32_t i = 0; i < CONSOLE_HISTORY_MAX; i++) { + if(CONSOLE.line[i][0] == '\0') continue; errorChain(textDraw( (float_t)SCREEN.scanX, (float_t)SCREEN.scanY + lineH * (float_t)i, diff --git a/src/dusk/ui/debug/uifps.c b/src/dusk/ui/debug/uifps.c index 901e3b09..8573ceb8 100644 --- a/src/dusk/ui/debug/uifps.c +++ b/src/dusk/ui/debug/uifps.c @@ -58,8 +58,11 @@ errorret_t uiFPSDraw() { )); errorChain(spriteBatchFlush()); - int32_t versionWidth, versionHeight; - textMeasure(ENGINE.version, &FONT_DEFAULT, &versionWidth, &versionHeight); + // ENGINE.version and FONT_DEFAULT never change after boot; measure once. + static int32_t versionWidth = -1, versionHeight = 0; + if(versionWidth < 0) { + textMeasure(ENGINE.version, &FONT_DEFAULT, &versionWidth, &versionHeight); + } errorChain(textDraw( (float_t)(SCREEN.scanX + SCREEN.scanWidth - versionWidth), (float_t)(SCREEN.scanY + SCREEN.scanHeight - versionHeight), diff --git a/src/dusk/ui/widget/uidropdown.c b/src/dusk/ui/widget/uidropdown.c index ae72bd4e..f983dc5b 100644 --- a/src/dusk/ui/widget/uidropdown.c +++ b/src/dusk/ui/widget/uidropdown.c @@ -29,6 +29,9 @@ void uiDropdownInit( dropdown->options = options; dropdown->optionCount = optionCount; dropdown->selectedIndex = selectedIndex < optionCount ? selectedIndex : 0; + textMeasure( + label, &FONT_DEFAULT, &dropdown->labelWidth, &dropdown->labelHeight + ); } uint8_t uiDropdownGetSelectedIndex(const uidropdown_t *dropdown) { @@ -83,9 +86,6 @@ errorret_t uiDropdownDraw( errorChain(textDraw(x, y, dropdown->label, color, &FONT_DEFAULT)); - int32_t labelW, labelH; - textMeasure(dropdown->label, &FONT_DEFAULT, &labelW, &labelH); - char_t valueText[UI_DROPDOWN_VALUE_TEXT_MAX]; stringFormat( valueText, UI_DROPDOWN_VALUE_TEXT_MAX - 1, "< %s >", @@ -93,7 +93,8 @@ errorret_t uiDropdownDraw( ); errorChain(textDraw( - x + (float_t)labelW + UI_DROPDOWN_GAP, y, valueText, color, &FONT_DEFAULT + x + (float_t)dropdown->labelWidth + UI_DROPDOWN_GAP, y, valueText, color, + &FONT_DEFAULT )); errorOk(); diff --git a/src/dusk/ui/widget/uidropdown.h b/src/dusk/ui/widget/uidropdown.h index 0c6884ac..68240c4a 100644 --- a/src/dusk/ui/widget/uidropdown.h +++ b/src/dusk/ui/widget/uidropdown.h @@ -17,6 +17,8 @@ typedef struct { uint8_t optionCount; uint8_t selectedIndex; bool_t highlighted; + int32_t labelWidth; + int32_t labelHeight; } uidropdown_t; /** diff --git a/src/dusk/ui/widget/uislider.c b/src/dusk/ui/widget/uislider.c index 95722b80..9d9f4b83 100644 --- a/src/dusk/ui/widget/uislider.c +++ b/src/dusk/ui/widget/uislider.c @@ -36,6 +36,7 @@ void uiSliderInitFloat( slider->max.f = max; slider->step.f = step; slider->value.f = mathClamp(value, min, max); + textMeasure(label, &FONT_DEFAULT, &slider->labelWidth, &slider->labelHeight); } void uiSliderInitInt( @@ -58,6 +59,7 @@ void uiSliderInitInt( slider->max.i = max; slider->step.i = step; slider->value.i = mathClamp(value, min, max); + textMeasure(label, &FONT_DEFAULT, &slider->labelWidth, &slider->labelHeight); } float_t uiSliderGetFloat(const uislider_t *slider) { @@ -151,11 +153,8 @@ errorret_t uiSliderDraw( errorChain(textDraw(x, y, slider->label, color, &FONT_DEFAULT)); - int32_t labelW, labelH; - textMeasure(slider->label, &FONT_DEFAULT, &labelW, &labelH); - - float_t trackX = x + (float_t)labelW + UI_SLIDER_GAP; - float_t trackY = y + ((float_t)labelH - UI_SLIDER_TRACK_HEIGHT) * 0.5f; + float_t trackX = x + (float_t)slider->labelWidth + UI_SLIDER_GAP; + float_t trackY = y + ((float_t)slider->labelHeight - UI_SLIDER_TRACK_HEIGHT) * 0.5f; spritebatchsprite_t trackSprite = { .min = { trackX, trackY, 0.0f }, @@ -170,7 +169,6 @@ errorret_t uiSliderDraw( } }; errorChain(spriteBatchBuffer(&trackSprite, 1, &SHADER_UNLIT, trackMaterial)); - errorChain(spriteBatchFlush()); float_t fillWidth = UI_SLIDER_TRACK_WIDTH * uiSliderGetRatio(slider); if(fillWidth > 0.0f) { @@ -187,7 +185,6 @@ errorret_t uiSliderDraw( } }; errorChain(spriteBatchBuffer(&fillSprite, 1, &SHADER_UNLIT, fillMaterial)); - errorChain(spriteBatchFlush()); } int32_t stepCount = uiSliderGetStepCount(slider); @@ -218,7 +215,6 @@ errorret_t uiSliderDraw( errorChain( spriteBatchBuffer(&markerSprite, 1, &SHADER_UNLIT, markerMaterial) ); - errorChain(spriteBatchFlush()); } } diff --git a/src/dusk/ui/widget/uislider.h b/src/dusk/ui/widget/uislider.h index d53dee00..b63567b3 100644 --- a/src/dusk/ui/widget/uislider.h +++ b/src/dusk/ui/widget/uislider.h @@ -41,6 +41,8 @@ typedef struct { uislidervalue_t max; uislidervalue_t step; bool_t highlighted; + int32_t labelWidth; + int32_t labelHeight; } uislider_t; /** diff --git a/src/dusk/ui/widget/uitab.c b/src/dusk/ui/widget/uitab.c index 12073ef2..00a30de7 100644 --- a/src/dusk/ui/widget/uitab.c +++ b/src/dusk/ui/widget/uitab.c @@ -19,6 +19,7 @@ void uiTabInit(uitab_t *tab, const char_t *label) { assertNotNull(label, "Label cannot be NULL"); memoryZero(tab, sizeof(uitab_t)); tab->label = label; + textMeasure(label, &FONT_DEFAULT, &tab->labelWidth, &tab->labelHeight); } bool_t uiTabIsActive(const uitab_t *tab) { @@ -38,12 +39,9 @@ errorret_t uiTabDraw( ) { assertNotNull(tab, "Tab cannot be NULL"); - int32_t labelW, labelH; - textMeasure(tab->label, &FONT_DEFAULT, &labelW, &labelH); - spritebatchsprite_t sprite = { .min = { x, y, 0.0f }, - .max = { x + (float_t)labelW, y + (float_t)labelH, 0.0f }, + .max = { x + (float_t)tab->labelWidth, y + (float_t)tab->labelHeight, 0.0f }, .uvMin = { 0.0f, 0.0f }, .uvMax = { 1.0f, 1.0f } }; diff --git a/src/dusk/ui/widget/uitab.h b/src/dusk/ui/widget/uitab.h index 678f54b6..3afbd1be 100644 --- a/src/dusk/ui/widget/uitab.h +++ b/src/dusk/ui/widget/uitab.h @@ -11,6 +11,8 @@ typedef struct { const char_t *label; bool_t active; + int32_t labelWidth; + int32_t labelHeight; } uitab_t; /** diff --git a/src/dusk/util/crypt.c b/src/dusk/util/crypt.c index cec0909b..0bf176bb 100644 --- a/src/dusk/util/crypt.c +++ b/src/dusk/util/crypt.c @@ -28,9 +28,3 @@ void cryptCRC32Update(uint32_t *crc, const void *data, const size_t size) { uint32_t cryptCRC32End(const uint32_t crc) { return ~crc; } - -uint32_t cryptCRC32(const void *data, const size_t size) { - uint32_t crc = cryptCRC32Begin(); - cryptCRC32Update(&crc, data, size); - return cryptCRC32End(crc); -} diff --git a/src/dusk/util/crypt.h b/src/dusk/util/crypt.h index b9484341..9e01669b 100644 --- a/src/dusk/util/crypt.h +++ b/src/dusk/util/crypt.h @@ -8,15 +8,6 @@ #pragma once #include "dusk.h" -/** - * Computes a CRC32 checksum over a block of data. - * - * @param data Pointer to the data to checksum. - * @param size Number of bytes to checksum. - * @return The CRC32 checksum. - */ -uint32_t cryptCRC32(const void *data, const size_t size); - /** * Returns the initial CRC32 accumulator value. * diff --git a/src/dusk/util/memory.c b/src/dusk/util/memory.c index 21bcfb64..603531c9 100644 --- a/src/dusk/util/memory.c +++ b/src/dusk/util/memory.c @@ -103,15 +103,6 @@ int_t memoryCompare( return memcmp(a, b, size); } -void memoryReallocate(void **ptr, const size_t size) { - assertNotNull(ptr, "Cannot reallocate NULL pointer."); - assertTrue(size > 0, "Cannot reallocate to 0 bytes of memory."); - void *newPointer = memoryAllocate(size); - assertNotNull(newPointer, "Memory reallocation failed."); - memoryFree(*ptr); - *ptr = newPointer; -} - void memoryCopyInterleaved( void *dest, const size_t destStride, diff --git a/src/dusk/util/memory.h b/src/dusk/util/memory.h index f03287e4..f1d2d513 100644 --- a/src/dusk/util/memory.h +++ b/src/dusk/util/memory.h @@ -114,14 +114,6 @@ int_t memoryCompare( const size_t size ); -/** - * Reallocates memory. - * - * @param ptr The pointer to the memory to reallocate. - * @param size The new size of the memory. - */ -void memoryReallocate(void **ptr, const size_t size); - /** * Reallocates memory, but copies existing data to the new memory. * diff --git a/src/dusk/util/random.c b/src/dusk/util/random.c index c7974029..2eba1d93 100644 --- a/src/dusk/util/random.c +++ b/src/dusk/util/random.c @@ -6,12 +6,15 @@ */ #include "random.h" +#include "assert/assert.h" #include float_t randomFloat(const float_t min, const float_t max) { + assertTrue(max > min, "randomFloat: max must be greater than min."); return min + ((float_t)rand() / (float_t)RAND_MAX) * (max - min); } int_t randomInt(const int_t min, const int_t max) { + assertTrue(max > min, "randomInt: max must be greater than min."); return min + (rand() % (max - min)); } diff --git a/src/duskgl/display/framebuffer/framebuffergl.c b/src/duskgl/display/framebuffer/framebuffergl.c index 95eecfa3..60ae8c94 100644 --- a/src/duskgl/display/framebuffer/framebuffergl.c +++ b/src/duskgl/display/framebuffer/framebuffergl.c @@ -21,9 +21,7 @@ uint32_t frameBufferGLGetWidth(const framebuffer_t *framebuffer) { if(framebuffer == &FRAMEBUFFER_BACKBUFFER) { #ifdef DUSK_DISPLAY_SIZE_DYNAMIC - int32_t windowWidth, windowHeight; - SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight); - return windowWidth; + return DISPLAY.cachedWidth; #else return DUSK_DISPLAY_WIDTH; #endif @@ -39,9 +37,7 @@ uint32_t frameBufferGLGetHeight(const framebuffer_t *framebuffer) { if(framebuffer == &FRAMEBUFFER_BACKBUFFER) { #ifdef DUSK_DISPLAY_SIZE_DYNAMIC - int32_t windowWidth, windowHeight; - SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight); - return windowHeight; + return DISPLAY.cachedHeight; #else return DUSK_DISPLAY_HEIGHT; #endif diff --git a/src/duskgl/display/mesh/meshgl.c b/src/duskgl/display/mesh/meshgl.c index fa6b7ed3..a454f8f9 100644 --- a/src/duskgl/display/mesh/meshgl.c +++ b/src/duskgl/display/mesh/meshgl.c @@ -93,13 +93,11 @@ errorret_t meshFlushGL( #else glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId); errorChain(errorGLCheck()); - glBufferData( + glBufferSubData( GL_ARRAY_BUFFER, - mesh->vertexCount * sizeof(meshvertex_t), - mesh->vertices, - // vertCount * sizeof(meshvertex_t), - // &mesh->vertices[vertOffset], - GL_DYNAMIC_DRAW + vertOffset * sizeof(meshvertex_t), + vertCount * sizeof(meshvertex_t), + &mesh->vertices[vertOffset] ); errorChain(errorGLCheck()); #endif diff --git a/src/duskgl/display/shader/shadergl.c b/src/duskgl/display/shader/shadergl.c index f7467f41..cb287819 100644 --- a/src/duskgl/display/shader/shadergl.c +++ b/src/duskgl/display/shader/shadergl.c @@ -157,13 +157,28 @@ errorret_t shaderParamGetLocationGL( #ifdef DUSK_OPENGL_LEGACY assertUnreachable("Cannot get uniform locations on legacy opengl."); #else + for(uint8_t i = 0; i < shader->uniformCacheCount; i++) { + if(stringCompare(name, shader->uniformCache[i].name) == 0) { + *location = shader->uniformCache[i].location; + errorOk(); + } + } + *location = glGetUniformLocation(shader->shaderProgramId, name); errorChain(errorGLCheck()); if(*location == -1) { errorThrow("Uniform '%s' not found in shader.", name); } + + assertTrue( + shader->uniformCacheCount < SHADERGL_MAX_CACHED_UNIFORMS, + "Uniform cache is full; increase SHADERGL_MAX_CACHED_UNIFORMS." + ); + shader->uniformCache[shader->uniformCacheCount].name = name; + shader->uniformCache[shader->uniformCacheCount].location = *location; + shader->uniformCacheCount++; #endif - + errorOk(); } diff --git a/src/duskgl/display/shader/shadergl.h b/src/duskgl/display/shader/shadergl.h index 11be1a43..7ee093d5 100644 --- a/src/duskgl/display/shader/shadergl.h +++ b/src/duskgl/display/shader/shadergl.h @@ -30,6 +30,9 @@ typedef struct { #endif } shaderdefinitiongl_t; +/** Number of distinct uniform names a single shader instance can cache. */ +#define SHADERGL_MAX_CACHED_UNIFORMS 8 + typedef struct shadergl_s { const shaderdefinitiongl_t *definition; @@ -41,6 +44,12 @@ typedef struct shadergl_s { GLuint shaderProgramId; GLuint vertexShaderId; GLuint fragmentShaderId; + + struct { + const char_t *name; + GLint location; + } uniformCache[SHADERGL_MAX_CACHED_UNIFORMS]; + uint8_t uniformCacheCount; #endif } shadergl_t; diff --git a/src/duskgl/display/shader/shaderunlitgl.c b/src/duskgl/display/shader/shaderunlitgl.c index f349de83..df47c021 100644 --- a/src/duskgl/display/shader/shaderunlitgl.c +++ b/src/duskgl/display/shader/shaderunlitgl.c @@ -13,6 +13,10 @@ .setMaterial = shaderUnlitSetMaterial, }; #else + // Last-uploaded palette for the (single, global) unlit shader instance; + // skips rebuilding/re-uploading the color array when it hasn't changed. + static palette_t *SHADER_UNLIT_LAST_PALETTE = NULL; + errorret_t shaderUnlitSetTextureGL( shadergl_t *shader, const char_t *name, @@ -52,23 +56,27 @@ glUniform1i(locType, 2); errorChain(errorGLCheck()); - shaderParamGetLocationGL(shader, "u_ColorCount", &locColorCount); - glUniform1i(locColorCount, texture->palette->count); - errorChain(errorGLCheck()); + if(texture->palette != SHADER_UNLIT_LAST_PALETTE) { + shaderParamGetLocationGL(shader, "u_ColorCount", &locColorCount); + glUniform1i(locColorCount, texture->palette->count); + errorChain(errorGLCheck()); - shaderParamGetLocationGL(shader, "u_Colors", &locColors); - GLuint paletteData[texture->palette->count]; - for(size_t i = 0; i < texture->palette->count; i++) { - color_t color = texture->palette->colors[i]; - paletteData[i] = ( - ((uint32_t)color.r << 24) | - ((uint32_t)color.g << 16) | - ((uint32_t)color.b << 8) | - ((uint32_t)color.a << 0) - ); + shaderParamGetLocationGL(shader, "u_Colors", &locColors); + GLuint paletteData[texture->palette->count]; + for(size_t i = 0; i < texture->palette->count; i++) { + color_t color = texture->palette->colors[i]; + paletteData[i] = ( + ((uint32_t)color.r << 24) | + ((uint32_t)color.g << 16) | + ((uint32_t)color.b << 8) | + ((uint32_t)color.a << 0) + ); + } + glUniform1uiv(locColors, texture->palette->count, paletteData); + errorChain(errorGLCheck()); + + SHADER_UNLIT_LAST_PALETTE = texture->palette; } - glUniform1uiv(locColors, texture->palette->count, paletteData); - errorChain(errorGLCheck()); } else { glUniform1i(locType, 1); errorChain(errorGLCheck()); diff --git a/src/dusksdl2/display/displaysdl2.c b/src/dusksdl2/display/displaysdl2.c index 74125db9..450a9ee2 100644 --- a/src/dusksdl2/display/displaysdl2.c +++ b/src/dusksdl2/display/displaysdl2.c @@ -10,7 +10,12 @@ #include "display/displaygl.h" #include "error/errorgl.h" +static displaystate_t DISPLAY_STATE_CACHE = { 0 }; +static bool_t DISPLAY_STATE_CACHE_VALID = false; + errorret_t displaySDL2Init(void) { + DISPLAY_STATE_CACHE_VALID = false; + uint32_t flags = SDL_INIT_VIDEO; #ifdef DUSK_INPUT_GAMEPAD flags |= SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK; @@ -57,7 +62,9 @@ errorret_t displaySDL2Init(void) { errorChain(errorGLCheck()); errorChain(displayOpenGLInit()); - + + SDL_GetWindowSize(DISPLAY.window, &DISPLAY.cachedWidth, &DISPLAY.cachedHeight); + errorChain(errorGLCheck()); errorOk(); } @@ -77,11 +84,19 @@ errorret_t displaySDL2Update(void) { ENGINE.running = false; break; } - + + case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_SIZE_CHANGED: { + DISPLAY.cachedWidth = event.window.data1; + DISPLAY.cachedHeight = event.window.data2; + break; + } + default: { break; } } + break; } default: { @@ -103,42 +118,57 @@ errorret_t displaySDL2Swap(void) { } errorret_t displaySDL2SetState(displaystate_t state) { - if(state.flags & DISPLAY_STATE_FLAG_CULL) { - glEnable(GL_CULL_FACE); - errorChain(errorGLCheck()); - glCullFace(GL_BACK); - errorChain(errorGLCheck()); - } else { - glDisable(GL_CULL_FACE); - errorChain(errorGLCheck()); + uint8_t changed = DISPLAY_STATE_CACHE_VALID + ? (state.flags ^ DISPLAY_STATE_CACHE.flags) + : 0xFF; + + if(changed & DISPLAY_STATE_FLAG_CULL) { + if(state.flags & DISPLAY_STATE_FLAG_CULL) { + glEnable(GL_CULL_FACE); + errorChain(errorGLCheck()); + glCullFace(GL_BACK); + errorChain(errorGLCheck()); + } else { + glDisable(GL_CULL_FACE); + errorChain(errorGLCheck()); + } } - if(state.flags & DISPLAY_STATE_FLAG_DEPTH_TEST) { - glEnable(GL_DEPTH_TEST); - errorChain(errorGLCheck()); - glDepthFunc(GL_LEQUAL); - errorChain(errorGLCheck()); - glClearDepth(1.0f); - errorChain(errorGLCheck()); - } else { - glDisable(GL_DEPTH_TEST); - errorChain(errorGLCheck()); + if(changed & DISPLAY_STATE_FLAG_DEPTH_TEST) { + if(state.flags & DISPLAY_STATE_FLAG_DEPTH_TEST) { + glEnable(GL_DEPTH_TEST); + errorChain(errorGLCheck()); + glDepthFunc(GL_LEQUAL); + errorChain(errorGLCheck()); + glClearDepth(1.0f); + errorChain(errorGLCheck()); + } else { + glDisable(GL_DEPTH_TEST); + errorChain(errorGLCheck()); + } } - if(state.flags & DISPLAY_STATE_FLAG_BLEND) { - glEnable(GL_BLEND); - errorChain(errorGLCheck()); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - errorChain(errorGLCheck()); - } else { - glDisable(GL_BLEND); - errorChain(errorGLCheck()); + if(changed & DISPLAY_STATE_FLAG_BLEND) { + if(state.flags & DISPLAY_STATE_FLAG_BLEND) { + glEnable(GL_BLEND); + errorChain(errorGLCheck()); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + errorChain(errorGLCheck()); + } else { + glDisable(GL_BLEND); + errorChain(errorGLCheck()); + } } + DISPLAY_STATE_CACHE = state; + DISPLAY_STATE_CACHE_VALID = true; + errorOk(); } void displaySDL2Dispose(void) { + DISPLAY_STATE_CACHE_VALID = false; + if(DISPLAY.glContext) { SDL_GL_DeleteContext(DISPLAY.glContext); DISPLAY.glContext = NULL; diff --git a/src/dusksdl2/display/displaysdl2.h b/src/dusksdl2/display/displaysdl2.h index bb02d478..03158e8d 100644 --- a/src/dusksdl2/display/displaysdl2.h +++ b/src/dusksdl2/display/displaysdl2.h @@ -13,6 +13,8 @@ typedef struct { SDL_Window *window; SDL_GLContext glContext; bool_t usingShaderedPalettes; + int32_t cachedWidth; + int32_t cachedHeight; } displaysdl2_t; /** diff --git a/test/util/test_memory.c b/test/util/test_memory.c index 285c45e5..4e220f42 100644 --- a/test/util/test_memory.c +++ b/test/util/test_memory.c @@ -326,39 +326,6 @@ static void test_memoryCompare(void **state) { assert_int_equal(memoryGetAllocatedCount(), 0); } -static void test_memoryReallocate(void **state) { - - - size_t initialSize = 16; - void *ptr = memoryAllocate(initialSize); - assert_non_null(ptr); - - // Reallocate to a larger size - size_t newSize = 32; - memoryReallocate(&ptr, newSize); - assert_non_null(ptr); - - // Reallocate to a smaller size - size_t smallerSize = 8; - memoryReallocate(&ptr, smallerSize); - assert_non_null(ptr); - - // Cannot realloc to size 0 - expect_assert_failure(memoryReallocate(&ptr, 0)); - - // Cannot realloc NULL pointer - expect_assert_failure(memoryReallocate(NULL, 16)); - - // Cannot reallocate more memory than possible - expect_assert_failure(memoryReallocate(&ptr, SIZE_MAX)); - - // All we really care about is that the pointer is valid after reallocations - memoryFree(ptr); - - // Expect no leaks - assert_int_equal(memoryGetAllocatedCount(), 0); -} - static void test_memoryResize(void **state) { @@ -514,7 +481,6 @@ int main(int argc, char **argv) { cmocka_unit_test(test_memoryCopyRangeSafe), cmocka_unit_test(test_memoryMove), cmocka_unit_test(test_memoryCompare), - cmocka_unit_test(test_memoryReallocate), cmocka_unit_test(test_memoryResize), cmocka_unit_test(test_memoryCopyInterleaved), };