Procházet zdrojové kódy

Migration to this repository

master
Patrick Jakobsen před 7 měsíci
rodič
revize
6b196fc399
16 změnil soubory, kde provedl 10178 přidání a 0 odebrání
  1. +1
    -0
      gui/build.sh
  2. +1
    -0
      gui/build_fast.sh
  3. +2
    -0
      gui/build_stbtt.sh
  4. +3250
    -0
      gui/gui.c
  5. +51
    -0
      gui/memory_arena.c
  6. +27
    -0
      gui/memory_arena_linux.c
  7. binární
      gui/michroma/Michroma-Regular.ttf
  8. +94
    -0
      gui/michroma/OFL.txt
  9. +9
    -0
      gui/size_of_file.c
  10. +2
    -0
      gui/stb_truetype.c
  11. +5077
    -0
      gui/stb_truetype.h
  12. +423
    -0
      rdic.h
  13. +1
    -0
      test/build.sh
  14. +51
    -0
      test/memory_arena.c
  15. +27
    -0
      test/memory_arena_linux.c
  16. +1162
    -0
      test/rdic_test.c

+ 1
- 0
gui/build.sh Zobrazit soubor

@ -0,0 +1 @@
gcc -g -Wall -Wextra $1 -fsanitize=address,undefined gui.c stb_truetype.o -lX11 -lm

+ 1
- 0
gui/build_fast.sh Zobrazit soubor

@ -0,0 +1 @@
gcc -g -Wall -Wextra $1 gui.c stb_truetype.o -lX11 -lm

+ 2
- 0
gui/build_stbtt.sh Zobrazit soubor

@ -0,0 +1,2 @@
gcc -O3 -c stb_truetype.c -o stb_truetype.o

+ 3250
- 0
gui/gui.c
Diff nebyl zobrazen, protože je příliš veliký
Zobrazit soubor


+ 51
- 0
gui/memory_arena.c Zobrazit soubor

@ -0,0 +1,51 @@
typedef struct Memory_Arena
{
void *memory;
size_t size;
size_t allocation_offset;
} Memory_Arena;
_Static_assert(sizeof(size_t) == sizeof(void*),
"The Memory_Arena implementation assumes that size_t and pointers are of the same size.");
void *memory_arena_allocate(
Memory_Arena *arena,
size_t size,
size_t alignment)
{
#if 0
printf(
"%s: arena: {.memory=%p, .size=%zu, .allocation_offset=%zu}\n"
"size: %zu, alignment: %zu\n"
, __func__
, arena->memory, arena->size, arena->allocation_offset
, size, alignment
);
#endif
size_t allocation_offset = arena->allocation_offset;
allocation_offset = ((allocation_offset + alignment - 1) / alignment) * alignment;
if(allocation_offset + size > arena->size)
{
return NULL;
}
void *allocation = ((char*)arena->memory) + allocation_offset;
arena->allocation_offset = allocation_offset + size;
return allocation;
}
void memory_arena_reset(
Memory_Arena *arena)
{
arena->allocation_offset = 0;
}

+ 27
- 0
gui/memory_arena_linux.c Zobrazit soubor

