diff --git a/nuklear.h b/nuklear.h index 44e29655f..c254c59ba 100644 --- a/nuklear.h +++ b/nuklear.h @@ -4857,6 +4857,9 @@ struct nk_command_buffer { int use_clipping; nk_handle userdata; nk_size begin, end, last; +#ifdef NK_DRAW_CRC + NK_UINT32 crc; +#endif }; /** shape outlines */ @@ -5962,6 +5965,9 @@ struct nk_context { struct nk_page_element *freelist; unsigned int count; unsigned int seq; +#ifdef NK_DRAW_CRC + NK_UINT32 crc; +#endif }; /* ============================================================== @@ -6175,6 +6181,11 @@ NK_LIB nk_bool nk_is_lower(int c); NK_LIB nk_bool nk_is_upper(int c); NK_LIB int nk_to_upper(int c); NK_LIB int nk_to_lower(int c); +#ifdef NK_DRAW_CRC +#ifndef NK_DRAW_CRC_CUSTOM +NK_LIB NK_UINT32 nk_crc_update(NK_UINT32 crc, NK_UINT8 *data, NK_SIZE_TYPE len); +#endif +#endif #ifndef NK_MEMCPY NK_LIB void* nk_memcopy(void *dst, const void *src, nk_size n); @@ -9204,6 +9215,206 @@ nk_str_free(struct nk_str *str) str->len = 0; } +/** + * \file nuklear_crc.c + * \brief Implements CRC checking of the draw command buffer. + * + * \details Implements a rolling CRC that gets updated with every draw_push + * function call. That is, for every `nk_xxx` api function call between + * `nk_begin()` and `nk_end()` there is one to many draw commands pushed into + * the draw buffer. For each one of those pushes, the command type is XOR'd + * then stored into that buffer's CRC member. Then on the next `nk_xxx` function + * call, another command push is executed and the previous crc is updated with + * the new XOR of the new command. This will result in a unique CRC value in the + * buffers crc member at the end of the draw execution. + * + * The purpose of this is to allow super cheap and fast detection of unique'ness + * between draw loops, such that the user can determine if they need to redraw + * the screen or not. The original method was to have the user store a copy of + * the entire draw buffer, then loop through the buffer and compare that with + * the most recent draw buffer. Once done, you can determine if something + * changed on the screen and needs to be `nk_convert`'d. Doing it that way would + * require 1 loop to fill the command buffer, 1 loop to check the command buffer + * against the previously stored buffer, then 1 more loop to copy the new buffer + * into the old for the next draw loop iteration. That is 3 loops of + * `command_buffer'length()` which could end up being quite large; making this + * an expensive operation. Which almost defeats the purpose of detecting + * unique'ness in the first place. + * + * Yet implementing this CRC, we cruch all of that down into just the initial + * draw loop. Then you have a unique code stored in a 32-bit integer that + * represents the buffer. So if you need to know if the current draw command + * buffer is different from the previous one; you simply need to retain a single + * interger value and then check whether the old interger is equal to the new + * one. We reduced 3x O(n) loop complexity and 2x O(n) memory/space complexity + * down to 1x O(n) loop complexity and O(1) memory complexity. The CRC + * calculation (depending on default or custom implementation) simply adds 1 + * table lookup and 1 XOR operation to a single integer. Practically a free + * upgrade by comparison. + * + * If you want to use the CRC, then you must either `#define NK_DRAW_CRC` + * or `#define NK_DRAW_CRC_CUSTOM` + * + * `#define NK_DRAW_CRC` will implement the default CRC algorithm which is + * a 32 bit castagnoli (CRC-32C). This is a very common CRC that is used in + * iSCSI, SMPTE, ext-4, etc. So common that intel processors have dedicated + * hardware for it and lots of libraries have SIMD acceleration for it (not that + * its necessary for this application) + * + * `#define NK_DRAW_CRC_CUSTOM` will allow the user to define their own CRC + * implementation. This is available because most embedded systems already have + * a CRC calculation of some kind, usually defined by their system constraints, + * so it would be redundant to create yet another CRC method. + * + * To use the `NK_DRAW_CRC_CUSTOM` the user will need to `#define + * NK_CRC_SEED` to their desired seed and also `#define NK_CRC_FUNC(c,d,l)` to + * point to a user defined implementation of the crc. + * + * The user defined implementation of the CRC **SHALL** have the declaration of + * ```c + * NK_UINT32 (NK_UINT32 crc, NK_UINT8 *data, NK_SIZE_TYPE len) + * ``` + * That is, Shall : + * - return a 32 bit integer (the new CRC value) + * - accept, as the first argument, a 32 bit integer. The initial value of the crc. + * - accept, as the second argument, a pointer to a byte buffer to be CRC'd. + * - accept, as the third argument, the length of the byte buffer. + * + * other than that, the user is free to use any CRC algorithm, table, + * implementation they choose. + * + * \internal + * Information for nuklear developers. Each context includes a command buffer + * for drawing, usually called `ctx->current->buffer`. This is the buffer with + * the draw commands and we grab the CRC value stored with this buffer + * `ctx->current->buffer.crc`. The buffer, in regards to a context, only exists + * to draw a panel and will _dissappear_ once `nk_end()` is called. This is + * because nk_end commits the draw buffer to the context's overal draw array and + * closes out the `current` buffer to wait for another `nk_begin()` to start a + * new panel using the `nk_buffer_start()` command. + * + * What this means for the CRC is that, when a user decides they want to check + * on the CRC, they will do so **after** the `nk_end()` call (because you need + * to make sure all of the information is there). But attempting to grab the CRC + * at `ctx->current->buffer.crc` will result in a Seg Fault because that memory + * was deallocated with the `nk_end()` call. But not only that, we determined + * that the `current->buffer` is meant to be used for all sequences of + * `nk_begin()` to `nk_end()`. Meaning that we will get new CRCs for each panel; + * but only be able to look at the final CRC (unless we grab them as we go, but + * thats silly when we just want to know whether to draw or not). That means we + * better hope the user only ever interacts with which ever panel was the final + * one drawn, otherwise we wont see a crc change. + * + * The solution was to store the CRC at the Context level. So now `struct + * nk_context` contains a `crc` member that gets rolled when the current buffer + * finishes (i.e. `nk_end()` was called). This means that, whenever + * `nk_finish` is called, we calc the context CRC based off the current buffer + * crc. This retains the CRC value through successive calls to `nk_begin()` and + * `nk_end()` and rolls the number appropriately. + * \addtogroup crc + * \brief Command buffer CRC Implementation + * @{ + */ + +#ifdef NK_DRAW_CRC_CUSTOM +#ifndef NK_CRC_SEED +#error "Must #define NK_CRC_SEED when using NK_DRAW_CRC_CUSTOM" +#endif +#ifndef NK_CRC_FUNC(b,d,l) +#error "Must #define NK_CRC_FUNC(c,d,l) when using NK_DRAW_CRC_CUSTOM" +#endif +#define NK_DRAW_CRC +#endif + +#ifdef NK_DRAW_CRC +#define NK_CRC_SEED 0xffffffff /**< seed value of the crc*/ +#define NK_CRC_FUNC(c,d,l) nk_crc_update(c, d, l) + + + +#ifndef NK_DRAW_CRC_CUSTOM +NK_STORAGE NK_UINT32 crc32c_table[] = { + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, 0x35F1141CL, + 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, 0x105EC76FL, 0xE235446CL, + 0xF165B798L, 0x030E349BL, 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL, + 0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, 0xAA64D611L, 0x580F5512L, + 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL, + 0x1642AE59L, 0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 0x417B1DBCL, 0xB3109EBFL, + 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, + 0xED03A29BL, 0x1F682198L, 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL, + 0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, 0xA65C047DL, 0x5437877EL, + 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, + 0x90A324FAL, 0x62C8A7F9L, 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL, 0xCEB018DEL, + 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, + 0xE9141340L, 0x1B7F9043L, 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL, + 0xB4091BFFL, 0x466298FCL, 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, 0xA24BB5A6L, 0x502036A5L, + 0x4370C551L, 0xB11B4652L, 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, + 0x0E330A81L, 0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L, 0xCAA7A905L, + 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L, + 0xE52CC12CL, 0x1747422FL, 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L, + 0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, 0x9E902E7BL, 0x6CFBAD78L, + 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L, + 0x88D28022L, 0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 0x34F4F86AL, 0xC69F7B69L, + 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L +}; + +#endif + + +/** + * \brief steps the crc value by the amount of new data. + * + * \details + * this function should only be called in the nk_command_buffer_push() function + * and should only be given the `type` enum of the command being pushed. As the + * buffer is getting filled, this function will roll and calculate the CRC + * value. Never clearing will result is a proper CRC at the end of the drawing + * updates. + * + * \param[in] crc is the previous value of the CRC from which to build on. + * \param[in] data is a pointer to the data to run the CRC on. + * \param[in] len is the size in bytes of the data. + * + * \returns the new crc value + */ +NK_LIB NK_UINT32 nk_crc_update(NK_UINT32 crc, NK_UINT8 *data, NK_SIZE_TYPE len) +{ + while (len--) crc = (crc<<8) ^ crc32c_table[(crc >> 24) ^ *data++]; + return crc; +} + +/** + * \brief returns the crc of the context + * \param[in] buf is the context for which to retrieve the CRC + */ +NK_API NK_UINT32 nk_get_crc(struct nk_context *ctx) +{ + return (ctx) ? ctx->crc : NK_CRC_SEED; +} +/** @} *//*end documentation grouping*/ +#endif /* NK_DRAW_CRC + @@ -9269,6 +9480,9 @@ nk_command_buffer_push(struct nk_command_buffer* b, cmd->userdata = b->userdata; #endif b->end = cmd->next; +#ifdef NK_DRAW_CRC + b->crc = NK_CRC_FUNC(b->crc, (NK_UINT8*)cmd,size); +#endif return cmd; } NK_API void @@ -19499,6 +19713,9 @@ nk_clear(struct nk_context *ctx) iter = next; } else iter = iter->next; } +#ifdef NK_DRAW_CRC + ctx->crc = NK_CRC_SEED; +#endif ctx->seq++; } NK_LIB void @@ -19511,6 +19728,9 @@ nk_start_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) buffer->end = buffer->begin; buffer->last = buffer->begin; buffer->clip = nk_null_rect; +#ifdef NK_DRAW_CRC + buffer->crc = NK_CRC_SEED; +#endif } NK_LIB void nk_start(struct nk_context *ctx, struct nk_window *win) @@ -19565,6 +19785,9 @@ nk_finish(struct nk_context *ctx, struct nk_window *win) NK_ASSERT(ctx); NK_ASSERT(win); if (!ctx || !win) return; +#ifdef NK_DRAW_CRC + ctx->crc = NK_CRC_FUNC(ctx->crc, &win->buffer.crc, sizeof(win->buffer.crc)); +#endif nk_finish_buffer(ctx, &win->buffer); if (!win->popup.buf.active) return; @@ -20804,6 +21027,9 @@ nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, } win->layout = (struct nk_panel*)nk_create_panel(ctx); ctx->current = win; +#ifdef NK_DRAW_CRC + ctx->crc = NK_CRC_SEED; +#endif ret = nk_panel_begin(ctx, title, NK_PANEL_WINDOW); win->layout->offset_x = &win->scrollbar.x; win->layout->offset_y = &win->scrollbar.y; @@ -30698,6 +30924,7 @@ nk_tooltipfv(struct nk_context *ctx, const char *fmt, va_list args) /// - [y]: Minor version with non-breaking API and library changes /// - [z]: Patch version with no direct changes to the API /// +/// - 2024/11/18 (4.12.2) - Adds CRC check on draw command buffer /// - 2024/03/07 (4.12.1) - Fix bitwise operations warnings in C++20 /// - 2023/11/26 (4.12.0) - Added an alignment option to checkboxes and radio buttons. /// - 2023/10/11 (4.11.0) - Added nk_widget_disable_begin() and nk_widget_disable_end() diff --git a/src/CHANGELOG b/src/CHANGELOG index c301e319d..aeb779896 100644 --- a/src/CHANGELOG +++ b/src/CHANGELOG @@ -7,6 +7,7 @@ /// - [y]: Minor version with non-breaking API and library changes /// - [z]: Patch version with no direct changes to the API /// +/// - 2024/11/18 (4.12.2) - Adds CRC check on draw command buffer /// - 2024/03/07 (4.12.1) - Fix bitwise operations warnings in C++20 /// - 2023/11/26 (4.12.0) - Added an alignment option to checkboxes and radio buttons. /// - 2023/10/11 (4.11.0) - Added nk_widget_disable_begin() and nk_widget_disable_end() diff --git a/src/build.py b/src/build.py index ba29fef8b..ff5c455d8 100644 --- a/src/build.py +++ b/src/build.py @@ -9,6 +9,7 @@ def print_help(): """usage: python single_header_packer.py --macro [--intro ] --extern --pub --priv1 --priv2 [--outro ] where can be a comma-separated list of files. e.g. --priv *.c,inc/*.h + or a space separated list encapsulated in quotes. e.g. --priv1 "file.c file2.c file3.c" The 'extern' files are placed between 'priv1' and 'priv2'. @@ -33,7 +34,8 @@ def print_help(): def parse_files(arg): files = [] - paths = arg.split(",") + paths = re.split(r'[,\s]', arg) + for path in paths: if "*" in path: # Wildcard diff --git a/src/nuklear.h b/src/nuklear.h index a76d384c1..abedd8a5a 100644 --- a/src/nuklear.h +++ b/src/nuklear.h @@ -4635,6 +4635,9 @@ struct nk_command_buffer { int use_clipping; nk_handle userdata; nk_size begin, end, last; +#ifdef NK_DRAW_CRC + NK_UINT32 crc; +#endif }; /** shape outlines */ @@ -5740,6 +5743,9 @@ struct nk_context { struct nk_page_element *freelist; unsigned int count; unsigned int seq; +#ifdef NK_DRAW_CRC + NK_UINT32 crc; +#endif }; /* ============================================================== diff --git a/src/nuklear_context.c b/src/nuklear_context.c index 771449689..e3646e9f5 100644 --- a/src/nuklear_context.c +++ b/src/nuklear_context.c @@ -170,6 +170,9 @@ nk_clear(struct nk_context *ctx) iter = next; } else iter = iter->next; } +#ifdef NK_DRAW_CRC + ctx->crc = NK_CRC_SEED; +#endif ctx->seq++; } NK_LIB void @@ -182,6 +185,9 @@ nk_start_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) buffer->end = buffer->begin; buffer->last = buffer->begin; buffer->clip = nk_null_rect; +#ifdef NK_DRAW_CRC + buffer->crc = NK_CRC_SEED; +#endif } NK_LIB void nk_start(struct nk_context *ctx, struct nk_window *win) @@ -236,6 +242,9 @@ nk_finish(struct nk_context *ctx, struct nk_window *win) NK_ASSERT(ctx); NK_ASSERT(win); if (!ctx || !win) return; +#ifdef NK_DRAW_CRC + ctx->crc = NK_CRC_FUNC(ctx->crc, &win->buffer.crc, sizeof(win->buffer.crc)); +#endif nk_finish_buffer(ctx, &win->buffer); if (!win->popup.buf.active) return; diff --git a/src/nuklear_crc.c b/src/nuklear_crc.c new file mode 100644 index 000000000..85d14ab43 --- /dev/null +++ b/src/nuklear_crc.c @@ -0,0 +1,199 @@ +/** + * \file nuklear_crc.c + * \brief Implements CRC checking of the draw command buffer. + * + * \details Implements a rolling CRC that gets updated with every draw_push + * function call. That is, for every `nk_xxx` api function call between + * `nk_begin()` and `nk_end()` there is one to many draw commands pushed into + * the draw buffer. For each one of those pushes, the command type is XOR'd + * then stored into that buffer's CRC member. Then on the next `nk_xxx` function + * call, another command push is executed and the previous crc is updated with + * the new XOR of the new command. This will result in a unique CRC value in the + * buffers crc member at the end of the draw execution. + * + * The purpose of this is to allow super cheap and fast detection of unique'ness + * between draw loops, such that the user can determine if they need to redraw + * the screen or not. The original method was to have the user store a copy of + * the entire draw buffer, then loop through the buffer and compare that with + * the most recent draw buffer. Once done, you can determine if something + * changed on the screen and needs to be `nk_convert`'d. Doing it that way would + * require 1 loop to fill the command buffer, 1 loop to check the command buffer + * against the previously stored buffer, then 1 more loop to copy the new buffer + * into the old for the next draw loop iteration. That is 3 loops of + * `command_buffer'length()` which could end up being quite large; making this + * an expensive operation. Which almost defeats the purpose of detecting + * unique'ness in the first place. + * + * Yet implementing this CRC, we cruch all of that down into just the initial + * draw loop. Then you have a unique code stored in a 32-bit integer that + * represents the buffer. So if you need to know if the current draw command + * buffer is different from the previous one; you simply need to retain a single + * interger value and then check whether the old interger is equal to the new + * one. We reduced 3x O(n) loop complexity and 2x O(n) memory/space complexity + * down to 1x O(n) loop complexity and O(1) memory complexity. The CRC + * calculation (depending on default or custom implementation) simply adds 1 + * table lookup and 1 XOR operation to a single integer. Practically a free + * upgrade by comparison. + * + * If you want to use the CRC, then you must either `#define NK_DRAW_CRC` + * or `#define NK_DRAW_CRC_CUSTOM` + * + * `#define NK_DRAW_CRC` will implement the default CRC algorithm which is + * a 32 bit castagnoli (CRC-32C). This is a very common CRC that is used in + * iSCSI, SMPTE, ext-4, etc. So common that intel processors have dedicated + * hardware for it and lots of libraries have SIMD acceleration for it (not that + * its necessary for this application) + * + * `#define NK_DRAW_CRC_CUSTOM` will allow the user to define their own CRC + * implementation. This is available because most embedded systems already have + * a CRC calculation of some kind, usually defined by their system constraints, + * so it would be redundant to create yet another CRC method. + * + * To use the `NK_DRAW_CRC_CUSTOM` the user will need to `#define + * NK_CRC_SEED` to their desired seed and also `#define NK_CRC_FUNC(c,d,l)` to + * point to a user defined implementation of the crc. + * + * The user defined implementation of the CRC **SHALL** have the declaration of + * ```c + * NK_UINT32 (NK_UINT32 crc, NK_UINT8 *data, NK_SIZE_TYPE len) + * ``` + * That is, Shall : + * - return a 32 bit integer (the new CRC value) + * - accept, as the first argument, a 32 bit integer. The initial value of the crc. + * - accept, as the second argument, a pointer to a byte buffer to be CRC'd. + * - accept, as the third argument, the length of the byte buffer. + * + * other than that, the user is free to use any CRC algorithm, table, + * implementation they choose. + * + * \internal + * Information for nuklear developers. Each context includes a command buffer + * for drawing, usually called `ctx->current->buffer`. This is the buffer with + * the draw commands and we grab the CRC value stored with this buffer + * `ctx->current->buffer.crc`. The buffer, in regards to a context, only exists + * to draw a panel and will _dissappear_ once `nk_end()` is called. This is + * because nk_end commits the draw buffer to the context's overal draw array and + * closes out the `current` buffer to wait for another `nk_begin()` to start a + * new panel using the `nk_buffer_start()` command. + * + * What this means for the CRC is that, when a user decides they want to check + * on the CRC, they will do so **after** the `nk_end()` call (because you need + * to make sure all of the information is there). But attempting to grab the CRC + * at `ctx->current->buffer.crc` will result in a Seg Fault because that memory + * was deallocated with the `nk_end()` call. But not only that, we determined + * that the `current->buffer` is meant to be used for all sequences of + * `nk_begin()` to `nk_end()`. Meaning that we will get new CRCs for each panel; + * but only be able to look at the final CRC (unless we grab them as we go, but + * thats silly when we just want to know whether to draw or not). That means we + * better hope the user only ever interacts with which ever panel was the final + * one drawn, otherwise we wont see a crc change. + * + * The solution was to store the CRC at the Context level. So now `struct + * nk_context` contains a `crc` member that gets rolled when the current buffer + * finishes (i.e. `nk_end()` was called). This means that, whenever + * `nk_finish` is called, we calc the context CRC based off the current buffer + * crc. This retains the CRC value through successive calls to `nk_begin()` and + * `nk_end()` and rolls the number appropriately. + * \addtogroup crc + * \brief Command buffer CRC Implementation + * @{ + */ + +#ifdef NK_DRAW_CRC_CUSTOM +#ifndef NK_CRC_SEED +#error "Must #define NK_CRC_SEED when using NK_DRAW_CRC_CUSTOM" +#endif +#ifndef NK_CRC_FUNC(b,d,l) +#error "Must #define NK_CRC_FUNC(c,d,l) when using NK_DRAW_CRC_CUSTOM" +#endif +#define NK_DRAW_CRC +#endif + +#ifdef NK_DRAW_CRC +#define NK_CRC_SEED 0xffffffff /**< seed value of the crc*/ +#define NK_CRC_FUNC(c,d,l) nk_crc_update(c, d, l) +#include "nuklear.h" +#include "nuklear_internal.h" + +#ifndef NK_DRAW_CRC_CUSTOM +NK_STORAGE NK_UINT32 crc32c_table[] = { + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, 0x35F1141CL, + 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, 0x105EC76FL, 0xE235446CL, + 0xF165B798L, 0x030E349BL, 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 0x5D1D08BFL, 0xAF768BBCL, + 0xBC267848L, 0x4E4DFB4BL, 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, 0xAA64D611L, 0x580F5512L, + 0x4B5FA6E6L, 0xB93425E5L, 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, 0xF779DEAEL, 0x05125DADL, + 0x1642AE59L, 0xE4292D5AL, 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 0x417B1DBCL, 0xB3109EBFL, + 0xA0406D4BL, 0x522BEE48L, 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, 0x0C38D26CL, 0xFE53516FL, + 0xED03A29BL, 0x1F682198L, 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 0xDBFC821CL, 0x2997011FL, + 0x3AC7F2EBL, 0xC8AC71E8L, 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, 0xA65C047DL, 0x5437877EL, + 0x4767748AL, 0xB50CF789L, 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, 0x7198540DL, 0x83F3D70EL, + 0x90A324FAL, 0x62C8A7F9L, 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 0x3CDB9BDDL, 0xCEB018DEL, + 0xDDE0EB2AL, 0x2F8B6829L, 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, 0x082F63B7L, 0xFA44E0B4L, + 0xE9141340L, 0x1B7F9043L, 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 0x55326B08L, 0xA759E80BL, + 0xB4091BFFL, 0x466298FCL, 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, 0xA24BB5A6L, 0x502036A5L, + 0x4370C551L, 0xB11B4652L, 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, 0xEF087A76L, 0x1D63F975L, + 0x0E330A81L, 0xFC588982L, 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 0x38CC2A06L, 0xCAA7A905L, + 0xD9F75AF1L, 0x2B9CD9F2L, 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, 0x0417B1DBL, 0xF67C32D8L, + 0xE52CC12CL, 0x1747422FL, 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 0xD3D3E1ABL, 0x21B862A8L, + 0x32E8915CL, 0xC083125FL, 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, 0x9E902E7BL, 0x6CFBAD78L, + 0x7FAB5E8CL, 0x8DC0DD8FL, 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, 0x69E9F0D5L, 0x9B8273D6L, + 0x88D28022L, 0x7AB90321L, 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 0x34F4F86AL, 0xC69F7B69L, + 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L +}; + +#endif + + +/** + * \brief steps the crc value by the amount of new data. + * + * \details + * this function should only be called in the nk_command_buffer_push() function + * and should only be given the `type` enum of the command being pushed. As the + * buffer is getting filled, this function will roll and calculate the CRC + * value. Never clearing will result is a proper CRC at the end of the drawing + * updates. + * + * \param[in] crc is the previous value of the CRC from which to build on. + * \param[in] data is a pointer to the data to run the CRC on. + * \param[in] len is the size in bytes of the data. + * + * \returns the new crc value + */ +NK_LIB NK_UINT32 nk_crc_update(NK_UINT32 crc, NK_UINT8 *data, NK_SIZE_TYPE len) +{ + while (len--) crc = (crc<<8) ^ crc32c_table[(crc >> 24) ^ *data++]; + return crc; +} + +/** + * \brief returns the crc of the context + * \param[in] buf is the context for which to retrieve the CRC + */ +NK_API NK_UINT32 nk_get_crc(struct nk_context *ctx) +{ + return (ctx) ? ctx->crc : NK_CRC_SEED; +} +/** @} *//*end documentation grouping*/ +#endif /* NK_DRAW_CRC diff --git a/src/nuklear_draw.c b/src/nuklear_draw.c index 8505ed7bf..76c4de367 100644 --- a/src/nuklear_draw.c +++ b/src/nuklear_draw.c @@ -63,6 +63,9 @@ nk_command_buffer_push(struct nk_command_buffer* b, cmd->userdata = b->userdata; #endif b->end = cmd->next; +#ifdef NK_DRAW_CRC + b->crc = NK_CRC_FUNC(b->crc, (NK_UINT8*)cmd,size); +#endif return cmd; } NK_API void diff --git a/src/nuklear_internal.h b/src/nuklear_internal.h index a1523e8e9..e81fecbdd 100644 --- a/src/nuklear_internal.h +++ b/src/nuklear_internal.h @@ -126,6 +126,11 @@ NK_LIB nk_bool nk_is_lower(int c); NK_LIB nk_bool nk_is_upper(int c); NK_LIB int nk_to_upper(int c); NK_LIB int nk_to_lower(int c); +#ifdef NK_DRAW_CRC +#ifndef NK_DRAW_CRC_CUSTOM +NK_LIB NK_UINT32 nk_crc_update(NK_UINT32 crc, NK_UINT8 *data, NK_SIZE_TYPE len); +#endif +#endif #ifndef NK_MEMCPY NK_LIB void* nk_memcopy(void *dst, const void *src, nk_size n); diff --git a/src/nuklear_window.c b/src/nuklear_window.c index 8c286e44e..e2ddec5d8 100644 --- a/src/nuklear_window.c +++ b/src/nuklear_window.c @@ -288,6 +288,9 @@ nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, } win->layout = (struct nk_panel*)nk_create_panel(ctx); ctx->current = win; +#ifdef NK_DRAW_CRC + ctx->crc = NK_CRC_SEED; +#endif ret = nk_panel_begin(ctx, title, NK_PANEL_WINDOW); win->layout->offset_x = &win->scrollbar.x; win->layout->offset_y = &win->scrollbar.y; diff --git a/src/paq.bat b/src/paq.bat index 654734819..e11260c3a 100644 --- a/src/paq.bat +++ b/src/paq.bat @@ -1 +1 @@ -build.py --macro NK --intro HEADER.md --pub nuklear.h --priv1 nuklear_internal.h,nuklear_math.c,nuklear_util.c,nuklear_color.c,nuklear_utf8.c,nuklear_buffer.c,nuklear_string.c,nuklear_draw.c,nuklear_vertex.c --extern stb_rect_pack.h,stb_truetype.h --priv2 nuklear_font.c,nuklear_input.c,nuklear_style.c,nuklear_context.c,nuklear_pool.c,nuklear_page_element.c,nuklear_table.c,nuklear_panel.c,nuklear_window.c,nuklear_popup.c,nuklear_contextual.c,nuklear_menu.c,nuklear_layout.c,nuklear_tree.c,nuklear_group.c,nuklear_list_view.c,nuklear_widget.c,nuklear_text.c,nuklear_image.c,nuklear_9slice.c,nuklear_button.c,nuklear_toggle.c,nuklear_selectable.c,nuklear_slider.c,nuklear_knob.c,nuklear_progress.c,nuklear_scrollbar.c,nuklear_text_editor.c,nuklear_edit.c,nuklear_property.c,nuklear_chart.c,nuklear_color_picker.c,nuklear_combo.c,nuklear_tooltip.c --outro LICENSE,CHANGELOG,CREDITS > ..\nuklear.h +build.py --macro NK --intro HEADER.md --pub nuklear.h --priv1 nuklear_internal.h,nuklear_math.c,nuklear_util.c,nuklear_color.c,nuklear_utf8.c,nuklear_buffer.c,nuklear_string.c,nuklear_draw.c,nuklear_vertex.c --extern stb_rect_pack.h,stb_truetype.h --priv2 nuklear_crc.c,nuklear_font.c,nuklear_input.c,nuklear_style.c,nuklear_context.c,nuklear_pool.c,nuklear_page_element.c,nuklear_table.c,nuklear_panel.c,nuklear_window.c,nuklear_popup.c,nuklear_contextual.c,nuklear_menu.c,nuklear_layout.c,nuklear_tree.c,nuklear_group.c,nuklear_list_view.c,nuklear_widget.c,nuklear_text.c,nuklear_image.c,nuklear_9slice.c,nuklear_button.c,nuklear_toggle.c,nuklear_selectable.c,nuklear_slider.c,nuklear_knob.c,nuklear_progress.c,nuklear_scrollbar.c,nuklear_text_editor.c,nuklear_edit.c,nuklear_property.c,nuklear_chart.c,nuklear_color_picker.c,nuklear_combo.c,nuklear_tooltip.c --outro LICENSE,CHANGELOG,CREDITS > ..\nuklear.h diff --git a/src/paq.sh b/src/paq.sh index 5e8a1da48..88f520fa3 100755 --- a/src/paq.sh +++ b/src/paq.sh @@ -1,2 +1,2 @@ #!/bin/sh -python3 build.py --macro NK --intro HEADER.md --pub nuklear.h --priv1 nuklear_internal.h,nuklear_math.c,nuklear_util.c,nuklear_color.c,nuklear_utf8.c,nuklear_buffer.c,nuklear_string.c,nuklear_draw.c,nuklear_vertex.c --extern stb_rect_pack.h,stb_truetype.h --priv2 nuklear_font.c,nuklear_input.c,nuklear_style.c,nuklear_context.c,nuklear_pool.c,nuklear_page_element.c,nuklear_table.c,nuklear_panel.c,nuklear_window.c,nuklear_popup.c,nuklear_contextual.c,nuklear_menu.c,nuklear_layout.c,nuklear_tree.c,nuklear_group.c,nuklear_list_view.c,nuklear_widget.c,nuklear_text.c,nuklear_image.c,nuklear_9slice.c,nuklear_button.c,nuklear_toggle.c,nuklear_selectable.c,nuklear_slider.c,nuklear_knob.c,nuklear_progress.c,nuklear_scrollbar.c,nuklear_text_editor.c,nuklear_edit.c,nuklear_property.c,nuklear_chart.c,nuklear_color_picker.c,nuklear_combo.c,nuklear_tooltip.c --outro LICENSE,CHANGELOG,CREDITS > ../nuklear.h +python3 build.py --macro NK --intro HEADER.md --pub nuklear.h --priv1 nuklear_internal.h,nuklear_math.c,nuklear_util.c,nuklear_color.c,nuklear_utf8.c,nuklear_buffer.c,nuklear_string.c,nuklear_crc.c,nuklear_draw.c,nuklear_vertex.c --extern stb_rect_pack.h,stb_truetype.h --priv2 nuklear_font.c,nuklear_input.c,nuklear_style.c,nuklear_context.c,nuklear_pool.c,nuklear_page_element.c,nuklear_table.c,nuklear_panel.c,nuklear_window.c,nuklear_popup.c,nuklear_contextual.c,nuklear_menu.c,nuklear_layout.c,nuklear_tree.c,nuklear_group.c,nuklear_list_view.c,nuklear_widget.c,nuklear_text.c,nuklear_image.c,nuklear_9slice.c,nuklear_button.c,nuklear_toggle.c,nuklear_selectable.c,nuklear_slider.c,nuklear_knob.c,nuklear_progress.c,nuklear_scrollbar.c,nuklear_text_editor.c,nuklear_edit.c,nuklear_property.c,nuklear_chart.c,nuklear_color_picker.c,nuklear_combo.c,nuklear_tooltip.c --outro LICENSE,CHANGELOG,CREDITS > ../nuklear.h