@ -0,0 +1,27 @@
#include <errno.h>
#include <sys/mman.h>
// NOTE(Patrick): The memory here is by default allocated uncommitted.
// This means that we don't lock down 1GB of memory up front, but get
// allocated pages by the kernel as needed.
int linux_allocate_arena_memory(
Memory_Arena *arena,
size_t arena_size)
{
void *arena_memory = mmap(
NULL, arena_size,
PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
if(arena_memory == (void*)-1) {
//perror("mmap");
return errno;
}
arena->memory = arena_memory;
arena->size = arena_size;
return 0;
}

binární
gui/michroma/Michroma-Regular.ttf Zobrazit soubor


+ 94
- 0
gui/michroma/OFL.txt Zobrazit soubor

@ -0,0 +1,94 @@
Copyright (c) 2011, Vernon Adams (vern@newtypography.co.uk),
with Reserved Font Name Michroma.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

+ 9
- 0
gui/size_of_file.c Zobrazit soubor

@ -0,0 +1,9 @@
long size_of_file(
FILE *file)
{
long position = ftell(file);
fseek(file, 0, SEEK_END);
long file_length = ftell(file);
fseek(file, position, SEEK_SET);
return file_length;
}

+ 2
- 0
gui/stb_truetype.c Zobrazit soubor

@ -0,0 +1,2 @@
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"

+ 5077
- 0
gui/stb_truetype.h
Diff nebyl zobrazen, protože je příliš veliký
Zobrazit soubor


+ 423
- 0
rdic.h Zobrazit soubor

@ -0,0 +1,423 @@
typedef struct RDIC_Node RDIC_Node;
struct RDIC_Node {
unsigned int generation;
RDIC_Node *parent; // Ease of access.
RDIC_Node *sibling;
RDIC_Node *first_child;
//RDIC_Node *last_child; // Ease of access.
//RDIC_Node *previous_sibling; // Ease of access.
#if 0
NOTE(Zelaven):
Pre-order traversal is self-then-first_child.
Post-order traversal is first_child-then-self.
The sibling always comes last.
#endif
};
typedef struct {
RDIC_Node *node;
unsigned int generation;
} RDIC_Node_Reference;
// IDEA(Zelaven): Can the node reference hold only the pointer, and the pointed-to node hold a back-pointer to the reference?
// There is only one valid reference at a time, so the reference validity should be easy to determine without use of generations.
// NOTE(Zelaven): This idea is bad because it assumes that a reference won't be reallocated. Generations are much more reliable.
typedef struct {
RDIC_Node *root;
RDIC_Node *node_freelist;
RDIC_Node *current_parent;
RDIC_Node *frame_current_node;
} RDIC_Context;
static inline int gui_node_references_equal(
RDIC_Node_Reference n,
RDIC_Node_Reference m)
{
return (n.node == m.node) && (n.generation == m.generation);
}
enum {
RDIC_GET_NODE__NODES_COLLECTED = 1 << 0,
RDIC_GET_NODE__NODE_ALLOCATED = 1 << 1,
RDIC_GET_NODE__OUT_OF_FREELIST = 1 << 2,
};
void rdic_cull_subtrees(
RDIC_Context context[static 1],
RDIC_Node *subtree);
RDIC_Node_Reference rdic_context_start_frame(
RDIC_Context context[static 1],
RDIC_Node_Reference last_root);
int rdic_context_finish_frame(
RDIC_Context context[static 1]);
RDIC_Node_Reference rdic_get_node(
RDIC_Context context[static 1],
RDIC_Node_Reference node_reference,
int *out_flags); // NOTE(Zelaven): NULL-safe.
void rdic_push_parent(
RDIC_Context context[static 1],
RDIC_Node_Reference parent);
int rdic_pop_parent(
RDIC_Context context[static 1]);
#ifndef NULL
#warning "NULL not defined. Please define NULL or include stddef."
#define NULL ((void*)0)
#endif
#ifdef RDIC_IMPLEMENTATION
// TODO(Zelaven): Offer a macro that enables runtime NULL-guards.
// ---
/* *** Information for those reading this source ***
* Procedure parameters such as `RDIC_Context context[static 1]` may look
* strange, but they aren't as evil as they look. Arrays degrade to pointers,
* and the static keyword simply requires that the array has a minimum size. In
* this case we use this to pass a pointer that may not be NULL. Note that this
* is just a compile time check and guard clauses for runtime NULL can still
* make plenty of sense.
*/
void rdic_cull_subtrees(
RDIC_Context context[static 1],
RDIC_Node *subtree)
{
assert(context != NULL);
// NOTE(Zelaven):
// This code collects an entire subtree.
// It requires that the initial node has a NULL sibling to limit its
// operation to only the subtree.
// The way it operates is that it uses the parent pointers to maintain a
// stack of "collect later" whenever a node in the tree has a sibling.
// When a subtree is finished it "pops" an element from the stack and
// continues collection from there.
RDIC_Node *current = subtree;
// NOTE(Zelaven): Top of our "stack" of "collect later".
RDIC_Node *stashed = NULL;
while(current != NULL)
{
RDIC_Node *next = NULL;
if(current->first_child != NULL)
{
if(current->sibling != NULL)
{
// Has both a child and a sibling - we need to save one for later.
// We "push" the sibling onto our "stack".
current->sibling->parent = stashed;
stashed = current->sibling;
}
// We always take the child as the next node.
next = current->first_child;
}
else if(current->sibling != NULL)
{
// No child but we have a sibling, then we just continue with the sibling.
next = current->sibling;
}
else
{
// A node with no child and no sibling. Time to "pop" from out "stack".
next = stashed;
if(stashed != NULL)
{
stashed = stashed->parent;
}
}
current->sibling = context->node_freelist;
current->generation += 1; // Invalidate references.
context->node_freelist = current;
current = next;
}
}
// ---
RDIC_Node_Reference rdic_context_start_frame(
RDIC_Context context[static 1],
RDIC_Node_Reference last_root)
{
RDIC_Node_Reference node_reference = last_root;
int need_new_node = (last_root.node == NULL)
|| (last_root.generation != last_root.node->generation);
if(need_new_node && context->node_freelist == NULL)
{
return (RDIC_Node_Reference){0};
}
if(need_new_node)
{
RDIC_Node_Reference result;
result.node = context->node_freelist;
result.generation = result.node->generation;
context->node_freelist = context->node_freelist->sibling;
RDIC_Node *root = result.node;
root->sibling = NULL;
root->parent = root;
node_reference = result;
}
context->frame_current_node = node_reference.node;
context->root = node_reference.node;
context->current_parent = context->root;
return node_reference;
}
// TODO(Zelaven): This is almost 1-1 with pop_parent, so consider compression.
// TODO(Zelaven): pop parents until it gets to a node that is a child of the
// root. This is so that all the stragglers of partial parents can be collected.
int rdic_context_finish_frame(
RDIC_Context context[static 1])
{
assert(context->current_parent == context->root);
RDIC_Node *last_root_child = NULL;
RDIC_Node *first_straggler = NULL;
if(context->frame_current_node == context->root)
{
last_root_child = context->root->first_child;
first_straggler = last_root_child;
}
else
{
last_root_child = context->frame_current_node;
first_straggler = last_root_child->sibling;
last_root_child->sibling = NULL;
}
int nodes_collected = 0;
if(last_root_child == NULL)
{
return 0;
}
assert(last_root_child->parent == context->root);
if(first_straggler != NULL)
{
rdic_cull_subtrees(context, first_straggler);
RDIC_Node *root = context->root;
if(first_straggler == root->first_child)
{
root->first_child = NULL;
}
nodes_collected = RDIC_GET_NODE__NODES_COLLECTED;
}
return nodes_collected;
}
// ---
RDIC_Node_Reference rdic_get_node(
RDIC_Context context[static 1],
RDIC_Node_Reference node_reference,
int *out_flags) // NOTE(Zelaven): NULL-safe.
{
//assert(context->current_parent != NULL);
if(context->current_parent == NULL)
{
return (RDIC_Node_Reference){0};
}
int reference_is_valid =
(node_reference.node != NULL)
&& (node_reference.generation == node_reference.node->generation);
int first_node_of_parent =
(context->current_parent == context->frame_current_node);
// NOTE(Zelaven): If the node is being moved to another parent, then we want
// to invalidate it to ensure consistent behavior.
reference_is_valid =
reference_is_valid
&& (context->current_parent == node_reference.node->parent);
if(!reference_is_valid && context->node_freelist == NULL)
{
if(out_flags != NULL)
{
*out_flags = RDIC_GET_NODE__OUT_OF_FREELIST;
}
return (RDIC_Node_Reference){0};
}
if(reference_is_valid)
{
RDIC_Node *subroot_to_cull = NULL;
if(first_node_of_parent)
{
// NOTE(Zelaven): Here I need to collect any preceding stale nodes.
subroot_to_cull = context->current_parent->first_child;
context->frame_current_node = node_reference.node;
context->current_parent->first_child = node_reference.node;
}
else
{
// NOTE(Zelaven): Skip (and collect) nodes between last and new nodes.
subroot_to_cull = context->frame_current_node->sibling;
context->frame_current_node->sibling = node_reference.node;
context->frame_current_node = node_reference.node;
}
if(subroot_to_cull != node_reference.node)
{
if(out_flags != NULL)
{
*out_flags = RDIC_GET_NODE__NODES_COLLECTED;
}
}
while(subroot_to_cull != node_reference.node)
{
// NOTE(Zelaven): Here we want to cull a subtree at a time because we are
// only skipping a certain quantity subtrees.
// TODO(Zelaven): Would it be preferable to "detach" the last node to cull
// and just to a single call to rdic_cull_subtrees?
// It really depends on what is faster, so that is profiling territory.
RDIC_Node *current = subroot_to_cull;
subroot_to_cull = subroot_to_cull->sibling;
current->sibling = NULL;
rdic_cull_subtrees(context, current);
}
}
else if(!reference_is_valid)
{
RDIC_Node_Reference new_reference = {0};
{ // Make new node
// NOTE(Zelaven): We guard against empty freelist, so this is safe.
RDIC_Node_Reference result = {0};
result.node = context->node_freelist;
result.generation = result.node->generation;
context->node_freelist = context->node_freelist->sibling;
// TODO(Zelaven): Should the various fields be filled out here, or outside?
// What will be the strategy to avoid redundant work?
// Doing it here certainly could avoid redundant work, assuming that all
// the styling and other fields will stay stable across frames.
// That will likely be the common case, but it may be necessary to expose
// reference_is_valid to the caller, so that the caller can decide if it
// should be used to avoid redundancy of work, whatever that means for
// the given node.
// I suppose that the caller can already determine that by comparing the
// last_reference with the new_reference, and if they are different then
// all work must be done.
new_reference = result;
}
RDIC_Node *new_node = new_reference.node;
if(first_node_of_parent)
{
RDIC_Node *parent = context->current_parent;
RDIC_Node *former_first_child = parent->first_child;
parent->first_child = new_node;
new_node->sibling = former_first_child;
new_node->parent = parent;
}
else
{
RDIC_Node *current = context->frame_current_node;
RDIC_Node *old_sibling = current->sibling;
current->sibling = new_node;
new_node->sibling = old_sibling;
new_node->parent = context->current_parent;
}
new_node->first_child = NULL;
context->frame_current_node = new_node;
node_reference = new_reference;
if(out_flags != NULL)
{
*out_flags = RDIC_GET_NODE__NODE_ALLOCATED;
}
}
return node_reference;
}
// ---
void rdic_push_parent(
RDIC_Context context[static 1],
RDIC_Node_Reference parent)
{
//assert(parent.node != NULL);
if(parent.node == NULL)
{
return;
}
context->current_parent = parent.node;
}
int rdic_pop_parent(
RDIC_Context context[static 1])
{
RDIC_Node *current_parent = context->current_parent;
RDIC_Node *last_child = NULL;
RDIC_Node *first_straggler = NULL;
if(context->frame_current_node == context->current_parent)
{
last_child = current_parent->first_child;
first_straggler = last_child;
}
else
{
last_child = context->frame_current_node;
first_straggler = last_child->sibling;
last_child->sibling = NULL;
}
int nodes_collected = 0;
if(last_child != NULL)
{
assert(last_child->parent == context->current_parent);
if(first_straggler != NULL)
{
rdic_cull_subtrees(context, first_straggler);
if(first_straggler == current_parent->first_child)
{
current_parent->first_child = NULL;
}
nodes_collected = RDIC_GET_NODE__NODES_COLLECTED;
}
}
context->frame_current_node = context->current_parent;
context->current_parent = context->current_parent->parent;
assert(context->current_parent != NULL);
return nodes_collected;
}
#endif /* RDIC_IMPLEMENTATION */

+ 1
- 0
test/build.sh Zobrazit soubor

@ -0,0 +1 @@
gcc -std=c99 -ggdb -Wall -Wextra -fsanitize=address,undefined rdic_test.c

+ 51
- 0
test/memory_arena.c Zobrazit soubor

@ -0,0 +1,51 @@
typedef struct Memory_Arena
{
void *memory;
size_t size;
size_t allocation_offset;
} Memory_Arena;
_Static_assert(sizeof(size_t) == sizeof(void*),
"The Memory_Arena implementation assumes that size_t and pointers are of the same size.");
void *memory_arena_allocate(
Memory_Arena *arena,
size_t size,
size_t alignment)
{
#if 0
printf(
"%s: arena: {.memory=%p, .size=%zu, .allocation_offset=%zu}\n"
"size: %zu, alignment: %zu\n"
, __func__
, arena->memory, arena->size, arena->allocation_offset
, size, alignment
);
#endif
size_t allocation_offset = arena->allocation_offset;
allocation_offset = ((allocation_offset + alignment - 1) / alignment) * alignment;
if(allocation_offset + size > arena->size)
{
return NULL;
}
void *allocation = ((char*)arena->memory) + allocation_offset;
arena->allocation_offset = allocation_offset + size;
return allocation;
}
void memory_arena_reset(
Memory_Arena *arena)
{
arena->allocation_offset = 0;
}

+ 27
- 0
test/memory_arena_linux.c Zobrazit soubor

@ -0,0 +1,27 @@
#include <errno.h>
#include <sys/mman.h>
// NOTE(Patrick): The memory here is by default allocated uncommitted.
// This means that we don't lock down 1GB of memory up front, but get
// allocated pages by the kernel as needed.
int linux_allocate_arena_memory(
Memory_Arena *arena,
size_t arena_size)
{
void *arena_memory = mmap(
NULL, arena_size,
PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
if(arena_memory == (void*)-1) {
//perror("mmap");
return errno;
}
arena->memory = arena_memory;
arena->size = arena_size;
return 0;
}

+ 1162
- 0
test/rdic_test.c
Diff nebyl zobrazen, protože je příliš veliký
Zobrazit soubor


Načítá se…
Zrušit
Uložit