diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 8671920f5..550daed3b 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -397,11 +397,34 @@ Logger :: struct { options: Logger_Options, } + +Random_Generator_Mode :: enum { + Read, + Reset, + Query_Info, +} + +Random_Generator_Query_Info_Flag :: enum u32 { + Cryptographic, + Uniform, + External_Entropy, + Resettable, +} +Random_Generator_Query_Info :: distinct bit_set[Random_Generator_Query_Info_Flag; u32] + +Random_Generator_Proc :: #type proc(data: rawptr, mode: Random_Generator_Mode, p: []byte) + +Random_Generator :: struct { + procedure: Random_Generator_Proc, + data: rawptr, +} + Context :: struct { allocator: Allocator, temp_allocator: Allocator, assertion_failure_proc: Assertion_Failure_Proc, logger: Logger, + random_generator: Random_Generator, user_ptr: rawptr, user_index: int, @@ -708,6 +731,9 @@ __init_context :: proc "contextless" (c: ^Context) { c.logger.procedure = default_logger_proc c.logger.data = nil + + c.random_generator.procedure = default_random_generator_proc + c.random_generator.data = nil } default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! { diff --git a/base/runtime/random_generator.odin b/base/runtime/random_generator.odin new file mode 100644 index 000000000..205d7eb7e --- /dev/null +++ b/base/runtime/random_generator.odin @@ -0,0 +1,115 @@ +package runtime + +import "base:intrinsics" + +@(require_results) +random_generator_read_bytes :: proc(rg: Random_Generator, p: []byte) -> bool { + if rg.procedure != nil { + rg.procedure(rg.data, .Read, p) + return true + } + return false +} + +@(require_results) +random_generator_read_ptr :: proc(rg: Random_Generator, p: rawptr, len: uint) -> bool { + if rg.procedure != nil { + rg.procedure(rg.data, .Read, ([^]byte)(p)[:len]) + return true + } + return false +} + +@(require_results) +random_generator_query_info :: proc(rg: Random_Generator) -> (info: Random_Generator_Query_Info) { + if rg.procedure != nil { + rg.procedure(rg.data, .Query_Info, ([^]byte)(&info)[:size_of(info)]) + } + return +} + + +random_generator_reset_bytes :: proc(rg: Random_Generator, p: []byte) { + if rg.procedure != nil { + rg.procedure(rg.data, .Reset, p) + } +} + +random_generator_reset_u64 :: proc(rg: Random_Generator, p: u64) { + if rg.procedure != nil { + p := p + rg.procedure(rg.data, .Reset, ([^]byte)(&p)[:size_of(p)]) + } +} + + +Default_Random_State :: struct { + state: u64, + inc: u64, +} + +default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, p: []byte) { + @(require_results) + read_u64 :: proc "contextless" (r: ^Default_Random_State) -> u64 { + old_state := r.state + r.state = old_state * 6364136223846793005 + (r.inc|1) + xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081 + rot := (old_state >> 59) + return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63)) + } + + @(thread_local) + global_rand_seed: Default_Random_State + + init :: proc "contextless" (r: ^Default_Random_State, seed: u64) { + seed := seed + if seed == 0 { + seed = u64(intrinsics.read_cycle_counter()) + } + r.state = 0 + r.inc = (seed << 1) | 1 + _ = read_u64(r) + r.state += seed + _ = read_u64(r) + } + + r := &global_rand_seed + + switch mode { + case .Read: + if r.state == 0 && r.inc == 0 { + init(r, 0) + } + + pos := i8(0) + val := u64(0) + for &v in p { + if pos == 0 { + val = read_u64(r) + pos = 7 + } + v = byte(val) + val >>= 8 + pos -= 1 + } + + case .Reset: + seed: u64 + mem_copy_non_overlapping(&seed, raw_data(p), min(size_of(seed), len(p))) + init(r, seed) + + case .Query_Info: + if len(p) != size_of(Random_Generator_Query_Info) { + return + } + info := (^Random_Generator_Query_Info)(raw_data(p)) + info^ += {.Uniform, .Resettable} + } +} + +default_random_generator :: proc "contextless" (state: ^Default_Random_State = nil) -> Random_Generator { + return { + procedure = default_random_generator_proc, + data = state, + } +} \ No newline at end of file diff --git a/core/crypto/crypto.odin b/core/crypto/crypto.odin index f0874cc6d..cd80567e2 100644 --- a/core/crypto/crypto.odin +++ b/core/crypto/crypto.odin @@ -4,6 +4,7 @@ helper routines. */ package crypto +import "base:runtime" import "core:mem" // compare_constant_time returns 1 iff a and b are equal, 0 otherwise. @@ -58,3 +59,24 @@ rand_bytes :: proc (dst: []byte) { _rand_bytes(dst) } + + +random_generator :: proc() -> runtime.Random_Generator { + return { + procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) { + switch mode { + case .Read: + rand_bytes(p) + case .Reset: + // do nothing + case .Query_Info: + if len(p) != size_of(runtime.Random_Generator_Query_Info) { + return + } + info := (^runtime.Random_Generator_Query_Info)(raw_data(p)) + info^ += {.Uniform, .Cryptographic, .External_Entropy} + } + }, + data = nil, + } +} \ No newline at end of file diff --git a/core/encoding/entity/entity.odin b/core/encoding/entity/entity.odin index cee6230ef..f5208ad6f 100644 --- a/core/encoding/entity/entity.odin +++ b/core/encoding/entity/entity.odin @@ -56,38 +56,27 @@ CDATA_END :: "]]>" COMMENT_START :: "" -/* - Default: CDATA and comments are passed through unchanged. -*/ +// Default: CDATA and comments are passed through unchanged. XML_Decode_Option :: enum u8 { - /* - Do not decode & entities. It decodes by default. - If given, overrides `Decode_CDATA`. - */ + // Do not decode & entities. It decodes by default. If given, overrides `Decode_CDATA`. No_Entity_Decode, - /* - CDATA is unboxed. - */ + // CDATA is unboxed. Unbox_CDATA, - /* - Unboxed CDATA is decoded as well. - Ignored if `.Unbox_CDATA` is not given. - */ + // Unboxed CDATA is decoded as well. Ignored if `.Unbox_CDATA` is not given. Decode_CDATA, - /* - Comments are stripped. - */ + // Comments are stripped. Comment_Strip, + + // Normalize whitespace + Normalize_Whitespace, } XML_Decode_Options :: bit_set[XML_Decode_Option; u8] -/* - Decode a string that may include SGML/XML/HTML entities. - The caller has to free the result. -*/ +// Decode a string that may include SGML/XML/HTML entities. +// The caller has to free the result. decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator := context.allocator) -> (decoded: string, err: Error) { context.allocator = allocator @@ -100,14 +89,14 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator := t := Tokenizer{src=input} in_data := false + prev: rune = ' ' + loop: for { advance(&t) or_return if t.r < 0 { break loop } - /* - Below here we're never inside a CDATA tag. - At most we'll see the start of one, but that doesn't affect the logic. - */ + // Below here we're never inside a CDATA tag. At most we'll see the start of one, + // but that doesn't affect the logic. switch t.r { case '<': /* @@ -126,9 +115,7 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator := in_data = _handle_xml_special(&t, &builder, options) or_return case ']': - /* - If we're unboxing _and_ decoding CDATA, we'll have to check for the end tag. - */ + // If we're unboxing _and_ decoding CDATA, we'll have to check for the end tag. if in_data { if t.read_offset + len(CDATA_END) < len(t.src) { if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END { @@ -143,22 +130,16 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator := case: if in_data && .Decode_CDATA not_in options { - /* - Unboxed, but undecoded. - */ + // Unboxed, but undecoded. write_rune(&builder, t.r) continue } if t.r == '&' { if entity, entity_err := _extract_xml_entity(&t); entity_err != .None { - /* - We read to the end of the string without closing the entity. - Pass through as-is. - */ + // We read to the end of the string without closing the entity. Pass through as-is. write_string(&builder, entity) } else { - if .No_Entity_Decode not_in options { if decoded, ok := xml_decode_entity(entity); ok { write_rune(&builder, decoded) @@ -166,19 +147,41 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator := } } - /* - Literal passthrough because the decode failed or we want entities not decoded. - */ + // Literal passthrough because the decode failed or we want entities not decoded. write_string(&builder, "&") write_string(&builder, entity) write_string(&builder, ";") } } else { - write_rune(&builder, t.r) + // Handle AV Normalization: https://www.w3.org/TR/2006/REC-xml11-20060816/#AVNormalize + if .Normalize_Whitespace in options { + switch t.r { + case ' ', '\r', '\n', '\t': + if prev != ' ' { + write_rune(&builder, ' ') + prev = ' ' + } + case: + write_rune(&builder, t.r) + prev = t.r + } + } else { + // https://www.w3.org/TR/2006/REC-xml11-20060816/#sec-line-ends + switch t.r { + case '\n', 0x85, 0x2028: + write_rune(&builder, '\n') + case '\r': // Do nothing until next character + case: + if prev == '\r' { // Turn a single carriage return into a \n + write_rune(&builder, '\n') + } + write_rune(&builder, t.r) + } + prev = t.r + } } } } - return strings.clone(strings.to_string(builder), allocator), err } @@ -253,24 +256,18 @@ xml_decode_entity :: proc(entity: string) -> (decoded: rune, ok: bool) { return rune(val), true case: - /* - Named entity. - */ + // Named entity. return named_xml_entity_to_rune(entity) } } -/* - Private XML helper to extract `&;` entity. -*/ +// Private XML helper to extract `&;` entity. @(private="file") _extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) { assert(t != nil && t.r == '&') - /* - All of these would be in the ASCII range. - Even if one is not, it doesn't matter. All characters we need to compare to extract are. - */ + // All of these would be in the ASCII range. + // Even if one is not, it doesn't matter. All characters we need to compare to extract are. length := len(t.src) found := false @@ -292,9 +289,7 @@ _extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) { return string(t.src[t.offset : t.read_offset]), .Invalid_Entity_Encoding } -/* - Private XML helper for CDATA and comments. -*/ +// Private XML helper for CDATA and comments. @(private="file") _handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: XML_Decode_Options) -> (in_data: bool, err: Error) { assert(t != nil && t.r == '<') @@ -304,20 +299,14 @@ _handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: X t.read_offset += len(CDATA_START) - 1 if .Unbox_CDATA in options && .Decode_CDATA in options { - /* - We're unboxing _and_ decoding CDATA - */ + // We're unboxing _and_ decoding CDATA return true, .None } - /* - CDATA is passed through. - */ + // CDATA is passed through. offset := t.offset - /* - Scan until end of CDATA. - */ + // Scan until end of CDATA. for { advance(t) or_return if t.r < 0 { return true, .CDATA_Not_Terminated } @@ -341,14 +330,10 @@ _handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: X } else if string(t.src[t.offset:][:len(COMMENT_START)]) == COMMENT_START { t.read_offset += len(COMMENT_START) - /* - Comment is passed through by default. - */ + // Comment is passed through by default. offset := t.offset - /* - Scan until end of Comment. - */ + // Scan until end of Comment. for { advance(t) or_return if t.r < 0 { return true, .Comment_Not_Terminated } diff --git a/core/encoding/xml/tokenizer.odin b/core/encoding/xml/tokenizer.odin index 0f87c366b..2d06038b7 100644 --- a/core/encoding/xml/tokenizer.odin +++ b/core/encoding/xml/tokenizer.odin @@ -218,9 +218,7 @@ scan_identifier :: proc(t: ^Tokenizer) -> string { for is_valid_identifier_rune(t.ch) { advance_rune(t) if t.ch == ':' { - /* - A namespaced attr can have at most two parts, `namespace:ident`. - */ + // A namespaced attr can have at most two parts, `namespace:ident`. if namespaced { break } @@ -268,14 +266,10 @@ scan_comment :: proc(t: ^Tokenizer) -> (comment: string, err: Error) { return string(t.src[offset : t.offset - 1]), .None } -/* - Skip CDATA -*/ +// Skip CDATA skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) { if t.read_offset + len(CDATA_START) >= len(t.src) { - /* - Can't be the start of a CDATA tag. - */ + // Can't be the start of a CDATA tag. return .None } @@ -290,9 +284,7 @@ skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) { return .Premature_EOF } - /* - Scan until the end of a CDATA tag. - */ + // Scan until the end of a CDATA tag. if t.read_offset + len(CDATA_END) < len(t.src) { if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END { t.read_offset += len(CDATA_END) @@ -319,14 +311,10 @@ scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close case '<': if peek_byte(t) == '!' { if peek_byte(t, 1) == '[' { - /* - Might be the start of a CDATA tag. - */ + // Might be the start of a CDATA tag. skip_cdata(t) or_return } else if peek_byte(t, 1) == '-' && peek_byte(t, 2) == '-' { - /* - Comment start. Eat comment. - */ + // Comment start. Eat comment. t.read_offset += 3 _ = scan_comment(t) or_return } @@ -342,17 +330,13 @@ scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close } if t.ch == close { - /* - If it's not a CDATA or comment, it's the end of this body. - */ + // If it's not a CDATA or comment, it's the end of this body. break loop } advance_rune(t) } - /* - Strip trailing whitespace. - */ + // Strip trailing whitespace. lit := string(t.src[offset : t.offset]) end := len(lit) @@ -369,11 +353,6 @@ scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close if consume_close { advance_rune(t) } - - /* - TODO: Handle decoding escape characters and unboxing CDATA. - */ - return lit, err } @@ -384,7 +363,7 @@ peek :: proc(t: ^Tokenizer) -> (token: Token) { return token } -scan :: proc(t: ^Tokenizer) -> Token { +scan :: proc(t: ^Tokenizer, multiline_string := false) -> Token { skip_whitespace(t) offset := t.offset @@ -418,7 +397,7 @@ scan :: proc(t: ^Tokenizer) -> Token { case '"', '\'': kind = .Invalid - lit, err = scan_string(t, t.offset, ch, true, false) + lit, err = scan_string(t, t.offset, ch, true, multiline_string) if err == .None { kind = .String } @@ -435,4 +414,4 @@ scan :: proc(t: ^Tokenizer) -> Token { lit = string(t.src[offset : t.offset]) } return Token{kind, lit, pos} -} +} \ No newline at end of file diff --git a/core/encoding/xml/xml_reader.odin b/core/encoding/xml/xml_reader.odin index 5b4b12948..b9656900f 100644 --- a/core/encoding/xml/xml_reader.odin +++ b/core/encoding/xml/xml_reader.odin @@ -203,9 +203,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha doc.elements = make([dynamic]Element, 1024, 1024, allocator) - // strings.intern_init(&doc.intern, allocator, allocator) - - err = .Unexpected_Token + err = .Unexpected_Token element, parent: Element_ID open: Token @@ -259,8 +257,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha case .Slash: // Empty tag. Close it. expect(t, .Gt) or_return - parent = doc.elements[element].parent - element = parent + parent = doc.elements[element].parent + element = parent case: error(t, t.offset, "Expected close tag, got: %#v\n", end_token) @@ -276,8 +274,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha error(t, t.offset, "Mismatched Closing Tag. Expected %v, got %v\n", doc.elements[element].ident, ident.text) return doc, .Mismatched_Closing_Tag } - parent = doc.elements[element].parent - element = parent + parent = doc.elements[element].parent + element = parent } else if open.kind == .Exclaim { // (validated: Options, err: Error) { return validated, .None } -expect :: proc(t: ^Tokenizer, kind: Token_Kind) -> (tok: Token, err: Error) { - tok = scan(t) +expect :: proc(t: ^Tokenizer, kind: Token_Kind, multiline_string := false) -> (tok: Token, err: Error) { + tok = scan(t, multiline_string=multiline_string) if tok.kind == kind { return tok, .None } error(t, t.offset, "Expected \"%v\", got \"%v\".", kind, tok.kind) @@ -480,7 +478,13 @@ parse_attribute :: proc(doc: ^Document) -> (attr: Attribute, offset: int, err: E offset = t.offset - len(key.text) _ = expect(t, .Eq) or_return - value := expect(t, .String) or_return + value := expect(t, .String, multiline_string=true) or_return + + normalized, normalize_err := entity.decode_xml(value.text, {.Normalize_Whitespace}, doc.allocator) + if normalize_err == .None { + append(&doc.strings_to_free, normalized) + value.text = normalized + } attr.key = key.text attr.val = value.text diff --git a/core/fmt/doc.odin b/core/fmt/doc.odin index be666dcc4..d45e6c796 100644 --- a/core/fmt/doc.odin +++ b/core/fmt/doc.odin @@ -9,6 +9,7 @@ The verbs: General: %v the value in a default format %#v an expanded format of %v with newlines and indentation + %w an Odin-syntax representation of the value %T an Odin-syntax representation of the type of the value %% a literal percent sign; consumes no value {{ a literal open brace; consumes no value diff --git a/core/fmt/example.odin b/core/fmt/example.odin new file mode 100644 index 000000000..503e64f2b --- /dev/null +++ b/core/fmt/example.odin @@ -0,0 +1,57 @@ +//+build ignore +package custom_formatter_example +import "core:fmt" +import "core:io" + +SomeType :: struct { + value: int, +} + +My_Custom_Base_Type :: distinct u32 + +main :: proc() { + // Ensure the fmt._user_formatters map is initialized + fmt.set_user_formatters(new(map[typeid]fmt.User_Formatter)) + + // Register custom formatters for my favorite types + err := fmt.register_user_formatter(type_info_of(SomeType).id, SomeType_Formatter) + assert(err == .None) + err = fmt.register_user_formatter(type_info_of(My_Custom_Base_Type).id, My_Custom_Base_Formatter) + assert(err == .None) + + // Use the custom formatters. + fmt.printfln("SomeType{{42}}: '%v'", SomeType{42}) + fmt.printfln("My_Custom_Base_Type(0xdeadbeef): '%v'", My_Custom_Base_Type(0xdeadbeef)) +} + +SomeType_Formatter :: proc(fi: ^fmt.Info, arg: any, verb: rune) -> bool { + m := cast(^SomeType)arg.data + switch verb { + case 'v', 'd': // We handle `%v` and `%d` + fmt.fmt_int(fi, u64(m.value), true, 8 * size_of(SomeType), verb) + case: + return false + } + return true +} + +My_Custom_Base_Formatter :: proc(fi: ^fmt.Info, arg: any, verb: rune) -> bool { + m := cast(^My_Custom_Base_Type)arg.data + switch verb { + case 'v', 'b': + value := u64(m^) + for value > 0 { + if value & 1 == 1 { + io.write_string(fi.writer, "Hellope!", &fi.n) + } else { + io.write_string(fi.writer, "Hellope?", &fi.n) + } + value >>= 1 + } + + case: + return false + } + return true +} + diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index f9113a7a7..4c65dd01f 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1726,10 +1726,12 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') { et := runtime.type_info_base(info.elem) - if name != "" { - io.write_string(fi.writer, name, &fi.n) - } else { - reflect.write_type(fi.writer, type_info, &fi.n) + if verb != 'w' { + if name != "" { + io.write_string(fi.writer, name, &fi.n) + } else { + reflect.write_type(fi.writer, type_info, &fi.n) + } } io.write_byte(fi.writer, '{', &fi.n) defer io.write_byte(fi.writer, '}', &fi.n) @@ -1746,9 +1748,17 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') { } if is_enum { + enum_name: string + if ti_named, is_named := info.elem.variant.(runtime.Type_Info_Named); is_named { + enum_name = ti_named.name + } for ev, evi in e.values { v := u64(ev) if v == u64(i) { + if verb == 'w' { + io.write_string(fi.writer, enum_name, &fi.n) + io.write_byte(fi.writer, '.', &fi.n) + } io.write_string(fi.writer, e.names[evi], &fi.n) commas += 1 continue loop @@ -2391,7 +2401,6 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named) runtime.Type_Info_Dynamic_Array, runtime.Type_Info_Slice, runtime.Type_Info_Struct, - runtime.Type_Info_Union, runtime.Type_Info_Enum, runtime.Type_Info_Map, runtime.Type_Info_Bit_Set, @@ -2498,8 +2507,9 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix } } else { // Printed in Row-Major layout to match text layout + row_separator := ", " if verb == 'w' else "; " for row in 0.. 0 { io.write_string(fi.writer, "; ", &fi.n) } + if row > 0 { io.write_string(fi.writer, row_separator, &fi.n) } for col in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) } diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 1969fac49..ee09bb2c7 100644 --- a/core/math/big/helpers.odin +++ b/core/math/big/helpers.odin @@ -362,11 +362,11 @@ platform_count_lsb :: #force_inline proc(a: $T) -> (count: int) count_lsb :: proc { int_count_lsb, platform_count_lsb, } -int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) { +int_random_digit :: proc() -> (res: DIGIT) { when _DIGIT_BITS == 60 { // DIGIT = u64 - return DIGIT(rnd.uint64(r)) & _MASK + return DIGIT(rnd.uint64()) & _MASK } else when _DIGIT_BITS == 28 { // DIGIT = u32 - return DIGIT(rnd.uint32(r)) & _MASK + return DIGIT(rnd.uint32()) & _MASK } else { panic("Unsupported DIGIT size.") } @@ -374,12 +374,12 @@ int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) { return 0 // We shouldn't get here. } -int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) { +int_random :: proc(dest: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { /* Check that `a` is usable. */ assert_if_nil(dest) - return #force_inline internal_int_random(dest, bits, r, allocator) + return #force_inline internal_int_random(dest, bits, allocator) } random :: proc { int_random, } diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index fa747e46a..c9b331e55 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -2178,15 +2178,20 @@ internal_int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator } /* - If not yet iniialized, initialize the `digit` backing with the allocator we were passed. + If not yet initialized, initialize the `digit` backing with the allocator we were passed. */ if cap == 0 { a.digit = make([dynamic]DIGIT, needed, allocator) - } else if cap != needed { + } else if cap < needed { /* `[dynamic]DIGIT` already knows what allocator was used for it, so resize will do the right thing. */ resize(&a.digit, needed) + } else if cap > needed { + /* + Same applies to builtin.shrink here as resize above + */ + builtin.shrink(&a.digit, needed) } /* Let's see if the allocation/resize worked as expected. @@ -2812,11 +2817,11 @@ internal_platform_count_lsb :: #force_inline proc(a: $T) -> (count: int) internal_count_lsb :: proc { internal_int_count_lsb, internal_platform_count_lsb, } -internal_int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) { +internal_int_random_digit :: proc() -> (res: DIGIT) { when _DIGIT_BITS == 60 { // DIGIT = u64 - return DIGIT(rnd.uint64(r)) & _MASK + return DIGIT(rnd.uint64()) & _MASK } else when _DIGIT_BITS == 28 { // DIGIT = u32 - return DIGIT(rnd.uint32(r)) & _MASK + return DIGIT(rnd.uint32()) & _MASK } else { panic("Unsupported DIGIT size.") } @@ -2824,7 +2829,7 @@ internal_int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) { return 0 // We shouldn't get here. } -internal_int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) { +internal_int_random :: proc(dest: ^Int, bits: int, allocator := context.allocator) -> (err: Error) { context.allocator = allocator bits := bits @@ -2841,7 +2846,7 @@ internal_int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator #force_inline internal_grow(dest, digits) or_return for i := 0; i < digits; i += 1 { - dest.digit[i] = int_random_digit(r) & _MASK + dest.digit[i] = int_random_digit() & _MASK } if bits > 0 { dest.digit[digits - 1] &= ((1 << uint(bits)) - 1) diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin index 7fc78c7e5..832c75119 100644 --- a/core/math/big/prime.odin +++ b/core/math/big/prime.odin @@ -12,8 +12,6 @@ package math_big -import rnd "core:math/rand" - /* Determines if an Integer is divisible by one of the _PRIME_TABLE primes. Returns true if it is, false if not. @@ -315,7 +313,7 @@ internal_int_prime_miller_rabin :: proc(a, b: ^Int, allocator := context.allocat Assumes `a` not to be `nil` and to have been initialized. */ -internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_rabin_only := USE_MILLER_RABIN_ONLY, r: ^rnd.Rand = nil, allocator := context.allocator) -> (is_prime: bool, err: Error) { +internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_rabin_only := USE_MILLER_RABIN_ONLY, allocator := context.allocator) -> (is_prime: bool, err: Error) { context.allocator = allocator miller_rabin_trials := miller_rabin_trials @@ -461,7 +459,7 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra for ix := 0; ix < miller_rabin_trials; ix += 1 { // rand() guarantees the first digit to be non-zero - internal_random(b, _DIGIT_TYPE_BITS, r) or_return + internal_random(b, _DIGIT_TYPE_BITS) or_return // Reduce digit before casting because DIGIT might be bigger than // an unsigned int and "mask" on the other side is most probably not. @@ -1183,7 +1181,7 @@ internal_int_prime_next_prime :: proc(a: ^Int, trials: int, bbs_style: bool, all This is possibly the mother of all prime generation functions, muahahahahaha! */ -internal_random_prime :: proc(a: ^Int, size_in_bits: int, trials: int, flags := Primality_Flags{}, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) { +internal_random_prime :: proc(a: ^Int, size_in_bits: int, trials: int, flags := Primality_Flags{}, allocator := context.allocator) -> (err: Error) { context.allocator = allocator flags := flags trials := trials diff --git a/core/math/rand/distributions.odin b/core/math/rand/distributions.odin index 9365e8b76..a10ea3238 100644 --- a/core/math/rand/distributions.odin +++ b/core/math/rand/distributions.odin @@ -8,12 +8,12 @@ float32_uniform :: float32_range // Triangular Distribution // See: http://wikipedia.org/wiki/Triangular_distribution @(require_results) -float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64 { +float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64)) -> f64 { if hi-lo == 0 { return lo } lo, hi := lo, hi - u := float64(r) + u := float64() c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1) if u > c { u = 1-u @@ -26,12 +26,12 @@ float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64 // Triangular Distribution // See: http://wikipedia.org/wiki/Triangular_distribution @(require_results) -float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32 { +float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32)) -> f32 { if hi-lo == 0 { return lo } lo, hi := lo, hi - u := float32(r) + u := float32() c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1) if u > c { u = 1-u @@ -44,25 +44,25 @@ float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32 // Normal/Gaussian Distribution @(require_results) -float64_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 { - return norm_float64(r) * stddev + mean +float64_normal :: proc(mean, stddev: f64) -> f64 { + return norm_float64() * stddev + mean } // Normal/Gaussian Distribution @(require_results) -float32_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 { - return f32(float64_normal(f64(mean), f64(stddev), r)) +float32_normal :: proc(mean, stddev: f32) -> f32 { + return f32(float64_normal(f64(mean), f64(stddev))) } // Log Normal Distribution @(require_results) -float64_log_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 { - return math.exp(float64_normal(mean, stddev, r)) +float64_log_normal :: proc(mean, stddev: f64) -> f64 { + return math.exp(float64_normal(mean, stddev)) } // Log Normal Distribution @(require_results) -float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 { - return f32(float64_log_normal(f64(mean), f64(stddev), r)) +float32_log_normal :: proc(mean, stddev: f32) -> f32 { + return f32(float64_log_normal(f64(mean), f64(stddev))) } @@ -72,8 +72,8 @@ float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 { // 0 to positive infinity if lambda > 0 // negative infinity to 0 if lambda <= 0 @(require_results) -float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 { - return - math.ln(1 - float64(r)) / lambda +float64_exponential :: proc(lambda: f64) -> f64 { + return - math.ln(1 - float64()) / lambda } // Exponential Distribution // `lambda` is 1.0/(desired mean). It should be non-zero. @@ -81,8 +81,8 @@ float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 { // 0 to positive infinity if lambda > 0 // negative infinity to 0 if lambda <= 0 @(require_results) -float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 { - return f32(float64_exponential(f64(lambda), r)) +float32_exponential :: proc(lambda: f32) -> f32 { + return f32(float64_exponential(f64(lambda))) } @@ -96,7 +96,7 @@ float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 { // // mean is alpha*beta, variance is math.pow(alpha*beta, 2) @(require_results) -float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { +float64_gamma :: proc(alpha, beta: f64) -> f64 { if alpha <= 0 || beta <= 0 { panic(#procedure + ": alpha and beta must be > 0.0") } @@ -112,11 +112,11 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { bbb := alpha - LOG4 ccc := alpha + ainv for { - u1 := float64(r) + u1 := float64() if !(1e-7 < u1 && u1 < 0.9999999) { continue } - u2 := 1 - float64(r) + u2 := 1 - float64() v := math.ln(u1 / (1 - u1)) / ainv x := alpha * math.exp(v) z := u1 * u1 * u2 @@ -127,12 +127,12 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { } case alpha == 1: // float64_exponential(1/beta) - return -math.ln(1 - float64(r)) * beta + return -math.ln(1 - float64()) * beta case: // ALGORITHM GS of Statistical Computing - Kennedy & Gentle x: f64 for { - u := float64(r) + u := float64() b := (math.e + alpha) / math.e p := b * u if p <= 1 { @@ -140,7 +140,7 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { } else { x = -math.ln((b - p) / alpha) } - u1 := float64(r) + u1 := float64() if p > 1 { if u1 <= math.pow(x, alpha-1) { break @@ -162,8 +162,8 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { // // mean is alpha*beta, variance is math.pow(alpha*beta, 2) @(require_results) -float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { - return f32(float64_gamma(f64(alpha), f64(beta), r)) +float32_gamma :: proc(alpha, beta: f32) -> f32 { + return f32(float64_gamma(f64(alpha), f64(beta))) } @@ -173,14 +173,14 @@ float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { // // Return values range between 0 and 1 @(require_results) -float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { +float64_beta :: proc(alpha, beta: f64) -> f64 { if alpha <= 0 || beta <= 0 { panic(#procedure + ": alpha and beta must be > 0.0") } // Knuth Vol 2 Ed 3 pg 134 "the beta distribution" - y := float64_gamma(alpha, 1.0, r) + y := float64_gamma(alpha, 1.0) if y != 0 { - return y / (y + float64_gamma(beta, 1.0, r)) + return y / (y + float64_gamma(beta, 1.0)) } return 0 } @@ -190,35 +190,35 @@ float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { // // Return values range between 0 and 1 @(require_results) -float32_beta :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { - return f32(float64_beta(f64(alpha), f64(beta), r)) +float32_beta :: proc(alpha, beta: f32) -> f32 { + return f32(float64_beta(f64(alpha), f64(beta))) } // Pareto distribution, `alpha` is the shape parameter. // https://wikipedia.org/wiki/Pareto_distribution @(require_results) -float64_pareto :: proc(alpha: f64, r: ^Rand = nil) -> f64 { - return math.pow(1 - float64(r), -1.0 / alpha) +float64_pareto :: proc(alpha: f64) -> f64 { + return math.pow(1 - float64(), -1.0 / alpha) } // Pareto distribution, `alpha` is the shape parameter. // https://wikipedia.org/wiki/Pareto_distribution @(require_results) -float32_pareto :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { - return f32(float64_pareto(f64(alpha), r)) +float32_pareto :: proc(alpha, beta: f32) -> f32 { + return f32(float64_pareto(f64(alpha))) } // Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter. @(require_results) -float64_weibull :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 { - u := 1 - float64(r) +float64_weibull :: proc(alpha, beta: f64) -> f64 { + u := 1 - float64() return alpha * math.pow(-math.ln(u), 1.0/beta) } // Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter. @(require_results) -float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { - return f32(float64_weibull(f64(alpha), f64(beta), r)) +float32_weibull :: proc(alpha, beta: f32) -> f32 { + return f32(float64_weibull(f64(alpha), f64(beta))) } @@ -227,23 +227,23 @@ float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 { // `kappa` is the concentration parameter which must be >= 0 // When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi @(require_results) -float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 { +float64_von_mises :: proc(mean_angle, kappa: f64) -> f64 { // Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993. mu := mean_angle if kappa <= 1e-6 { - return math.TAU * float64(r) + return math.TAU * float64() } s := 0.5 / kappa t := s + math.sqrt(1 + s*s) z: f64 for { - u1 := float64(r) + u1 := float64() z = math.cos(math.TAU * 0.5 * u1) d := z / (t + z) - u2 := float64(r) + u2 := float64() if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) { break } @@ -251,7 +251,7 @@ float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 { q := 1.0 / t f := (q + z) / (1 + q*z) - u3 := float64(r) + u3 := float64() if u3 > 0.5 { return math.mod(mu + math.acos(f), math.TAU) } else { @@ -263,57 +263,57 @@ float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 { // `kappa` is the concentration parameter which must be >= 0 // When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi @(require_results) -float32_von_mises :: proc(mean_angle, kappa: f32, r: ^Rand = nil) -> f32 { - return f32(float64_von_mises(f64(mean_angle), f64(kappa), r)) +float32_von_mises :: proc(mean_angle, kappa: f32) -> f32 { + return f32(float64_von_mises(f64(mean_angle), f64(kappa))) } // Cauchy-Lorentz Distribution // `x_0` is the location, `gamma` is the scale where `gamma` > 0 @(require_results) -float64_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 { +float64_cauchy_lorentz :: proc(x_0, gamma: f64) -> f64 { assert(gamma > 0) // Calculated from the inverse CDF - return math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0 + return math.tan(math.PI * (float64() - 0.5))*gamma + x_0 } // Cauchy-Lorentz Distribution // `x_0` is the location, `gamma` is the scale where `gamma` > 0 @(require_results) -float32_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 { - return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma), r)) +float32_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 { + return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma))) } // Log Cauchy-Lorentz Distribution // `x_0` is the location, `gamma` is the scale where `gamma` > 0 @(require_results) -float64_log_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 { +float64_log_cauchy_lorentz :: proc(x_0, gamma: f64) -> f64 { assert(gamma > 0) - return math.exp(math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0) + return math.exp(math.tan(math.PI * (float64() - 0.5))*gamma + x_0) } // Log Cauchy-Lorentz Distribution // `x_0` is the location, `gamma` is the scale where `gamma` > 0 @(require_results) -float32_log_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 { - return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma), r)) +float32_log_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 { + return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma))) } // Laplace Distribution // `b` is the scale where `b` > 0 @(require_results) -float64_laplace :: proc(mean, b: f64, r: ^Rand = nil) -> f64 { +float64_laplace :: proc(mean, b: f64) -> f64 { assert(b > 0) - p := float64(r)-0.5 + p := float64()-0.5 return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean } // Laplace Distribution // `b` is the scale where `b` > 0 @(require_results) -float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 { - return f32(float64_laplace(f64(mean), f64(b), r)) +float32_laplace :: proc(mean, b: f32) -> f32 { + return f32(float64_laplace(f64(mean), f64(b))) } @@ -321,18 +321,18 @@ float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 { // `eta` is the shape, `b` is the scale // Both `eta` and `b` must be > 0 @(require_results) -float64_gompertz :: proc(eta, b: f64, r: ^Rand = nil) -> f64 { +float64_gompertz :: proc(eta, b: f64) -> f64 { if eta <= 0 || b <= 0 { panic(#procedure + ": eta and b must be > 0.0") } - p := float64(r) + p := float64() return math.ln(1 - math.ln(1 - p)/eta)/b } // Gompertz Distribution // `eta` is the shape, `b` is the scale // Both `eta` and `b` must be > 0 @(require_results) -float32_gompertz :: proc(eta, b: f32, r: ^Rand = nil) -> f32 { - return f32(float64_gompertz(f64(eta), f64(b), r)) +float32_gompertz :: proc(eta, b: f32) -> f32 { + return f32(float64_gompertz(f64(eta), f64(b))) } diff --git a/core/math/rand/exp.odin b/core/math/rand/exp.odin index ebc849b2f..f30f11f50 100644 --- a/core/math/rand/exp.odin +++ b/core/math/rand/exp.odin @@ -16,7 +16,7 @@ import "core:math" // https://www.jstatsoft.org/article/view/v005i08 [web page] // @(require_results) -exp_float64 :: proc(r: ^Rand = nil) -> f64 { +exp_float64 :: proc() -> f64 { re :: 7.69711747013104972 @(static, rodata) @@ -199,16 +199,16 @@ exp_float64 :: proc(r: ^Rand = nil) -> f64 { } for { - j := uint32(r) + j := uint32() i := j & 0xFF x := f64(j) * f64(we[i]) if j < ke[i] { return x } if i == 0 { - return re - math.ln(float64(r)) + return re - math.ln(float64()) } - if fe[i]+f32(float64(r))*(fe[i-1]-fe[i]) < f32(math.exp(-x)) { + if fe[i]+f32(float64())*(fe[i-1]-fe[i]) < f32(math.exp(-x)) { return x } } diff --git a/core/math/rand/normal.odin b/core/math/rand/normal.odin index c8681db80..eefa013df 100644 --- a/core/math/rand/normal.odin +++ b/core/math/rand/normal.odin @@ -18,7 +18,7 @@ import "core:math" // https://www.jstatsoft.org/article/view/v005i08 [web page] // @(require_results) -norm_float64 :: proc(r: ^Rand = nil) -> f64 { +norm_float64 :: proc() -> f64 { rn :: 3.442619855899 @(static, rodata) @@ -115,15 +115,8 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 { 0.008624485, 0.005548995, 0.0026696292, } - r := r - if r == nil { - // NOTE(bill, 2020-09-07): Do this so that people can - // enforce the global random state if necessary with `nil` - r = &global_rand - } - for { - j := i32(uint32(r)) + j := i32(uint32()) i := j & 0x7f x := f64(j) * f64(wn[i]) if u32(abs(j)) < kn[i] { @@ -133,15 +126,15 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 { if i == 0 { for { - x = -math.ln(float64(r)) * (1.0/ rn) - y := -math.ln(float64(r)) + x = -math.ln(float64()) * (1.0/ rn) + y := -math.ln(float64()) if y+y >= x*x { break } } return j > 0 ? rn + x : -rn - x } - if fn[i]+f32(float64(r))*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) { + if fn[i]+f32(float64())*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) { return x } } diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index 664d6abc9..8e3c0264d 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -5,22 +5,22 @@ Package core:math/rand implements various random number generators package rand import "base:intrinsics" -import "core:crypto" +import "base:runtime" import "core:math" import "core:mem" -Rand :: struct { - state: u64, - inc: u64, - is_system: bool, +Default_Random_State :: runtime.Default_Random_State +default_random_generator :: runtime.default_random_generator + +create :: proc(seed: u64) -> (state: Default_Random_State) { + seed := seed + runtime.default_random_generator(&state) + runtime.default_random_generator_proc(&state, .Reset, ([^]byte)(&seed)[:size_of(seed)]) + return } - -@(private) -global_rand := create(u64(intrinsics.read_cycle_counter())) - /* -Sets the seed used by the global random number generator. +Reset the seed used by the context.random_generator. Inputs: - seed: The seed value @@ -37,139 +37,46 @@ Example: Possible Output: 10 - */ +@(deprecated="Prefer `rand.reset`") set_global_seed :: proc(seed: u64) { - init(&global_rand, seed) + runtime.random_generator_reset_u64(context.random_generator, seed) } /* -Creates a new random number generator. +Reset the seed used by the context.random_generator. Inputs: -- seed: The seed value to create the random number generator with - -Returns: -- res: The created random number generator +- seed: The seed value Example: import "core:math/rand" import "core:fmt" - create_example :: proc() { - my_rand := rand.create(1) - fmt.println(rand.uint64(&my_rand)) + set_global_seed_example :: proc() { + rand.set_global_seed(1) + fmt.println(rand.uint64()) } Possible Output: 10 - */ -@(require_results) -create :: proc(seed: u64) -> (res: Rand) { - r: Rand - init(&r, seed) - return r +reset :: proc(seed: u64) { + runtime.random_generator_reset_u64(context.random_generator, seed) } -/* -Initialises a random number generator. - -Inputs: -- r: The random number generator to initialise -- seed: The seed value to initialise this random number generator - -Example: - import "core:math/rand" - import "core:fmt" - - init_example :: proc() { - my_rand: rand.Rand - rand.init(&my_rand, 1) - fmt.println(rand.uint64(&my_rand)) - } - -Possible Output: - - 10 - -*/ -init :: proc(r: ^Rand, seed: u64) { - r.state = 0 - r.inc = (seed << 1) | 1 - _random_u64(r) - r.state += seed - _random_u64(r) -} - -/* -Initialises a random number generator to use the system random number generator. -The system random number generator is platform specific, and not supported -on all targets. - -Inputs: -- r: The random number generator to use the system random number generator - -WARNING: Panics if the system random number generator is not supported. -Support can be determined via the `core:crypto.HAS_RAND_BYTES` constant. - -Example: - import "core:crypto" - import "core:math/rand" - import "core:fmt" - - init_as_system_example :: proc() { - my_rand: rand.Rand - switch crypto.HAS_RAND_BYTES { - case true: - rand.init_as_system(&my_rand) - fmt.println(rand.uint64(&my_rand)) - case false: - fmt.println("system random not supported!") - } - } - -Possible Output: - - 10 - -*/ -init_as_system :: proc(r: ^Rand) { - if !crypto.HAS_RAND_BYTES { - panic(#procedure + " is not supported on this platform yet") - } - r.state = 0 - r.inc = 0 - r.is_system = true -} - @(private) -_random_u64 :: proc(r: ^Rand) -> u64 { - r := r - switch { - case r == nil: - r = &global_rand - case r.is_system: - value: u64 - crypto.rand_bytes((cast([^]u8)&value)[:size_of(u64)]) - return value - } - - old_state := r.state - r.state = old_state * 6364136223846793005 + (r.inc|1) - xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081 - rot := (old_state >> 59) - return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63)) +_random_u64 :: proc() -> (res: u64) { + ok := runtime.random_generator_read_ptr(context.random_generator, &res, size_of(res)) + assert(ok, "uninitialized context.random_generator") + return } /* Generates a random 32 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random unsigned 32 bit value @@ -178,11 +85,7 @@ Example: import "core:fmt" uint32_example :: proc() { - // Using the global random number generator fmt.println(rand.uint32()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.uint32(&my_rand)) } Possible Output: @@ -192,14 +95,11 @@ Possible Output: */ @(require_results) -uint32 :: proc(r: ^Rand = nil) -> (val: u32) { return u32(_random_u64(r)) } +uint32 :: proc() -> (val: u32) { return u32(_random_u64()) } /* Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random unsigned 64 bit value @@ -208,11 +108,7 @@ Example: import "core:fmt" uint64_example :: proc() { - // Using the global random number generator fmt.println(rand.uint64()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.uint64(&my_rand)) } Possible Output: @@ -222,14 +118,11 @@ Possible Output: */ @(require_results) -uint64 :: proc(r: ^Rand = nil) -> (val: u64) { return _random_u64(r) } +uint64 :: proc() -> (val: u64) { return _random_u64() } /* Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random unsigned 128 bit value @@ -238,11 +131,7 @@ Example: import "core:fmt" uint128_example :: proc() { - // Using the global random number generator fmt.println(rand.uint128()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.uint128(&my_rand)) } Possible Output: @@ -252,9 +141,9 @@ Possible Output: */ @(require_results) -uint128 :: proc(r: ^Rand = nil) -> (val: u128) { - a := u128(_random_u64(r)) - b := u128(_random_u64(r)) +uint128 :: proc() -> (val: u128) { + a := u128(_random_u64()) + b := u128(_random_u64()) return (a<<64) | b } @@ -262,9 +151,6 @@ uint128 :: proc(r: ^Rand = nil) -> (val: u128) { Generates a random 31 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. The sign bit will always be set to 0, thus all generated numbers will be positive. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random 31 bit value @@ -273,11 +159,7 @@ Example: import "core:fmt" int31_example :: proc() { - // Using the global random number generator fmt.println(rand.int31()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int31(&my_rand)) } Possible Output: @@ -286,15 +168,12 @@ Possible Output: 389 */ -@(require_results) int31 :: proc(r: ^Rand = nil) -> (val: i32) { return i32(uint32(r) << 1 >> 1) } +@(require_results) int31 :: proc() -> (val: i32) { return i32(uint32() << 1 >> 1) } /* Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. The sign bit will always be set to 0, thus all generated numbers will be positive. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random 63 bit value @@ -303,11 +182,7 @@ Example: import "core:fmt" int63_example :: proc() { - // Using the global random number generator fmt.println(rand.int63()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int63(&my_rand)) } Possible Output: @@ -316,15 +191,12 @@ Possible Output: 389 */ -@(require_results) int63 :: proc(r: ^Rand = nil) -> (val: i64) { return i64(uint64(r) << 1 >> 1) } +@(require_results) int63 :: proc() -> (val: i64) { return i64(uint64() << 1 >> 1) } /* Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. The sign bit will always be set to 0, thus all generated numbers will be positive. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random 127 bit value @@ -333,11 +205,7 @@ Example: import "core:fmt" int127_example :: proc() { - // Using the global random number generator fmt.println(rand.int127()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int127(&my_rand)) } Possible Output: @@ -346,14 +214,13 @@ Possible Output: 389 */ -@(require_results) int127 :: proc(r: ^Rand = nil) -> (val: i128) { return i128(uint128(r) << 1 >> 1) } +@(require_results) int127 :: proc() -> (val: i128) { return i128(uint128() << 1 >> 1) } /* Generates a random 31 bit value in the range `[0, n)` using the provided random number generator. If no generator is provided the global random number generator will be used. Inputs: - n: The upper bound of the generated number, this value is exclusive -- r: The random number generator to use, or nil for the global generator Returns: - val: A random 31 bit value in the range `[0, n)` @@ -365,11 +232,7 @@ Example: import "core:fmt" int31_max_example :: proc() { - // Using the global random number generator fmt.println(rand.int31_max(16)) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int31_max(1024, &my_rand)) } Possible Output: @@ -379,17 +242,17 @@ Possible Output: */ @(require_results) -int31_max :: proc(n: i32, r: ^Rand = nil) -> (val: i32) { +int31_max :: proc(n: i32) -> (val: i32) { if n <= 0 { panic("Invalid argument to int31_max") } if n&(n-1) == 0 { - return int31(r) & (n-1) + return int31() & (n-1) } max := i32((1<<31) - 1 - (1<<31)%u32(n)) - v := int31(r) + v := int31() for v > max { - v = int31(r) + v = int31() } return v % n } @@ -399,7 +262,6 @@ Generates a random 63 bit value in the range `[0, n)` using the provided random Inputs: - n: The upper bound of the generated number, this value is exclusive -- r: The random number generator to use, or nil for the global generator Returns: - val: A random 63 bit value in the range `[0, n)` @@ -411,11 +273,7 @@ Example: import "core:fmt" int63_max_example :: proc() { - // Using the global random number generator fmt.println(rand.int63_max(16)) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int63_max(1024, &my_rand)) } Possible Output: @@ -425,17 +283,17 @@ Possible Output: */ @(require_results) -int63_max :: proc(n: i64, r: ^Rand = nil) -> (val: i64) { +int63_max :: proc(n: i64) -> (val: i64) { if n <= 0 { panic("Invalid argument to int63_max") } if n&(n-1) == 0 { - return int63(r) & (n-1) + return int63() & (n-1) } max := i64((1<<63) - 1 - (1<<63)%u64(n)) - v := int63(r) + v := int63() for v > max { - v = int63(r) + v = int63() } return v % n } @@ -445,7 +303,6 @@ Generates a random 127 bit value in the range `[0, n)` using the provided random Inputs: - n: The upper bound of the generated number, this value is exclusive -- r: The random number generator to use, or nil for the global generator Returns: - val: A random 127 bit value in the range `[0, n)` @@ -457,11 +314,7 @@ Example: import "core:fmt" int127_max_example :: proc() { - // Using the global random number generator fmt.println(rand.int127_max(16)) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int127_max(1024, &my_rand)) } Possible Output: @@ -471,17 +324,17 @@ Possible Output: */ @(require_results) -int127_max :: proc(n: i128, r: ^Rand = nil) -> (val: i128) { +int127_max :: proc(n: i128) -> (val: i128) { if n <= 0 { panic("Invalid argument to int127_max") } if n&(n-1) == 0 { - return int127(r) & (n-1) + return int127() & (n-1) } max := i128((1<<127) - 1 - (1<<127)%u128(n)) - v := int127(r) + v := int127() for v > max { - v = int127(r) + v = int127() } return v % n } @@ -491,7 +344,6 @@ Generates a random integer value in the range `[0, n)` using the provided random Inputs: - n: The upper bound of the generated number, this value is exclusive -- r: The random number generator to use, or nil for the global generator Returns: - val: A random integer value in the range `[0, n)` @@ -503,11 +355,7 @@ Example: import "core:fmt" int_max_example :: proc() { - // Using the global random number generator fmt.println(rand.int_max(16)) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.int_max(1024, &my_rand)) } Possible Output: @@ -517,23 +365,20 @@ Possible Output: */ @(require_results) -int_max :: proc(n: int, r: ^Rand = nil) -> (val: int) { +int_max :: proc(n: int) -> (val: int) { if n <= 0 { panic("Invalid argument to int_max") } when size_of(int) == 4 { - return int(int31_max(i32(n), r)) + return int(int31_max(i32(n))) } else { - return int(int63_max(i64(n), r)) + return int(int63_max(i64(n))) } } /* Generates a random double floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random double floating point value in the range `[0, 1)` @@ -542,11 +387,7 @@ Example: import "core:fmt" float64_example :: proc() { - // Using the global random number generator fmt.println(rand.float64()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.float64(&my_rand)) } Possible Output: @@ -555,14 +396,11 @@ Possible Output: 0.511 */ -@(require_results) float64 :: proc(r: ^Rand = nil) -> (val: f64) { return f64(int63_max(1<<53, r)) / (1 << 53) } +@(require_results) float64 :: proc() -> (val: f64) { return f64(int63_max(1<<53)) / (1 << 53) } /* Generates a random single floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used. -Inputs: -- r: The random number generator to use, or nil for the global generator - Returns: - val: A random single floating point value in the range `[0, 1)` @@ -571,11 +409,7 @@ Example: import "core:fmt" float32_example :: proc() { - // Using the global random number generator fmt.println(rand.float32()) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.float32(&my_rand)) } Possible Output: @@ -584,7 +418,7 @@ Possible Output: 0.511 */ -@(require_results) float32 :: proc(r: ^Rand = nil) -> (val: f32) { return f32(int31_max(1<<24, r)) / (1 << 24) } +@(require_results) float32 :: proc() -> (val: f32) { return f32(int31_max(1<<24)) / (1 << 24) } /* Generates a random double floating point value in the range `[low, high)` using the provided random number generator. If no generator is provided the global random number generator will be used. @@ -594,7 +428,6 @@ WARNING: Panics if `high < low` Inputs: - low: The lower bounds of the value, this value is inclusive - high: The upper bounds of the value, this value is exclusive -- r: The random number generator to use, or nil for the global generator Returns: - val: A random double floating point value in the range [low, high) @@ -604,11 +437,7 @@ Example: import "core:fmt" float64_range_example :: proc() { - // Using the global random number generator fmt.println(rand.float64_range(-10, 300)) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.float64_range(600, 900, &my_rand)) } Possible Output: @@ -617,9 +446,9 @@ Possible Output: 673.130 */ -@(require_results) float64_range :: proc(low, high: f64, r: ^Rand = nil) -> (val: f64) { +@(require_results) float64_range :: proc(low, high: f64) -> (val: f64) { assert(low <= high, "low must be lower than or equal to high") - val = (high-low)*float64(r) + low + val = (high-low)*float64() + low if val >= high { val = max(low, high * (1 - math.F64_EPSILON)) } @@ -632,7 +461,6 @@ Generates a random single floating point value in the range `[low, high)` using Inputs: - low: The lower bounds of the value, this value is inclusive - high: The upper bounds of the value, this value is exclusive -- r: The random number generator to use, or nil for the global generator Returns: - val: A random single floating point value in the range [low, high) @@ -644,11 +472,7 @@ Example: import "core:fmt" float32_range_example :: proc() { - // Using the global random number generator fmt.println(rand.float32_range(-10, 300)) - // Using local random number generator - my_rand := rand.create(1) - fmt.println(rand.float32_range(600, 900, &my_rand)) } Possible Output: @@ -657,9 +481,9 @@ Possible Output: 673.130 */ -@(require_results) float32_range :: proc(low, high: f32, r: ^Rand = nil) -> (val: f32) { +@(require_results) float32_range :: proc(low, high: f32) -> (val: f32) { assert(low <= high, "low must be lower than or equal to high") - val = (high-low)*float32(r) + low + val = (high-low)*float32() + low if val >= high { val = max(low, high * (1 - math.F32_EPSILON)) } @@ -672,7 +496,6 @@ Due to floating point precision there is no guarantee if the upper and lower bou Inputs: - p: The byte slice to fill -- r: The random number generator to use, or nil for the global generator Returns: - n: The number of bytes generated @@ -682,7 +505,6 @@ Example: import "core:fmt" read_example :: proc() { - // Using the global random number generator data: [8]byte n := rand.read(data[:]) fmt.println(n) @@ -696,12 +518,12 @@ Possible Output: */ @(require_results) -read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) { +read :: proc(p: []byte) -> (n: int) { pos := i8(0) val := i64(0) for n = 0; n < len(p); n += 1 { if pos == 0 { - val = int63(r) + val = int63() pos = 7 } p[n] = byte(val) @@ -718,7 +540,6 @@ Creates a slice of `int` filled with random values using the provided random num Inputs: - n: The size of the created slice -- r: The random number generator to use, or nil for the global generator - allocator: (default: context.allocator) Returns: @@ -731,16 +552,10 @@ Example: import "core:fmt" perm_example :: proc() -> (err: mem.Allocator_Error) { - // Using the global random number generator and using the context allocator data := rand.perm(4) or_return fmt.println(data) defer delete(data, context.allocator) - // Using local random number generator and temp allocator - my_rand := rand.create(1) - data_tmp := rand.perm(4, &my_rand, context.temp_allocator) or_return - fmt.println(data_tmp) - return } @@ -751,10 +566,10 @@ Possible Output: */ @(require_results) -perm :: proc(n: int, r: ^Rand = nil, allocator := context.allocator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error { +perm :: proc(n: int, allocator := context.allocator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error { m := make([]int, n, allocator) or_return for i := 0; i < n; i += 1 { - j := int_max(i+1, r) + j := int_max(i+1) m[i] = m[j] m[j] = i } @@ -766,14 +581,12 @@ Randomizes the ordering of elements for the provided slice. If no generator is p Inputs: - array: The slice to randomize -- r: The random number generator to use, or nil for the global generator Example: import "core:math/rand" import "core:fmt" shuffle_example :: proc() { - // Using the global random number generator data: [4]int = { 1, 2, 3, 4 } fmt.println(data) // the contents are in order rand.shuffle(data[:]) @@ -786,14 +599,14 @@ Possible Output: [2, 4, 3, 1] */ -shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) { +shuffle :: proc(array: $T/[]$E) { n := i64(len(array)) if n < 2 { return } for i := i64(n - 1); i > 0; i -= 1 { - j := int63_max(i + 1, r) + j := int63_max(i + 1) array[i], array[j] = array[j], array[i] } } @@ -803,7 +616,6 @@ Returns a random element from the provided slice. If no generator is provided th Inputs: - array: The slice to choose an element from -- r: The random number generator to use, or nil for the global generator Returns: - res: A random element from `array` @@ -813,7 +625,6 @@ Example: import "core:fmt" choice_example :: proc() { - // Using the global random number generator data: [4]int = { 1, 2, 3, 4 } fmt.println(rand.choice(data[:])) fmt.println(rand.choice(data[:])) @@ -830,17 +641,17 @@ Possible Output: */ @(require_results) -choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) { +choice :: proc(array: $T/[]$E) -> (res: E) { n := i64(len(array)) if n < 1 { return E{} } - return array[int63_max(n, r)] + return array[int63_max(n)] } @(require_results) -choice_enum :: proc($T: typeid, r: ^Rand = nil) -> T +choice_enum :: proc($T: typeid) -> T where intrinsics.type_is_enum(T), size_of(T) <= 8, @@ -848,11 +659,11 @@ choice_enum :: proc($T: typeid, r: ^Rand = nil) -> T { when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) && u64(max(T)) > u64(max(i64)) { - i := uint64(r) % u64(len(T)) + i := uint64() % u64(len(T)) i += u64(min(T)) return T(i) } else { - i := int63_max(i64(len(T)), r) + i := int63_max(i64(len(T))) i += i64(min(T)) return T(i) } diff --git a/core/slice/slice.odin b/core/slice/slice.odin index 3cab9189d..d8f4df88e 100644 --- a/core/slice/slice.odin +++ b/core/slice/slice.odin @@ -180,7 +180,7 @@ binary_search_by :: proc(array: $A/[]$T, key: T, f: proc(T, T) -> Ordering) -> ( } @(require_results) -equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_comparable(E) { +equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_comparable(E) #no_bounds_check { if len(a) != len(b) { return false } @@ -736,4 +736,4 @@ bitset_to_enum_slice_with_make :: proc(bs: $T, $E: typeid, allocator := context. return bitset_to_enum_slice(buf, bs) } -bitset_to_enum_slice :: proc{bitset_to_enum_slice_with_make, bitset_to_enum_slice_with_buffer} \ No newline at end of file +bitset_to_enum_slice :: proc{bitset_to_enum_slice_with_make, bitset_to_enum_slice_with_buffer} diff --git a/core/sync/chan/chan.odin b/core/sync/chan/chan.odin index f4774e4f8..0c3f4a725 100644 --- a/core/sync/chan/chan.odin +++ b/core/sync/chan/chan.odin @@ -476,10 +476,7 @@ select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: [] return } - r: ^rand.Rand = nil - - - select_idx = rand.int_max(count, r) if count > 0 else 0 + select_idx = rand.int_max(count) if count > 0 else 0 sel := candidates[select_idx] if sel.is_recv { diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin old mode 100644 new mode 100755 index 16b6fa244..0d9ad826a --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -64,6 +64,7 @@ foreign kernel32 { RemoveVectoredContinueHandler :: proc(Handle: LPVOID) -> DWORD --- RaiseException :: proc(dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! --- + SetUnhandledExceptionFilter :: proc(lpTopLevelExceptionFilter: LPTOP_LEVEL_EXCEPTION_FILTER) -> LPTOP_LEVEL_EXCEPTION_FILTER --- CreateHardLinkW :: proc(lpSymlinkFileName: LPCWSTR, lpTargetFileName: LPCWSTR, @@ -464,6 +465,8 @@ foreign kernel32 { GetHandleInformation :: proc(hObject: HANDLE, lpdwFlags: ^DWORD) -> BOOL --- RtlCaptureStackBackTrace :: proc(FramesToSkip: ULONG, FramesToCapture: ULONG, BackTrace: [^]PVOID, BackTraceHash: PULONG) -> USHORT --- + + GetSystemPowerStatus :: proc(lpSystemPowerStatus: ^SYSTEM_POWER_STATUS) -> BOOL --- } DEBUG_PROCESS :: 0x00000001 @@ -1009,31 +1012,9 @@ foreign kernel32 { HandlerRoutine :: proc "system" (dwCtrlType: DWORD) -> BOOL PHANDLER_ROUTINE :: HandlerRoutine +// NOTE(Jeroen, 2024-06-13): As Odin now supports bit_fields, we no longer need +// a helper procedure. `init_dcb_with_config` and `get_dcb_config` have been removed. - - -DCB_Config :: struct { - fParity: bool, - fOutxCtsFlow: bool, - fOutxDsrFlow: bool, - fDtrControl: DTR_Control, - fDsrSensitivity: bool, - fTXContinueOnXoff: bool, - fOutX: bool, - fInX: bool, - fErrorChar: bool, - fNull: bool, - fRtsControl: RTS_Control, - fAbortOnError: bool, - BaudRate: DWORD, - ByteSize: BYTE, - Parity: Parity, - StopBits: Stop_Bits, - XonChar: byte, - XoffChar: byte, - ErrorChar: byte, - EvtChar: byte, -} DTR_Control :: enum byte { Disable = 0, Enable = 1, @@ -1058,92 +1039,35 @@ Stop_Bits :: enum byte { Two = 2, } -// A helper procedure to set the values of a DCB structure. -init_dcb_with_config :: proc "contextless" (dcb: ^DCB, config: DCB_Config) { - out: u32 - - // NOTE(tetra, 2022-09-21): On both Clang 14 on Windows, and MSVC, the bits in the bitfield - // appear to be defined from LSB to MSB order. - // i.e: `fBinary` (the first bitfield in the C source) is the LSB in the `settings` u32. - - out |= u32(1) << 0 // fBinary must always be true on Windows. - - out |= u32(config.fParity) << 1 - out |= u32(config.fOutxCtsFlow) << 2 - out |= u32(config.fOutxDsrFlow) << 3 - - out |= u32(config.fDtrControl) << 4 - - out |= u32(config.fDsrSensitivity) << 6 - out |= u32(config.fTXContinueOnXoff) << 7 - out |= u32(config.fOutX) << 8 - out |= u32(config.fInX) << 9 - out |= u32(config.fErrorChar) << 10 - out |= u32(config.fNull) << 11 - - out |= u32(config.fRtsControl) << 12 - - out |= u32(config.fAbortOnError) << 14 - - dcb.settings = out - - dcb.BaudRate = config.BaudRate - dcb.ByteSize = config.ByteSize - dcb.Parity = config.Parity - dcb.StopBits = config.StopBits - dcb.XonChar = config.XonChar - dcb.XoffChar = config.XoffChar - dcb.ErrorChar = config.ErrorChar - dcb.EvtChar = config.EvtChar - - dcb.DCBlength = size_of(DCB) -} -get_dcb_config :: proc "contextless" (dcb: DCB) -> (config: DCB_Config) { - config.fParity = bool((dcb.settings >> 1) & 0x01) - config.fOutxCtsFlow = bool((dcb.settings >> 2) & 0x01) - config.fOutxDsrFlow = bool((dcb.settings >> 3) & 0x01) - - config.fDtrControl = DTR_Control((dcb.settings >> 4) & 0x02) - - config.fDsrSensitivity = bool((dcb.settings >> 6) & 0x01) - config.fTXContinueOnXoff = bool((dcb.settings >> 7) & 0x01) - config.fOutX = bool((dcb.settings >> 8) & 0x01) - config.fInX = bool((dcb.settings >> 9) & 0x01) - config.fErrorChar = bool((dcb.settings >> 10) & 0x01) - config.fNull = bool((dcb.settings >> 11) & 0x01) - - config.fRtsControl = RTS_Control((dcb.settings >> 12) & 0x02) - - config.fAbortOnError = bool((dcb.settings >> 14) & 0x01) - - config.BaudRate = dcb.BaudRate - config.ByteSize = dcb.ByteSize - config.Parity = dcb.Parity - config.StopBits = dcb.StopBits - config.XonChar = dcb.XonChar - config.XoffChar = dcb.XoffChar - config.ErrorChar = dcb.ErrorChar - config.EvtChar = dcb.EvtChar - - return -} - -// NOTE(tetra): See get_dcb_config() and init_dcb_with_config() for help with initializing this. DCB :: struct { - DCBlength: DWORD, // NOTE(tetra): Must be set to size_of(DCB). - BaudRate: DWORD, - settings: u32, // NOTE(tetra): These are bitfields in the C struct. - wReserved: WORD, - XOnLim: WORD, - XOffLim: WORD, - ByteSize: BYTE, - Parity: Parity, - StopBits: Stop_Bits, - XonChar: byte, - XoffChar: byte, - ErrorChar: byte, - EofChar: byte, - EvtChar: byte, + DCBlength: DWORD, + BaudRate: DWORD, + using _: bit_field DWORD { + fBinary: bool | 1, + fParity: bool | 1, + fOutxCtsFlow: bool | 1, + fOutxDsrFlow: bool | 1, + fDtrControl: DTR_Control | 2, + fDsrSensitivity: bool | 1, + fTXContinueOnXoff: bool | 1, + fOutX: bool | 1, + fInX: bool | 1, + fErrorChar: bool | 1, + fNull: bool | 1, + fRtsControl: RTS_Control | 2, + fAbortOnError: bool | 1, + }, + wReserved: WORD, + XOnLim: WORD, + XOffLim: WORD, + ByteSize: BYTE, + Parity: Parity, + StopBits: Stop_Bits, + XonChar: byte, + XoffChar: byte, + ErrorChar: byte, + EofChar: byte, + EvtChar: byte, wReserved1: WORD, } @@ -1153,6 +1077,19 @@ foreign kernel32 { SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL --- } +COMMTIMEOUTS :: struct { + ReadIntervalTimeout: DWORD, + ReadTotalTimeoutMultiplier: DWORD, + ReadTotalTimeoutConstant: DWORD, + WriteTotalTimeoutMultiplier: DWORD, + WriteTotalTimeoutConstant: DWORD, +} + +@(default_calling_convention="system") +foreign kernel32 { + GetCommTimeouts :: proc(handle: HANDLE, timeouts: ^COMMTIMEOUTS) -> BOOL --- + SetCommTimeouts :: proc(handle: HANDLE, timeouts: ^COMMTIMEOUTS) -> BOOL --- +} LPFIBER_START_ROUTINE :: #type proc "system" (lpFiberParameter: LPVOID) @@ -1210,6 +1147,30 @@ SYSTEM_LOGICAL_PROCESSOR_INFORMATION :: struct { DummyUnion: DUMMYUNIONNAME_u, } +SYSTEM_POWER_STATUS :: struct { + ACLineStatus: AC_Line_Status, + BatteryFlag: Battery_Flags, + BatteryLifePercent: BYTE, + SystemStatusFlag: BYTE, + BatteryLifeTime: DWORD, + BatteryFullLifeTime: DWORD, +} + +AC_Line_Status :: enum BYTE { + Offline = 0, + Online = 1, + Unknown = 255, +} + +Battery_Flag :: enum BYTE { + High = 0, + Low = 1, + Critical = 2, + Charging = 3, + No_Battery = 7, +} +Battery_Flags :: bit_set[Battery_Flag; BYTE] + /* Global Memory Flags */ GMEM_FIXED :: 0x0000 GMEM_MOVEABLE :: 0x0002 @@ -1228,3 +1189,5 @@ GMEM_INVALID_HANDLE :: 0x8000 GHND :: (GMEM_MOVEABLE | GMEM_ZEROINIT) GPTR :: (GMEM_FIXED | GMEM_ZEROINIT) + +LPTOP_LEVEL_EXCEPTION_FILTER :: PVECTORED_EXCEPTION_HANDLER diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 11d2774d6..7ed322169 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -34,6 +34,7 @@ HGDIOBJ :: distinct HANDLE HBITMAP :: distinct HANDLE HGLOBAL :: distinct HANDLE HHOOK :: distinct HANDLE +HWINEVENTHOOK :: distinct HANDLE HKEY :: distinct HANDLE HDESK :: distinct HANDLE HFONT :: distinct HANDLE @@ -703,6 +704,14 @@ WNDPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM) -> LRESULT HOOKPROC :: #type proc "system" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT +WINEVENTPROC :: #type proc "system" ( + hWinEventHook: HWINEVENTHOOK, + event: DWORD, + hwnd: HWND, + idObject, idChild: LONG, + idEventThread, dwmsEventTime: DWORD, +) + CWPRETSTRUCT :: struct { lResult: LRESULT, lParam: LPARAM, diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin index 1fc0116f5..3bc083acb 100644 --- a/core/sys/windows/user32.odin +++ b/core/sys/windows/user32.odin @@ -17,6 +17,18 @@ foreign user32 { GetClassNameW :: proc(hWnd: HWND, lpClassName: LPWSTR, nMaxCount: c_int) -> c_int --- + GetParent :: proc(hWnd: HWND) -> HWND --- + IsWindowVisible :: proc(hWnd: HWND) -> BOOL --- + SetWinEventHook :: proc( + eventMin, eventMax: DWORD, + hmodWinEventProc: HMODULE, + pfnWinEvenProc: WINEVENTPROC, + idProcess, idThread: DWORD, + dwFlags: WinEventFlags, + ) -> HWINEVENTHOOK --- + + IsChild :: proc(hWndParent, hWnd: HWND) -> BOOL --- + RegisterClassW :: proc(lpWndClass: ^WNDCLASSW) -> ATOM --- RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM --- UnregisterClassW :: proc(lpClassName: LPCWSTR, hInstance: HINSTANCE) -> BOOL --- @@ -568,3 +580,12 @@ RedrawWindowFlags :: enum UINT { RDW_FRAME = 0x0400, RDW_NOFRAME = 0x0800, } + +// OUTOFCONTEXT is the zero value, use {} +WinEventFlags :: bit_set[WinEventFlag; DWORD] + +WinEventFlag :: enum DWORD { + SKIPOWNTHREAD = 0, + SKIPOWNPROCESS = 1, + INCONTEXT = 2, +} diff --git a/core/sys/windows/winerror.odin b/core/sys/windows/winerror.odin index 118327ffa..c66a22322 100644 --- a/core/sys/windows/winerror.odin +++ b/core/sys/windows/winerror.odin @@ -47,7 +47,7 @@ ERROR_PIPE_BUSY : DWORD : 231 E_NOTIMPL :: HRESULT(-0x7fff_bfff) // 0x8000_4001 -SUCCEEDED :: #force_inline proc(#any_int result: int) -> bool { return result >= 0 } +SUCCEEDED :: #force_inline proc "contextless" (#any_int result: int) -> bool { return result >= 0 } System_Error :: enum DWORD { diff --git a/core/testing/logging.odin b/core/testing/logging.odin index 5bbbffeae..f1e75d33c 100644 --- a/core/testing/logging.odin +++ b/core/testing/logging.odin @@ -8,13 +8,22 @@ import "core:strings" import "core:sync/chan" import "core:time" -Default_Test_Logger_Opts :: runtime.Logger_Options { - .Level, - .Terminal_Color, - .Short_File_Path, - .Line, - .Procedure, - .Date, .Time, +when USING_SHORT_LOGS { + Default_Test_Logger_Opts :: runtime.Logger_Options { + .Level, + .Terminal_Color, + .Short_File_Path, + .Line, + } +} else { + Default_Test_Logger_Opts :: runtime.Logger_Options { + .Level, + .Terminal_Color, + .Short_File_Path, + .Line, + .Procedure, + .Date, .Time, + } } Log_Message :: struct { diff --git a/core/testing/runner.odin b/core/testing/runner.odin index 328186c35..a0f9eee31 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -41,6 +41,8 @@ PROGRESS_WIDTH : int : #config(ODIN_TEST_PROGRESS_WIDTH, 24) SHARED_RANDOM_SEED : u64 : #config(ODIN_TEST_RANDOM_SEED, 0) // Set the lowest log level for this test run. LOG_LEVEL : string : #config(ODIN_TEST_LOG_LEVEL, "info") +// Show only the most necessary logging information. +USING_SHORT_LOGS : bool : #config(ODIN_TEST_SHORT_LOGS, false) get_log_level :: #force_inline proc() -> runtime.Logger_Level { @@ -497,6 +499,7 @@ runner :: proc(internal_tests: []Internal_Test) -> bool { data.it = it data.t.seed = shared_random_seed data.t.error_count = 0 + data.t._fail_now_called = false thread.pool_add_task(&pool, task.allocator, run_test_task, data, run_index) } @@ -604,10 +607,10 @@ runner :: proc(internal_tests: []Internal_Test) -> bool { }) fmt.assertf(alloc_error == nil, "Error appending to log messages: %v", alloc_error) - find_task_data: for &data in task_data_slots { + find_task_data_for_timeout: for &data in task_data_slots { if data.it.pkg == it.pkg && data.it.name == it.name { end_t(&data.t) - break find_task_data + break find_task_data_for_timeout } } } @@ -645,21 +648,36 @@ runner :: proc(internal_tests: []Internal_Test) -> bool { "A signal (%v) was raised to stop test #%i %s.%s, but it was unable to be found.", reason, test_index, it.pkg, it.name) - if test_index not_in failed_test_reason_map { - // We only write a new error message here if there wasn't one - // already, because the message we can provide based only on - // the signal won't be very useful, whereas asserts and panics - // will provide a user-written error message. - failed_test_reason_map[test_index] = fmt.aprintf("Signal caught: %v", reason, allocator = shared_log_allocator) - pkg_log.fatalf("Caught signal to stop test #%i %s.%s for: %v.", test_index, it.pkg, it.name, reason) - + // The order this is handled in is a little particular. + task_data: ^Task_Data + find_task_data_for_stop_signal: for &data in task_data_slots { + if data.it.pkg == it.pkg && data.it.name == it.name { + task_data = &data + break find_task_data_for_stop_signal + } } - when FANCY_OUTPUT { - bypass_progress_overwrite = true - signals_were_raised = true + fmt.assertf(task_data != nil, "A signal (%v) was raised to stop test #%i %s.%s, but its task data is missing.", + reason, test_index, it.pkg, it.name) + + if !task_data.t._fail_now_called { + if test_index not_in failed_test_reason_map { + // We only write a new error message here if there wasn't one + // already, because the message we can provide based only on + // the signal won't be very useful, whereas asserts and panics + // will provide a user-written error message. + failed_test_reason_map[test_index] = fmt.aprintf("Signal caught: %v", reason, allocator = shared_log_allocator) + pkg_log.fatalf("Caught signal to stop test #%i %s.%s for: %v.", test_index, it.pkg, it.name, reason) + } + + when FANCY_OUTPUT { + bypass_progress_overwrite = true + signals_were_raised = true + } } + end_t(&task_data.t) + total_failure_count += 1 total_done_count += 1 } diff --git a/core/testing/testing.odin b/core/testing/testing.odin index 92b4d391d..29fe853ef 100644 --- a/core/testing/testing.odin +++ b/core/testing/testing.odin @@ -48,7 +48,7 @@ T :: struct { // tests during channel transmission. _log_allocator: runtime.Allocator, - _fail_now: proc() -> !, + _fail_now_called: bool, } @@ -66,15 +66,20 @@ fail :: proc(t: ^T, loc := #caller_location) { pkg_log.error("FAIL", location=loc) } -fail_now :: proc(t: ^T, msg := "", loc := #caller_location) { +// fail_now will cause a test to immediately fail and abort, much in the same +// way a failed assertion or panic call will stop a thread. +// +// It is for when you absolutely need a test to fail without calling any of its +// deferred statements. It will be cleaner than a regular assert or panic, +// as the test runner will know to expect the signal this procedure will raise. +fail_now :: proc(t: ^T, msg := "", loc := #caller_location) -> ! { + t._fail_now_called = true if msg != "" { pkg_log.error("FAIL:", msg, location=loc) } else { pkg_log.error("FAIL", location=loc) } - if t._fail_now != nil { - t._fail_now() - } + runtime.trap() } failed :: proc(t: ^T) -> bool { @@ -94,7 +99,17 @@ logf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) { // cleanup registers a procedure and user_data, which will be called when the test, and all its subtests, complete. // Cleanup procedures will be called in LIFO (last added, first called) order. -// Each procedure will use a copy of the context at the time of registering. +// +// Each procedure will use a copy of the context at the time of registering, +// and if the test failed due to a timeout, failed assertion, panic, bounds-checking error, +// memory access violation, or any other signal-based fault, this procedure will +// run with greater privilege in the test runner's main thread. +// +// That means that any cleanup procedure absolutely must not fail in the same way, +// or it will take down the entire test runner with it. This is for when you +// need something to run no matter what, if a test failed. +// +// For almost every usual case, `defer` should be preferable and sufficient. cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) { append(&t.cleanups, Internal_Cleanup{procedure, user_data, context}) } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index b6423d17a..49144f4b9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -2550,7 +2550,7 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast * error_line("\tSuggestion: Did you want to pass the iterable value to the for statement by pointer to get addressable semantics?\n"); } - if (is_type_map(parent_type)) { + if (parent_type != nullptr && is_type_map(parent_type)) { error_line("\t Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string)); } else { error_line("\t Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string)); @@ -3564,6 +3564,9 @@ gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand x->mode = Addressing_Value; if (are_types_identical(xt, yt)) { + if (are_types_identical(x->type, y->type)) { + return; + } if (!is_type_named(x->type) && is_type_named(y->type)) { // prefer the named type x->type = y->type; diff --git a/src/main.cpp b/src/main.cpp index 70def5802..f4cd40fe9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2934,7 +2934,8 @@ int main(int arg_count, char const **arg_ptr) { // TODO(jeroen): Remove the `init_filename` param. // Let's put that on `build_context.build_paths[0]` instead. if (parse_packages(parser, init_filename) != ParseFile_None) { - return 1; + GB_ASSERT_MSG(any_errors(), "parse_packages failed but no error was reported."); + // We depend on the next conditional block to return 1, after printing errors. } if (any_errors()) { diff --git a/tests/core/assets/XML/attribute-whitespace.xml b/tests/core/assets/XML/attribute-whitespace.xml new file mode 100644 index 000000000..6381225d5 --- /dev/null +++ b/tests/core/assets/XML/attribute-whitespace.xml @@ -0,0 +1,8 @@ + + +Barzle +<부끄러운:barzle> + Indeed! + \ No newline at end of file diff --git a/tests/core/container/test_core_avl.odin b/tests/core/container/test_core_avl.odin index 99dbba8b2..556f371af 100644 --- a/tests/core/container/test_core_avl.odin +++ b/tests/core/container/test_core_avl.odin @@ -20,15 +20,15 @@ test_avl :: proc(t: ^testing.T) { iter := avl.iterator(&tree, avl.Direction.Forward) testing.expect(t, avl.iterator_get(&iter) == nil, "empty/iterator: first node should be nil") - r: rand.Rand - rand.init(&r, t.seed) + r := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&r) // Test insertion. NR_INSERTS :: 32 + 1 // Ensure at least 1 collision. inserted_map := make(map[int]^avl.Node(int)) defer delete(inserted_map) for i := 0; i < NR_INSERTS; i += 1 { - v := int(rand.uint32(&r) & 0x1f) + v := int(rand.uint32() & 0x1f) existing_node, in_map := inserted_map[v] n, ok, _ := avl.find_or_insert(&tree, v) @@ -78,7 +78,7 @@ test_avl :: proc(t: ^testing.T) { testing.expect(t, visited == nrEntries, "iterator/backward: visited") // Test removal. - rand.shuffle(inserted_values[:], &r) + rand.shuffle(inserted_values[:]) for v, i in inserted_values { node := avl.find(&tree, v) testing.expect(t, node != nil, "remove: find (pre)") diff --git a/tests/core/container/test_core_rbtree.odin b/tests/core/container/test_core_rbtree.odin index 8def8edb6..425a9b440 100644 --- a/tests/core/container/test_core_rbtree.odin +++ b/tests/core/container/test_core_rbtree.odin @@ -14,8 +14,8 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) { defer mem.tracking_allocator_destroy(&track) context.allocator = mem.tracking_allocator(&track) - r: rand.Rand - rand.init(&r, t.seed) + r := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&r) log.infof("Testing Red-Black Tree($Key=%v,$Value=%v) using random seed %v.", type_info_of(Key), type_info_of(Value), t.seed) tree: rb.Tree(Key, Value) @@ -35,9 +35,9 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) { max_key := min(Key) for i := 0; i < NR_INSERTS; i += 1 { - k := Key(rand.uint32(&r)) & 0x1f + k := Key(rand.uint32()) & 0x1f min_key = min(min_key, k); max_key = max(max_key, k) - v := Value(rand.uint32(&r)) + v := Value(rand.uint32()) existing_node, in_map := inserted_map[k] n, inserted, _ := rb.find_or_insert(&tree, k, v) @@ -92,7 +92,7 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) { testing.expect(t, visited == entry_count, "iterator/backward: visited") // Test removal (and on_remove callback) - rand.shuffle(inserted_keys[:], &r) + rand.shuffle(inserted_keys[:]) callback_count := entry_count tree.user_data = &callback_count tree.on_remove = proc(key: Key, value: Value, user_data: rawptr) { diff --git a/tests/core/encoding/xml/test_core_xml.odin b/tests/core/encoding/xml/test_core_xml.odin index 22852d1f3..b29431e10 100644 --- a/tests/core/encoding/xml/test_core_xml.odin +++ b/tests/core/encoding/xml/test_core_xml.odin @@ -36,7 +36,7 @@ xml_test_utf8_normal :: proc(t: ^testing.T) { }, expected_doctype = "恥ずべきフクロウ", }, - crc32 = 0xe9b62f03, + crc32 = 0xefa55f27, }) } @@ -52,7 +52,7 @@ xml_test_utf8_unbox_cdata :: proc(t: ^testing.T) { }, expected_doctype = "恥ずべきフクロウ", }, - crc32 = 0x9c2643ed, + crc32 = 0x2dd27770, }) } @@ -128,7 +128,7 @@ xml_test_entities_unbox :: proc(t: ^testing.T) { }, expected_doctype = "html", }, - crc32 = 0x3b6d4a90, + crc32 = 0x350ca83e, }) } @@ -142,7 +142,21 @@ xml_test_entities_unbox_decode :: proc(t: ^testing.T) { }, expected_doctype = "html", }, - crc32 = 0x5be2ffdc, + crc32 = 0x7f58db7d, + }) +} + +@(test) +xml_test_attribute_whitespace :: proc(t: ^testing.T) { + run_test(t, { + // Same as above. + // Unbox CDATA in data tag. + filename = "XML/attribute-whitespace.xml", + options = { + flags = {}, + expected_doctype = "foozle", + }, + crc32 = 0x8f5fd6c1, }) } @@ -172,7 +186,7 @@ xml_test_unicode :: proc(t: ^testing.T) { expected_doctype = "", }, err = .None, - crc32 = 0x0b6100ab, + crc32 = 0x73070b55, }) } diff --git a/tests/core/fmt/test_core_fmt.odin b/tests/core/fmt/test_core_fmt.odin index 507e0f433..49142e24d 100644 --- a/tests/core/fmt/test_core_fmt.odin +++ b/tests/core/fmt/test_core_fmt.odin @@ -258,6 +258,120 @@ test_pointers :: proc(t: ^testing.T) { check(t, "0xFFFF", "%#4p", d) } +@(test) +test_odin_value_export :: proc(t: ^testing.T) { + E :: enum u32 { + A, B, C, + } + + F :: enum i16 { + A, B, F, + } + + S :: struct { + j, k: int, + } + + ST :: struct { + x: int `fmt:"-"`, + y: u8 `fmt:"r,0"`, + z: string `fmt:"s,0"`, + } + + U :: union { + i8, + i16, + } + + UEF :: union { E, F } + + A :: [2]int + + BSE :: distinct bit_set[E] + + i : int = 64 + f : f64 = 3.14 + c : complex128 = 7+3i + q : quaternion256 = 1+2i+3j+4k + mat : matrix[2,3]f32 = {1.5, 2, 1, 0.777, 0.333, 0.8} + matc : #column_major matrix[2,3]f32 = {1.5, 2, 1, 0.777, 0.333, 0.8} + e : enum {A, B, C} = .B + en : E = E.C + ena : [2]E = {E.A, E.C} + s : struct { j: int, k: int } = { j = 16, k = 8 } + sn : S = S{ j = 24, k = 12 } + st : ST = { 32768, 57, "Hellope" } + str : string = "Hellope" + strc : cstring = "Hellope" + bsu : bit_set[0..<32; u32] = {0, 1} + bs : bit_set[4..<16] = {5, 7} + bse : bit_set[E] = { .B, .A } + bsE : BSE = { .A, .C } + arr : [3]int = {1, 2, 3} + ars : [3]S = {S{j = 3, k = 2}, S{j = 2, k = 1}, S{j = 1, k = 0}} + darr : [dynamic]u8 = { 128, 64, 32 } + dars : [dynamic]S = {S{j = 1, k = 2}, S{j = 3, k = 4}} + na : A = {7, 5} + may0 : Maybe(int) + may1 : Maybe(int) = 1 + uz : union {i8, i16} = i8(-33) + u0 : U = U(nil) + u1 : U = i16(42) + uef0 : UEF = E.A + uefa : [3]UEF = { E.A, F.A, F.F } + map_ : map[string]u8 = {"foo" = 8, "bar" = 4} + + bf : bit_field int { + a: int | 4, + b: int | 4, + e: E | 4, + } = {a = 1, b = 2, e = .A} + + defer { + delete(darr) + delete(dars) + delete(map_) + } + + check(t, "64", "%w", i) + check(t, "3.14", "%w", f) + check(t, "7+3i", "%w", c) + check(t, "1+2i+3j+4k", "%w", q) + check(t, "{1.5, 2, 1, 0.777, 0.333, 0.8}", "%w", mat) + check(t, "{1.5, 2, 1, 0.777, 0.333, 0.8}", "%w", matc) + check(t, ".B", "%w", e) + check(t, "E.C", "%w", en) + check(t, "{E.A, E.C}", "%w", ena) + check(t, "{j = 16, k = 8}", "%w", s) + check(t, "S{j = 24, k = 12}", "%w", sn) + check(t, `ST{y = 57, z = "Hellope"}`, "%w", st) + check(t, `"Hellope"`, "%w", str) + check(t, `"Hellope"`, "%w", strc) + check(t, "{0, 1}", "%w", bsu) + check(t, "{5, 7}", "%w", bs) + check(t, "{E.A, E.B}", "%w", bse) + check(t, "{E.A, E.C}", "%w", bsE) + check(t, "{1, 2, 3}", "%w", arr) + check(t, "{S{j = 3, k = 2}, S{j = 2, k = 1}, S{j = 1, k = 0}}", "%w", ars) + check(t, "{128, 64, 32}", "%w", darr) + check(t, "{S{j = 1, k = 2}, S{j = 3, k = 4}}", "%w", dars) + check(t, "{7, 5}", "%w", na) + check(t, "nil", "%w", may0) + check(t, "1", "%w", may1) + check(t, "-33", "%w", uz) + check(t, "nil", "%w", u0) + check(t, "42", "%w", u1) + check(t, "E.A", "%w", uef0) + check(t, "{E.A, F.A, F.F}", "%w", uefa) + check(t, "{a = 1, b = 2, e = E.A}", "%w", bf) + // Check this manually due to the non-deterministic ordering of map keys. + switch fmt.tprintf("%w", map_) { + case `{"foo"=8, "bar"=4}`: break + case `{"bar"=4, "foo"=8}`: break + case: testing.fail(t) + } +} + @(private) check :: proc(t: ^testing.T, exp: string, format: string, args: ..any, loc := #caller_location) { got := fmt.tprintf(format, ..args) diff --git a/tests/core/hash/test_core_hash.odin b/tests/core/hash/test_core_hash.odin index c332383e7..0255717a2 100644 --- a/tests/core/hash/test_core_hash.odin +++ b/tests/core/hash/test_core_hash.odin @@ -54,8 +54,9 @@ test_xxhash_zero_streamed_random_updates :: proc(t: ^testing.T) { // XXH3_128_update random_seed := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&random_seed) for len(b) > 0 { - update_size := min(len(b), rand.int_max(8192, &random_seed)) + update_size := min(len(b), rand.int_max(8192)) if update_size > 4096 { update_size %= 73 } diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin index 23de1b482..432636664 100644 --- a/tests/core/slice/test_core_slice.odin +++ b/tests/core/slice/test_core_slice.odin @@ -3,6 +3,7 @@ package test_core_slice import "core:slice" import "core:testing" import "core:math/rand" +import "core:log" @test test_sort_with_indices :: proc(t: ^testing.T) { @@ -11,6 +12,7 @@ test_sort_with_indices :: proc(t: ^testing.T) { for test_size in test_sizes { r := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&r) vals := make([]u64, test_size) r_idx := make([]int, test_size) // Reverse index @@ -21,7 +23,7 @@ test_sort_with_indices :: proc(t: ^testing.T) { // Set up test values for _, i in vals { - vals[i] = rand.uint64(&r) + vals[i] = rand.uint64() } // Sort @@ -29,7 +31,7 @@ test_sort_with_indices :: proc(t: ^testing.T) { defer delete(f_idx) // Verify sorted test values - rand.init(&r, t.seed) + rand.reset(t.seed) for v, i in f_idx { r_idx[v] = i @@ -45,7 +47,7 @@ test_sort_with_indices :: proc(t: ^testing.T) { } } - idx_pass := vals[r_idx[i]] == rand.uint64(&r) + idx_pass := vals[r_idx[i]] == rand.uint64() testing.expect(t, idx_pass, "Expected index to have been sorted") if !idx_pass { break @@ -62,6 +64,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { for test_size in test_sizes { r := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&r) vals := make([]u64, test_size) r_idx := make([]int, test_size) // Reverse index @@ -72,7 +75,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { // Set up test values for _, i in vals { - vals[i] = rand.uint64(&r) + vals[i] = rand.uint64() } // Sort @@ -80,7 +83,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { defer delete(f_idx) // Verify sorted test values - rand.init(&r, t.seed) + rand.reset(t.seed) { indices := make([]int, test_size) @@ -205,7 +208,7 @@ test_permutation_iterator :: proc(t: ^testing.T) { n += item } if n in seen { - testing.fail_now(t, "Permutation iterator made a duplicate permutation.") + log.error("Permutation iterator made a duplicate permutation.") return } seen[n] = true diff --git a/tests/internal/test_map.odin b/tests/internal/test_map.odin index a9a8cf5d4..ab7e52f33 100644 --- a/tests/internal/test_map.odin +++ b/tests/internal/test_map.odin @@ -17,9 +17,10 @@ map_insert_random_key_value :: proc(t: ^testing.T) { unique_keys := 0 r := rand.create(t.seed + seed_incr) + context.random_generator = rand.default_random_generator(&r) for _ in 0.. Matrix { // Get float array of matrix data @(require_results) MatrixToFloatV :: proc "c" (mat: Matrix) -> [16]f32 { - return transmute([16]f32)mat + return transmute([16]f32)linalg.transpose(mat) } diff --git a/vendor/raylib/rlgl.odin b/vendor/raylib/rlgl.odin deleted file mode 100644 index c9e8c28c2..000000000 --- a/vendor/raylib/rlgl.odin +++ /dev/null @@ -1,555 +0,0 @@ -/********************************************************************************************** -* -* rlgl v5.0 - A multi-OpenGL abstraction layer with an immediate-mode style API -* -* DESCRIPTION: -* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) -* that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) -* -* ADDITIONAL NOTES: -* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are -* initialized on rlglInit() to accumulate vertex data. -* -* When an internal state change is required all the stored vertex data is renderer in batch, -* additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch. -* -* Some resources are also loaded for convenience, here the complete list: -* - Default batch (RLGL.defaultBatch): RenderBatch system to accumulate vertex data -* - Default texture (RLGL.defaultTextureId): 1x1 white pixel R8G8B8A8 -* - Default shader (RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs) -* -* Internal buffer (and resources) must be manually unloaded calling rlglClose(). -* -* CONFIGURATION: -* #define GRAPHICS_API_OPENGL_11 -* #define GRAPHICS_API_OPENGL_21 -* #define GRAPHICS_API_OPENGL_33 -* #define GRAPHICS_API_OPENGL_43 -* #define GRAPHICS_API_OPENGL_ES2 -* #define GRAPHICS_API_OPENGL_ES3 -* Use selected OpenGL graphics backend, should be supported by platform -* Those preprocessor defines are only used on rlgl module, if OpenGL version is -* required by any other module, use rlGetVersion() to check it -* -* #define RLGL_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. -* -* #define RLGL_RENDER_TEXTURES_HINT -* Enable framebuffer objects (fbo) support (enabled by default) -* Some GPUs could not support them despite the OpenGL version -* -* #define RLGL_SHOW_GL_DETAILS_INFO -* Show OpenGL extensions and capabilities detailed logs on init -* -* #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT -* Enable debug context (only available on OpenGL 4.3) -* -* rlgl capabilities could be customized just defining some internal -* values before library inclusion (default values listed): -* -* #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // Default internal render batch elements limits -* #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) -* #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) -* #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) -* -* #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack -* #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported -* #define RL_CULL_DISTANCE_NEAR 0.01 // Default projection matrix near cull distance -* #define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance -* -* When loading a shader, the following vertex attributes and uniform -* location names are tried to be set automatically: -* -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: 5 -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) -* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) -* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) -* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) -* -* DEPENDENCIES: -* - OpenGL libraries (depending on platform and OpenGL version selected) -* - GLAD OpenGL extensions loading library (only for OpenGL 3.3 Core, 4.3 Core) -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - - -package raylib - -import "core:c" - -RLGL_VERSION :: "4.5" - -when ODIN_OS == .Windows { - foreign import lib { - "windows/raylib.lib", - "system:Winmm.lib", - "system:Gdi32.lib", - "system:User32.lib", - "system:Shell32.lib", - } -} else when ODIN_OS == .Linux { - foreign import lib "linux/libraylib.a" -} else when ODIN_OS == .Darwin { - foreign import lib { - "macos-arm64/libraylib.a" when ODIN_ARCH == .arm64 else "macos/libraylib.a", - "system:Cocoa.framework", - "system:OpenGL.framework", - "system:IOKit.framework", - } -} else { - foreign import lib "system:raylib" -} - -RL_GRAPHICS_API_OPENGL_11 :: false -RL_GRAPHICS_API_OPENGL_21 :: true -RL_GRAPHICS_API_OPENGL_33 :: RL_GRAPHICS_API_OPENGL_21 // default currently -RL_GRAPHICS_API_OPENGL_ES2 :: false -RL_GRAPHICS_API_OPENGL_43 :: false -RL_GRAPHICS_API_OPENGL_ES3 :: false - -when RL_GRAPHICS_API_OPENGL_ES3 { - RL_GRAPHICS_API_OPENGL_ES2 :: true -} - -when !RL_GRAPHICS_API_OPENGL_ES2 { - // This is the maximum amount of elements (quads) per batch - // NOTE: Be careful with text, every letter maps to a quad - RL_DEFAULT_BATCH_BUFFER_ELEMENTS :: 8192 -} else { - // We reduce memory sizes for embedded systems (RPI and HTML5) - // NOTE: On HTML5 (emscripten) this is allocated on heap, - // by default it's only 16MB!...just take care... - RL_DEFAULT_BATCH_BUFFER_ELEMENTS :: 2048 -} - -RL_DEFAULT_BATCH_BUFFERS :: 1 // Default number of batch buffers (multi-buffering) -RL_DEFAULT_BATCH_DRAWCALLS :: 256 // Default number of batch draw calls (by state changes: mode, texture) -RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS :: 4 // Maximum number of additional textures that can be activated on batch drawing (SetShaderValueTexture()) - -// Internal Matrix stack -RL_MAX_MATRIX_STACK_SIZE :: 32 // Maximum size of Matrix stack - -// Shader limits -RL_MAX_SHADER_LOCATIONS :: 32 // Maximum number of shader locations supported - -// Projection matrix culling -RL_CULL_DISTANCE_NEAR :: 0.01 // Default near cull distance -RL_CULL_DISTANCE_FAR :: 1000.0 // Default far cull distance - -// Texture parameters (equivalent to OpenGL defines) -RL_TEXTURE_WRAP_S :: 0x2802 // GL_TEXTURE_WRAP_S -RL_TEXTURE_WRAP_T :: 0x2803 // GL_TEXTURE_WRAP_T -RL_TEXTURE_MAG_FILTER :: 0x2800 // GL_TEXTURE_MAG_FILTER -RL_TEXTURE_MIN_FILTER :: 0x2801 // GL_TEXTURE_MIN_FILTER - -RL_TEXTURE_FILTER_NEAREST :: 0x2600 // GL_NEAREST -RL_TEXTURE_FILTER_LINEAR :: 0x2601 // GL_LINEAR -RL_TEXTURE_FILTER_MIP_NEAREST :: 0x2700 // GL_NEAREST_MIPMAP_NEAREST -RL_TEXTURE_FILTER_NEAREST_MIP_LINEAR :: 0x2702 // GL_NEAREST_MIPMAP_LINEAR -RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST :: 0x2701 // GL_LINEAR_MIPMAP_NEAREST -RL_TEXTURE_FILTER_MIP_LINEAR :: 0x2703 // GL_LINEAR_MIPMAP_LINEAR -RL_TEXTURE_FILTER_ANISOTROPIC :: 0x3000 // Anisotropic filter (custom identifier) - -RL_TEXTURE_WRAP_REPEAT :: 0x2901 // GL_REPEAT -RL_TEXTURE_WRAP_CLAMP :: 0x812F // GL_CLAMP_TO_EDGE -RL_TEXTURE_WRAP_MIRROR_REPEAT :: 0x8370 // GL_MIRRORED_REPEAT -RL_TEXTURE_WRAP_MIRROR_CLAMP :: 0x8742 // GL_MIRROR_CLAMP_EXT - -// Matrix modes (equivalent to OpenGL) -RL_MODELVIEW :: 0x1700 // GL_MODELVIEW -RL_PROJECTION :: 0x1701 // GL_PROJECTION -RL_TEXTURE :: 0x1702 // GL_TEXTURE - -// Primitive assembly draw modes -RL_LINES :: 0x0001 // GL_LINES -RL_TRIANGLES :: 0x0004 // GL_TRIANGLES -RL_QUADS :: 0x0007 // GL_QUADS - -// GL equivalent data types -RL_UNSIGNED_BYTE :: 0x1401 // GL_UNSIGNED_BYTE -RL_FLOAT :: 0x1406 // GL_FLOAT - -// Buffer usage hint -RL_STREAM_DRAW :: 0x88E0 // GL_STREAM_DRAW -RL_STREAM_READ :: 0x88E1 // GL_STREAM_READ -RL_STREAM_COPY :: 0x88E2 // GL_STREAM_COPY -RL_STATIC_DRAW :: 0x88E4 // GL_STATIC_DRAW -RL_STATIC_READ :: 0x88E5 // GL_STATIC_READ -RL_STATIC_COPY :: 0x88E6 // GL_STATIC_COPY -RL_DYNAMIC_DRAW :: 0x88E8 // GL_DYNAMIC_DRAW -RL_DYNAMIC_READ :: 0x88E9 // GL_DYNAMIC_READ -RL_DYNAMIC_COPY :: 0x88EA // GL_DYNAMIC_COPY - -// GL Shader type -RL_FRAGMENT_SHADER :: 0x8B30 // GL_FRAGMENT_SHADER -RL_VERTEX_SHADER :: 0x8B31 // GL_VERTEX_SHADER -RL_COMPUTE_SHADER :: 0x91B9 // GL_COMPUTE_SHADER - -// GL blending factors -RL_ZERO :: 0 // GL_ZERO -RL_ONE :: 1 // GL_ONE -RL_SRC_COLOR :: 0x0300 // GL_SRC_COLOR -RL_ONE_MINUS_SRC_COLOR :: 0x0301 // GL_ONE_MINUS_SRC_COLOR -RL_SRC_ALPHA :: 0x0302 // GL_SRC_ALPHA -RL_ONE_MINUS_SRC_ALPHA :: 0x0303 // GL_ONE_MINUS_SRC_ALPHA -RL_DST_ALPHA :: 0x0304 // GL_DST_ALPHA -RL_ONE_MINUS_DST_ALPHA :: 0x0305 // GL_ONE_MINUS_DST_ALPHA -RL_DST_COLOR :: 0x0306 // GL_DST_COLOR -RL_ONE_MINUS_DST_COLOR :: 0x0307 // GL_ONE_MINUS_DST_COLOR -RL_SRC_ALPHA_SATURATE :: 0x0308 // GL_SRC_ALPHA_SATURATE -RL_CONSTANT_COLOR :: 0x8001 // GL_CONSTANT_COLOR -RL_ONE_MINUS_CONSTANT_COLOR :: 0x8002 // GL_ONE_MINUS_CONSTANT_COLOR -RL_CONSTANT_ALPHA :: 0x8003 // GL_CONSTANT_ALPHA -RL_ONE_MINUS_CONSTANT_ALPHA :: 0x8004 // GL_ONE_MINUS_CONSTANT_ALPHA - -// GL blending functions/equations -RL_FUNC_ADD :: 0x8006 // GL_FUNC_ADD -RL_MIN :: 0x8007 // GL_MIN -RL_MAX :: 0x8008 // GL_MAX -RL_FUNC_SUBTRACT :: 0x800A // GL_FUNC_SUBTRACT -RL_FUNC_REVERSE_SUBTRACT :: 0x800B // GL_FUNC_REVERSE_SUBTRACT -RL_BLEND_EQUATION :: 0x8009 // GL_BLEND_EQUATION -RL_BLEND_EQUATION_RGB :: 0x8009 // GL_BLEND_EQUATION_RGB // (Same as BLEND_EQUATION) -RL_BLEND_EQUATION_ALPHA :: 0x883D // GL_BLEND_EQUATION_ALPHA -RL_BLEND_DST_RGB :: 0x80C8 // GL_BLEND_DST_RGB -RL_BLEND_SRC_RGB :: 0x80C9 // GL_BLEND_SRC_RGB -RL_BLEND_DST_ALPHA :: 0x80CA // GL_BLEND_DST_ALPHA -RL_BLEND_SRC_ALPHA :: 0x80CB // GL_BLEND_SRC_ALPHA -RL_BLEND_COLOR :: 0x8005 // GL_BLEND_COLOR - - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- - - -VertexBufferIndexType :: c.ushort when RL_GRAPHICS_API_OPENGL_ES2 else c.uint - -// Dynamic vertex buffers (position + texcoords + colors + indices arrays) -VertexBuffer :: struct { - elementCount: c.int, // Number of elements in the buffer (QUADS) - - vertices: [^]f32, // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) - texcoords: [^]f32, // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) - colors: [^]u8, // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) - indices: [^]VertexBufferIndexType, // Vertex indices (in case vertex data comes indexed) (6 indices per quad) - vaoId: c.uint, // OpenGL Vertex Array Object id - vboId: [4]c.uint, // OpenGL Vertex Buffer Objects id (4 types of vertex data) -} - -// Draw call type -// NOTE: Only texture changes register a new draw, other state-change-related elements are not -// used at this moment (vaoId, shaderId, matrices), raylib just forces a batch draw call if any -// of those state-change happens (this is done in core module) -DrawCall :: struct { - mode: c.int, // Drawing mode: LINES, TRIANGLES, QUADS - vertexCount: c.int, // Number of vertex of the draw - vertexAlignment: c.int, // Number of vertex required for index alignment (LINES, TRIANGLES) - textureId: c.uint, // Texture id to be used on the draw -> Use to create new draw call if changes -} - -// RenderBatch type -RenderBatch :: struct { - bufferCount: c.int, // Number of vertex buffers (multi-buffering support) - currentBuffer: c.int, // Current buffer tracking in case of multi-buffering - vertexBuffer: [^]VertexBuffer, // Dynamic buffer(s) for vertex data - - draws: [^]DrawCall, // Draw calls array, depends on textureId - drawCounter: c.int, // Draw calls counter - currentDepth: f32, // Current depth value for next draw -} - - -// OpenGL version -GlVersion :: enum c.int { - OPENGL_11 = 1, // OpenGL 1.1 - OPENGL_21, // OpenGL 2.1 (GLSL 120) - OPENGL_33, // OpenGL 3.3 (GLSL 330) - OPENGL_43, // OpenGL 4.3 (using GLSL 330) - OPENGL_ES_20, // OpenGL ES 2.0 (GLSL 100) - OPENGL_ES_30, // OpenGL ES 3.0 (GLSL 300 es) -} - - -// Shader attribute data types -ShaderAttributeDataType :: enum c.int { - FLOAT = 0, // Shader attribute type: float - VEC2, // Shader attribute type: vec2 (2 float) - VEC3, // Shader attribute type: vec3 (3 float) - VEC4, // Shader attribute type: vec4 (4 float) -} - -// Framebuffer attachment type -// NOTE: By default up to 8 color channels defined, but it can be more -FramebufferAttachType :: enum c.int { - COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 - COLOR_CHANNEL1 = 1, // Framebuffer attachment type: color 1 - COLOR_CHANNEL2 = 2, // Framebuffer attachment type: color 2 - COLOR_CHANNEL3 = 3, // Framebuffer attachment type: color 3 - COLOR_CHANNEL4 = 4, // Framebuffer attachment type: color 4 - COLOR_CHANNEL5 = 5, // Framebuffer attachment type: color 5 - COLOR_CHANNEL6 = 6, // Framebuffer attachment type: color 6 - COLOR_CHANNEL7 = 7, // Framebuffer attachment type: color 7 - DEPTH = 100, // Framebuffer attachment type: depth - STENCIL = 200, // Framebuffer attachment type: stencil -} - -// Framebuffer texture attachment type -FramebufferAttachTextureType :: enum c.int { - CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side - CUBEMAP_NEGATIVE_X = 1, // Framebuffer texture attachment type: cubemap, -X side - CUBEMAP_POSITIVE_Y = 2, // Framebuffer texture attachment type: cubemap, +Y side - CUBEMAP_NEGATIVE_Y = 3, // Framebuffer texture attachment type: cubemap, -Y side - CUBEMAP_POSITIVE_Z = 4, // Framebuffer texture attachment type: cubemap, +Z side - CUBEMAP_NEGATIVE_Z = 5, // Framebuffer texture attachment type: cubemap, -Z side - TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d - RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer -} - -CullMode :: enum c.int { - FRONT = 0, - BACK, -} - -@(default_calling_convention="c") -foreign lib { - //------------------------------------------------------------------------------------ - // Functions Declaration - Matrix operations - //------------------------------------------------------------------------------------ - rlMatrixMode :: proc(mode: c.int) --- // Choose the current matrix to be transformed - rlPushMatrix :: proc() --- // Push the current matrix to stack - rlPopMatrix :: proc() --- // Pop lattest inserted matrix from stack - rlLoadIdentity :: proc() --- // Reset current matrix to identity matrix - rlTranslatef :: proc(x, y, z: f32) --- // Multiply the current matrix by a translation matrix - rlRotatef :: proc(angleDeg: f32, x, y, z: f32) --- // Multiply the current matrix by a rotation matrix - rlScalef :: proc(x, y, z: f32) --- // Multiply the current matrix by a scaling matrix - rlMultMatrixf :: proc(matf: [^]f32) --- // Multiply the current matrix by another matrix - rlFrustum :: proc(left, right, bottom, top, znear, zfar: f64) --- - rlOrtho :: proc(left, right, bottom, top, znear, zfar: f64) --- - rlViewport :: proc(x, y, width, height: c.int) --- // Set the viewport area - - //------------------------------------------------------------------------------------ - // Functions Declaration - Vertex level operations - //------------------------------------------------------------------------------------ - rlBegin :: proc(mode: c.int) --- // Initialize drawing mode (how to organize vertex) - rlEnd :: proc() --- // Finish vertex providing - rlVertex2i :: proc(x, y: c.int) --- // Define one vertex (position) - 2 int - rlVertex2f :: proc(x, y: f32) --- // Define one vertex (position) - 2 f32 - rlVertex3f :: proc(x, y, z: f32) --- // Define one vertex (position) - 3 f32 - rlTexCoord2f :: proc(x, y: f32) --- // Define one vertex (texture coordinate) - 2 f32 - rlNormal3f :: proc(x, y, z: f32) --- // Define one vertex (normal) - 3 f32 - rlColor4ub :: proc(r, g, b, a: u8) --- // Define one vertex (color) - 4 byte - rlColor3f :: proc(x, y, z: f32) --- // Define one vertex (color) - 3 f32 - rlColor4f :: proc(x, y, z, w: f32) --- // Define one vertex (color) - 4 f32 - - //------------------------------------------------------------------------------------ - // Functions Declaration - OpenGL style functions (common to 1.1, 3.3+, ES2) - // NOTE: This functions are used to completely abstract raylib code from OpenGL layer, - // some of them are direct wrappers over OpenGL calls, some others are custom - //------------------------------------------------------------------------------------ - - // Vertex buffers state - rlEnableVertexArray :: proc(vaoId: c.uint) -> bool --- // Enable vertex array (VAO, if supported) - rlDisableVertexArray :: proc() --- // Disable vertex array (VAO, if supported) - rlEnableVertexBuffer :: proc(id: c.uint) --- // Enable vertex buffer (VBO) - rlDisableVertexBuffer :: proc() --- // Disable vertex buffer (VBO) - rlEnableVertexBufferElement :: proc(id: c.uint) --- // Enable vertex buffer element (VBO element) - rlDisableVertexBufferElement :: proc() --- // Disable vertex buffer element (VBO element) - rlEnableVertexAttribute :: proc(index: c.uint) --- // Enable vertex attribute index - rlDisableVertexAttribute :: proc(index: c.uint) --- // Disable vertex attribute index - when RL_GRAPHICS_API_OPENGL_11 { - rlEnableStatePointer :: proc(vertexAttribType: c.int, buffer: rawptr) --- - rlDisableStatePointer :: proc(vertexAttribType: c.int) --- - } - - // Textures state - rlActiveTextureSlot :: proc(slot: c.int) --- // Select and active a texture slot - rlEnableTexture :: proc(id: c.uint) --- // Enable texture - rlDisableTexture :: proc() --- // Disable texture - rlEnableTextureCubemap :: proc(id: c.uint) --- // Enable texture cubemap - rlDisableTextureCubemap :: proc() --- // Disable texture cubemap - rlTextureParameters :: proc(id: c.uint, param: c.int, value: c.int) --- // Set texture parameters (filter, wrap) - rlCubemapParameters :: proc(id: i32, param: c.int, value: c.int) --- // Set cubemap parameters (filter, wrap) - - // Shader state - rlEnableShader :: proc(id: c.uint) --- // Enable shader program - rlDisableShader :: proc() --- // Disable shader program - - // Framebuffer state - rlEnableFramebuffer :: proc(id: c.uint) --- // Enable render texture (fbo) - rlDisableFramebuffer :: proc() --- // Disable render texture (fbo), return to default framebuffer - rlActiveDrawBuffers :: proc(count: c.int) --- // Activate multiple draw color buffers - rlBlitFramebuffer :: proc(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight, bufferMask: c.int) --- // Blit active framebuffer to main framebuffer - - // General render state - rlDisableColorBlend :: proc() --- // Disable color blending - rlEnableDepthTest :: proc() --- // Enable depth test - rlDisableDepthTest :: proc() --- // Disable depth test - rlEnableDepthMask :: proc() --- // Enable depth write - rlDisableDepthMask :: proc() --- // Disable depth write - rlEnableBackfaceCulling :: proc() --- // Enable backface culling - rlDisableBackfaceCulling :: proc() --- // Disable backface culling - rlSetCullFace :: proc(mode: CullMode) --- // Set face culling mode - rlEnableScissorTest :: proc() --- // Enable scissor test - rlDisableScissorTest :: proc() --- // Disable scissor test - rlScissor :: proc(x, y, width, height: c.int) --- // Scissor test - rlEnableWireMode :: proc() --- // Enable wire mode - rlEnablePointMode :: proc() --- // Enable point mode - rlDisableWireMode :: proc() --- // Disable wire and point modes - rlSetLineWidth :: proc(width: f32) --- // Set the line drawing width - rlGetLineWidth :: proc() -> f32 --- // Get the line drawing width - rlEnableSmoothLines :: proc() --- // Enable line aliasing - rlDisableSmoothLines :: proc() --- // Disable line aliasing - rlEnableStereoRender :: proc() --- // Enable stereo rendering - rlDisableStereoRender :: proc() --- // Disable stereo rendering - rlIsStereoRenderEnabled :: proc() -> bool --- // Check if stereo render is enabled - - - rlClearColor :: proc(r, g, b, a: u8) --- // Clear color buffer with color - rlClearScreenBuffers :: proc() --- // Clear used screen buffers (color and depth) - rlCheckErrors :: proc() --- // Check and log OpenGL error codes - rlSetBlendMode :: proc(mode: c.int) --- // Set blending mode - rlSetBlendFactors :: proc(glSrcFactor, glDstFactor, glEquation: c.int) --- // Set blending mode factor and equation (using OpenGL factors) - rlSetBlendFactorsSeparate :: proc(glSrcRGB, glDstRGB, glSrcAlpha, glDstAlpha, glEqRGB, glEqAlpha: c.int) --- // Set blending mode factors and equations separately (using OpenGL factors) - - //------------------------------------------------------------------------------------ - // Functions Declaration - rlgl functionality - //------------------------------------------------------------------------------------ - // rlgl initialization functions - rlglInit :: proc(width, height: c.int) --- // Initialize rlgl (buffers, shaders, textures, states) - rlglClose :: proc() --- // De-initialize rlgl (buffers, shaders, textures) - rlLoadExtensions :: proc(loader: rawptr) --- // Load OpenGL extensions (loader function required) - rlGetVersion :: proc() -> GlVersion --- // Get current OpenGL version - rlSetFramebufferWidth :: proc(width: c.int) --- // Set current framebuffer width - rlGetFramebufferWidth :: proc() -> c.int --- // Get default framebuffer width - rlSetFramebufferHeight :: proc(height: c.int) --- // Set current framebuffer height - rlGetFramebufferHeight :: proc() -> c.int --- // Get default framebuffer height - - - rlGetTextureIdDefault :: proc() -> c.uint --- // Get default texture id - rlGetShaderIdDefault :: proc() -> c.uint --- // Get default shader id - rlGetShaderLocsDefault :: proc() -> [^]c.int --- // Get default shader locations - - // Render batch management - // NOTE: rlgl provides a default render batch to behave like OpenGL 1.1 immediate mode - // but this render batch API is exposed in case of custom batches are required - rlLoadRenderBatch :: proc(numBuffers, bufferElements: c.int) -> RenderBatch --- // Load a render batch system - rlUnloadRenderBatch :: proc(batch: RenderBatch) --- // Unload render batch system - rlDrawRenderBatch :: proc(batch: ^RenderBatch) --- // Draw render batch data (Update->Draw->Reset) - rlSetRenderBatchActive :: proc(batch: ^RenderBatch) --- // Set the active render batch for rlgl (NULL for default internal) - rlDrawRenderBatchActive :: proc() --- // Update and draw internal render batch - rlCheckRenderBatchLimit :: proc(vCount: c.int) -> c.int --- // Check internal buffer overflow for a given number of vertex - - rlSetTexture :: proc(id: c.uint) --- // Set current texture for render batch and check buffers limits - - //------------------------------------------------------------------------------------------------------------------------ - - // Vertex buffers management - rlLoadVertexArray :: proc() -> c.uint --- // Load vertex array (vao) if supported - rlLoadVertexBuffer :: proc(buffer: rawptr, size: c.int, is_dynamic: bool) -> c.uint --- // Load a vertex buffer attribute - rlLoadVertexBufferElement :: proc(buffer: rawptr, size: c.int, is_dynamic: bool) -> c.uint --- // Load a new attributes element buffer - rlUpdateVertexBuffer :: proc(bufferId: c.uint, data: rawptr, dataSize: c.int, offset: c.int) --- // Update GPU buffer with new data - rlUpdateVertexBufferElements :: proc(id: c.uint, data: rawptr, dataSize: c.int, offset: c.int) --- // Update vertex buffer elements with new data - rlUnloadVertexArray :: proc(vaoId: c.uint) --- - rlUnloadVertexBuffer :: proc(vboId: c.uint) --- - rlSetVertexAttribute :: proc(index: c.uint, compSize: c.int, type: c.int, normalized: bool, stride: c.int, pointer: rawptr) --- - rlSetVertexAttributeDivisor :: proc(index: c.uint, divisor: c.int) --- - rlSetVertexAttributeDefault :: proc(locIndex: c.int, value: rawptr, attribType: c.int, count: c.int) --- // Set vertex attribute default value - rlDrawVertexArray :: proc(offset: c.int, count: c.int) --- - rlDrawVertexArrayElements :: proc(offset: c.int, count: c.int, buffer: rawptr) --- - rlDrawVertexArrayInstanced :: proc(offset: c.int, count: c.int, instances: c.int) --- - rlDrawVertexArrayElementsInstanced :: proc(offset: c.int, count: c.int, buffer: rawptr, instances: c.int) --- - - // Textures management - rlLoadTexture :: proc(data: rawptr, width, height: c.int, format: c.int, mipmapCount: c.int) -> c.uint --- // Load texture in GPU - rlLoadTextureDepth :: proc(width, height: c.int, useRenderBuffer: bool) -> c.uint --- // Load depth texture/renderbuffer (to be attached to fbo) - rlLoadTextureCubemap :: proc(data: rawptr, size: c.int, format: c.int) -> c.uint --- // Load texture cubemap - rlUpdateTexture :: proc(id: c.uint, offsetX, offsetY: c.int, width, height: c.int, format: c.int, data: rawptr) --- // Update GPU texture with new data - rlGetGlTextureFormats :: proc(format: c.int, glInternalFormat, glFormat, glType: ^c.uint) --- // Get OpenGL internal formats - rlGetPixelFormatName :: proc(format: c.uint) -> cstring --- // Get name string for pixel format - rlUnloadTexture :: proc(id: c.uint) --- // Unload texture from GPU memory - rlGenTextureMipmaps :: proc(id: c.uint, width, height: c.int, format: c.int, mipmaps: ^c.int) --- // Generate mipmap data for selected texture - rlReadTexturePixels :: proc(id: c.uint, width, height: c.int, format: c.int) -> rawptr --- // Read texture pixel data - rlReadScreenPixels :: proc(width, height: c.int) -> [^]byte --- // Read screen pixel data (color buffer) - - // Framebuffer management (fbo) - rlLoadFramebuffer :: proc(width, height: c.int) -> c.uint --- // Load an empty framebuffer - rlFramebufferAttach :: proc(fboId, texId: c.uint, attachType: c.int, texType: c.int, mipLevel: c.int) --- // Attach texture/renderbuffer to a framebuffer - rlFramebufferComplete :: proc(id: c.uint) -> bool --- // Verify framebuffer is complete - rlUnloadFramebuffer :: proc(id: c.uint) --- // Delete framebuffer from GPU - - // Shaders management - rlLoadShaderCode :: proc(vsCode, fsCode: cstring) -> c.uint --- // Load shader from code strings - rlCompileShader :: proc(shaderCode: cstring, type: c.int) -> c.uint --- // Compile custom shader and return shader id (type: RL_VERTEX_SHADER, RL_FRAGMENT_SHADER, RL_COMPUTE_SHADER) - rlLoadShaderProgram :: proc(vShaderId, fShaderId: c.uint) -> c.uint --- // Load custom shader program - rlUnloadShaderProgram :: proc(id: c.uint) --- // Unload shader program - rlGetLocationUniform :: proc(shaderId: c.uint, uniformName: cstring) -> c.int --- // Get shader location uniform - rlGetLocationAttrib :: proc(shaderId: c.uint, attribName: cstring) -> c.int --- // Get shader location attribute - rlSetUniform :: proc(locIndex: c.int, value: rawptr, uniformType: c.int, count: c.int) --- // Set shader value uniform - rlSetUniformMatrix :: proc(locIndex: c.int, mat: Matrix) --- // Set shader value matrix - rlSetUniformSampler :: proc(locIndex: c.int, textureId: c.uint) --- // Set shader value sampler - rlSetShader :: proc(id: c.uint, locs: [^]c.int) --- // Set shader currently active (id and locations) - - // Compute shader management - rlLoadComputeShaderProgram :: proc(shaderId: c.uint) -> c.uint --- // Load compute shader program - rlComputeShaderDispatch :: proc(groupX, groupY, groupZ: c.uint) --- // Dispatch compute shader (equivalent to *draw* for graphics pipeline) - - // Shader buffer storage object management (ssbo) - rlLoadShaderBuffer :: proc(size: c.uint, data: rawptr, usageHint: c.int) -> c.uint --- // Load shader storage buffer object (SSBO) - rlUnloadShaderBuffer :: proc(ssboId: c.uint) --- // Unload shader storage buffer object (SSBO) - rlUpdateShaderBuffer :: proc(id: c.uint, data: rawptr, dataSize: c.uint, offset: c.uint) --- // Update SSBO buffer data - rlBindShaderBuffer :: proc(id: c.uint, index: c.uint) --- // Bind SSBO buffer - rlReadShaderBuffer :: proc(id: c.uint, dest: rawptr, count: c.uint, offset: c.uint) --- // Read SSBO buffer data (GPU->CPU) - rlCopyShaderBuffer :: proc(destId, srcId: c.uint, destOffset, srcOffset: c.uint, count: c.uint) --- // Copy SSBO data between buffers - rlGetShaderBufferSize :: proc(id: c.uint) -> c.uint --- // Get SSBO buffer size - - // Buffer management - rlBindImageTexture :: proc(id: c.uint, index: c.uint, format: c.int, readonly: bool) --- // Bind image texture - - // Matrix state management - rlGetMatrixModelview :: proc() -> Matrix --- // Get internal modelview matrix - rlGetMatrixProjection :: proc() -> Matrix --- // Get internal projection matrix - rlGetMatrixTransform :: proc() -> Matrix --- // Get internal accumulated transform matrix - rlGetMatrixProjectionStereo :: proc(eye: c.int) -> Matrix --- // Get internal projection matrix for stereo render (selected eye) - rlGetMatrixViewOffsetStereo :: proc(eye: c.int) -> Matrix --- // Get internal view offset matrix for stereo render (selected eye) - rlSetMatrixProjection :: proc(proj: Matrix) --- // Set a custom projection matrix (replaces internal projection matrix) - rlSetMatrixModelview :: proc(view: Matrix) --- // Set a custom modelview matrix (replaces internal modelview matrix) - rlSetMatrixProjectionStereo :: proc(right, left: Matrix) --- // Set eyes projection matrices for stereo rendering - rlSetMatrixViewOffsetStereo :: proc(right, left: Matrix) --- // Set eyes view offsets matrices for stereo rendering - - // Quick and dirty cube/quad buffers load->draw->unload - rlLoadDrawCube :: proc() --- // Load and draw a cube - rlLoadDrawQuad :: proc() --- // Load and draw a quad -} diff --git a/vendor/raylib/rlgl/rlgl.odin b/vendor/raylib/rlgl/rlgl.odin new file mode 100644 index 000000000..cef31c238 --- /dev/null +++ b/vendor/raylib/rlgl/rlgl.odin @@ -0,0 +1,581 @@ +/********************************************************************************************** +* +* rlgl v5.0 - A multi-OpenGL abstraction layer with an immediate-mode style API +* +* DESCRIPTION: +* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) +* that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) +* +* ADDITIONAL NOTES: +* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are +* initialized on rlglInit() to accumulate vertex data. +* +* When an internal state change is required all the stored vertex data is renderer in batch, +* additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch. +* +* Some resources are also loaded for convenience, here the complete list: +* - Default batch (RLGL.defaultBatch): RenderBatch system to accumulate vertex data +* - Default texture (RLGL.defaultTextureId): 1x1 white pixel R8G8B8A8 +* - Default shader (RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs) +* +* Internal buffer (and resources) must be manually unloaded calling rlglClose(). +* +* CONFIGURATION: +* #define GRAPHICS_API_OPENGL_11 +* #define GRAPHICS_API_OPENGL_21 +* #define GRAPHICS_API_OPENGL_33 +* #define GRAPHICS_API_OPENGL_43 +* #define GRAPHICS_API_OPENGL_ES2 +* #define GRAPHICS_API_OPENGL_ES3 +* Use selected OpenGL graphics backend, should be supported by platform +* Those preprocessor defines are only used on rlgl module, if OpenGL version is +* required by any other module, use rlGetVersion() to check it +* +* #define RLGL_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* #define RLGL_RENDER_TEXTURES_HINT +* Enable framebuffer objects (fbo) support (enabled by default) +* Some GPUs could not support them despite the OpenGL version +* +* #define RLGL_SHOW_GL_DETAILS_INFO +* Show OpenGL extensions and capabilities detailed logs on init +* +* #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT +* Enable debug context (only available on OpenGL 4.3) +* +* rlgl capabilities could be customized just defining some internal +* values before library inclusion (default values listed): +* +* #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // Default internal render batch elements limits +* #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) +* #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) +* #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) +* +* #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack +* #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported +* #define RL_CULL_DISTANCE_NEAR 0.01 // Default projection matrix near cull distance +* #define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance +* +* When loading a shader, the following vertex attributes and uniform +* location names are tried to be set automatically: +* +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD2 "vertexTexCoord2" // Bound by default to shader location: 5 +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) +* +* DEPENDENCIES: +* - OpenGL libraries (depending on platform and OpenGL version selected) +* - GLAD OpenGL extensions loading library (only for OpenGL 3.3 Core, 4.3 Core) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + + +package rlgl + +import "core:c" +import rl "../." + +VERSION :: "5.0" + +RAYLIB_SHARED :: #config(RAYLIB_SHARED, false) + +// Note: We pull in the full raylib library. If you want a truly stand-alone rlgl, then: +// - Compile a separate rlgl library and use that in the foreign import blocks below. +// - Remove the `import rl "../."` line +// - Copy the code from raylib.odin for any types we alias from that package (see PixelFormat etc) + +when ODIN_OS == .Windows { + @(extra_linker_flags="/NODEFAULTLIB:" + ("msvcrt" when RAYLIB_SHARED else "libcmt")) + foreign import lib { + "../windows/raylibdll.lib" when RAYLIB_SHARED else "../windows/raylib.lib" , + "system:Winmm.lib", + "system:Gdi32.lib", + "system:User32.lib", + "system:Shell32.lib", + } +} else when ODIN_OS == .Linux { + foreign import lib { + // Note(bumbread): I'm not sure why in `linux/` folder there are + // multiple copies of raylib.so, but since these bindings are for + // particular version of the library, I better specify it. Ideally, + // though, it's best specified in terms of major (.so.4) + "../linux/libraylib.so.500" when RAYLIB_SHARED else "../linux/libraylib.a", + "system:dl", + "system:pthread", + } +} else when ODIN_OS == .Darwin { + foreign import lib { + "../macos" + + ("-arm64" when ODIN_ARCH == .arm64 else "") + + "/libraylib" + (".500.dylib" when RAYLIB_SHARED else ".a"), + "system:Cocoa.framework", + "system:OpenGL.framework", + "system:IOKit.framework", + } +} else { + foreign import lib "system:raylib" +} + +GRAPHICS_API_OPENGL_11 :: false +GRAPHICS_API_OPENGL_21 :: true +GRAPHICS_API_OPENGL_33 :: GRAPHICS_API_OPENGL_21 // default currently +GRAPHICS_API_OPENGL_ES2 :: false +GRAPHICS_API_OPENGL_43 :: false +GRAPHICS_API_OPENGL_ES3 :: false + +when GRAPHICS_API_OPENGL_ES3 { + GRAPHICS_API_OPENGL_ES2 :: true +} + +when !GRAPHICS_API_OPENGL_ES2 { + // This is the maximum amount of elements (quads) per batch + // NOTE: Be careful with text, every letter maps to a quad + DEFAULT_BATCH_BUFFER_ELEMENTS :: 8192 +} else { + // We reduce memory sizes for embedded systems (RPI and HTML5) + // NOTE: On HTML5 (emscripten) this is allocated on heap, + // by default it's only 16MB!...just take care... + DEFAULT_BATCH_BUFFER_ELEMENTS :: 2048 +} + +DEFAULT_BATCH_BUFFERS :: 1 // Default number of batch buffers (multi-buffering) +DEFAULT_BATCH_DRAWCALLS :: 256 // Default number of batch draw calls (by state changes: mode, texture) +DEFAULT_BATCH_MAX_TEXTURE_UNITS :: 4 // Maximum number of additional textures that can be activated on batch drawing (SetShaderValueTexture()) + +// Internal Matrix stack +MAX_MATRIX_STACK_SIZE :: 32 // Maximum size of Matrix stack + +// Shader limits +MAX_SHADER_LOCATIONS :: 32 // Maximum number of shader locations supported + +// Projection matrix culling +CULL_DISTANCE_NEAR :: 0.01 // Default near cull distance +CULL_DISTANCE_FAR :: 1000.0 // Default far cull distance + +// Texture parameters (equivalent to OpenGL defines) +TEXTURE_WRAP_S :: 0x2802 // GL_TEXTURE_WRAP_S +TEXTURE_WRAP_T :: 0x2803 // GL_TEXTURE_WRAP_T +TEXTURE_MAG_FILTER :: 0x2800 // GL_TEXTURE_MAG_FILTER +TEXTURE_MIN_FILTER :: 0x2801 // GL_TEXTURE_MIN_FILTER + +TEXTURE_FILTER_NEAREST :: 0x2600 // GL_NEAREST +TEXTURE_FILTER_LINEAR :: 0x2601 // GL_LINEAR +TEXTURE_FILTER_MIP_NEAREST :: 0x2700 // GL_NEAREST_MIPMAP_NEAREST +TEXTURE_FILTER_NEAREST_MIP_LINEAR :: 0x2702 // GL_NEAREST_MIPMAP_LINEAR +TEXTURE_FILTER_LINEAR_MIP_NEAREST :: 0x2701 // GL_LINEAR_MIPMAP_NEAREST +TEXTURE_FILTER_MIP_LINEAR :: 0x2703 // GL_LINEAR_MIPMAP_LINEAR +TEXTURE_FILTER_ANISOTROPIC :: 0x3000 // Anisotropic filter (custom identifier) + +TEXTURE_WRAP_REPEAT :: 0x2901 // GL_REPEAT +TEXTURE_WRAP_CLAMP :: 0x812F // GL_CLAMP_TO_EDGE +TEXTURE_WRAP_MIRROR_REPEAT :: 0x8370 // GL_MIRRORED_REPEAT +TEXTURE_WRAP_MIRROR_CLAMP :: 0x8742 // GL_MIRROR_CLAMP_EXT + +// Matrix modes (equivalent to OpenGL) +MODELVIEW :: 0x1700 // GL_MODELVIEW +PROJECTION :: 0x1701 // GL_PROJECTION +TEXTURE :: 0x1702 // GL_TEXTURE + +// Primitive assembly draw modes +LINES :: 0x0001 // GL_LINES +TRIANGLES :: 0x0004 // GL_TRIANGLES +QUADS :: 0x0007 // GL_QUADS + +// GL equivalent data types +UNSIGNED_BYTE :: 0x1401 // GL_UNSIGNED_BYTE +FLOAT :: 0x1406 // GL_FLOAT + +// Buffer usage hint +STREAM_DRAW :: 0x88E0 // GL_STREAM_DRAW +STREAM_READ :: 0x88E1 // GL_STREAM_READ +STREAM_COPY :: 0x88E2 // GL_STREAM_COPY +STATIC_DRAW :: 0x88E4 // GL_STATIC_DRAW +STATIC_READ :: 0x88E5 // GL_STATIC_READ +STATIC_COPY :: 0x88E6 // GL_STATIC_COPY +DYNAMIC_DRAW :: 0x88E8 // GL_DYNAMIC_DRAW +DYNAMIC_READ :: 0x88E9 // GL_DYNAMIC_READ +DYNAMIC_COPY :: 0x88EA // GL_DYNAMIC_COPY + +// GL Shader type +FRAGMENT_SHADER :: 0x8B30 // GL_FRAGMENT_SHADER +VERTEX_SHADER :: 0x8B31 // GL_VERTEX_SHADER +COMPUTE_SHADER :: 0x91B9 // GL_COMPUTE_SHADER + +// GL blending factors +ZERO :: 0 // GL_ZERO +ONE :: 1 // GL_ONE +SRC_COLOR :: 0x0300 // GL_SRC_COLOR +ONE_MINUS_SRC_COLOR :: 0x0301 // GL_ONE_MINUS_SRC_COLOR +SRC_ALPHA :: 0x0302 // GL_SRC_ALPHA +ONE_MINUS_SRC_ALPHA :: 0x0303 // GL_ONE_MINUS_SRC_ALPHA +DST_ALPHA :: 0x0304 // GL_DST_ALPHA +ONE_MINUS_DST_ALPHA :: 0x0305 // GL_ONE_MINUS_DST_ALPHA +DST_COLOR :: 0x0306 // GL_DST_COLOR +ONE_MINUS_DST_COLOR :: 0x0307 // GL_ONE_MINUS_DST_COLOR +SRC_ALPHA_SATURATE :: 0x0308 // GL_SRC_ALPHA_SATURATE +CONSTANT_COLOR :: 0x8001 // GL_CONSTANT_COLOR +ONE_MINUS_CONSTANT_COLOR :: 0x8002 // GL_ONE_MINUS_CONSTANT_COLOR +CONSTANT_ALPHA :: 0x8003 // GL_CONSTANT_ALPHA +ONE_MINUS_CONSTANT_ALPHA :: 0x8004 // GL_ONE_MINUS_CONSTANT_ALPHA + +// GL blending functions/equations +FUNC_ADD :: 0x8006 // GL_FUNC_ADD +MIN :: 0x8007 // GL_MIN +MAX :: 0x8008 // GL_MAX +FUNC_SUBTRACT :: 0x800A // GL_FUNC_SUBTRACT +FUNC_REVERSE_SUBTRACT :: 0x800B // GL_FUNC_REVERSE_SUBTRACT +BLEND_EQUATION :: 0x8009 // GL_BLEND_EQUATION +BLEND_EQUATION_RGB :: 0x8009 // GL_BLEND_EQUATION_RGB // (Same as BLEND_EQUATION) +BLEND_EQUATION_ALPHA :: 0x883D // GL_BLEND_EQUATION_ALPHA +BLEND_DST_RGB :: 0x80C8 // GL_BLEND_DST_RGB +BLEND_SRC_RGB :: 0x80C9 // GL_BLEND_SRC_RGB +BLEND_DST_ALPHA :: 0x80CA // GL_BLEND_DST_ALPHA +BLEND_SRC_ALPHA :: 0x80CB // GL_BLEND_SRC_ALPHA +BLEND_COLOR :: 0x8005 // GL_BLEND_COLOR + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + + +VertexBufferIndexType :: c.ushort when GRAPHICS_API_OPENGL_ES2 else c.uint + +// Dynamic vertex buffers (position + texcoords + colors + indices arrays) +VertexBuffer :: struct { + elementCount: c.int, // Number of elements in the buffer (QUADS) + + vertices: [^]f32, // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) + texcoords: [^]f32, // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) + colors: [^]u8, // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) + indices: [^]VertexBufferIndexType, // Vertex indices (in case vertex data comes indexed) (6 indices per quad) + vaoId: c.uint, // OpenGL Vertex Array Object id + vboId: [4]c.uint, // OpenGL Vertex Buffer Objects id (4 types of vertex data) +} + +// Draw call type +// NOTE: Only texture changes register a new draw, other state-change-related elements are not +// used at this moment (vaoId, shaderId, matrices), raylib just forces a batch draw call if any +// of those state-change happens (this is done in core module) +DrawCall :: struct { + mode: c.int, // Drawing mode: LINES, TRIANGLES, QUADS + vertexCount: c.int, // Number of vertex of the draw + vertexAlignment: c.int, // Number of vertex required for index alignment (LINES, TRIANGLES) + textureId: c.uint, // Texture id to be used on the draw -> Use to create new draw call if changes +} + +// RenderBatch type +RenderBatch :: struct { + bufferCount: c.int, // Number of vertex buffers (multi-buffering support) + currentBuffer: c.int, // Current buffer tracking in case of multi-buffering + vertexBuffer: [^]VertexBuffer, // Dynamic buffer(s) for vertex data + + draws: [^]DrawCall, // Draw calls array, depends on textureId + drawCounter: c.int, // Draw calls counter + currentDepth: f32, // Current depth value for next draw +} + +// OpenGL version +GlVersion :: enum c.int { + OPENGL_11 = 1, // OpenGL 1.1 + OPENGL_21, // OpenGL 2.1 (GLSL 120) + OPENGL_33, // OpenGL 3.3 (GLSL 330) + OPENGL_43, // OpenGL 4.3 (using GLSL 330) + OPENGL_ES_20, // OpenGL ES 2.0 (GLSL 100) + OPENGL_ES_30, // OpenGL ES 3.0 (GLSL 300 es) +} + +PixelFormat :: rl.PixelFormat +TextureFilter :: rl.TextureFilter +BlendMode :: rl.BlendMode +ShaderLocationIndex :: rl.ShaderLocationIndex +ShaderUniformDataType :: rl.ShaderUniformDataType + +// Shader attribute data types +ShaderAttributeDataType :: enum c.int { + FLOAT = 0, // Shader attribute type: float + VEC2, // Shader attribute type: vec2 (2 float) + VEC3, // Shader attribute type: vec3 (3 float) + VEC4, // Shader attribute type: vec4 (4 float) +} + +// Framebuffer attachment type +// NOTE: By default up to 8 color channels defined, but it can be more +FramebufferAttachType :: enum c.int { + COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 + COLOR_CHANNEL1 = 1, // Framebuffer attachment type: color 1 + COLOR_CHANNEL2 = 2, // Framebuffer attachment type: color 2 + COLOR_CHANNEL3 = 3, // Framebuffer attachment type: color 3 + COLOR_CHANNEL4 = 4, // Framebuffer attachment type: color 4 + COLOR_CHANNEL5 = 5, // Framebuffer attachment type: color 5 + COLOR_CHANNEL6 = 6, // Framebuffer attachment type: color 6 + COLOR_CHANNEL7 = 7, // Framebuffer attachment type: color 7 + DEPTH = 100, // Framebuffer attachment type: depth + STENCIL = 200, // Framebuffer attachment type: stencil +} + +// Framebuffer texture attachment type +FramebufferAttachTextureType :: enum c.int { + CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side + CUBEMAP_NEGATIVE_X = 1, // Framebuffer texture attachment type: cubemap, -X side + CUBEMAP_POSITIVE_Y = 2, // Framebuffer texture attachment type: cubemap, +Y side + CUBEMAP_NEGATIVE_Y = 3, // Framebuffer texture attachment type: cubemap, -Y side + CUBEMAP_POSITIVE_Z = 4, // Framebuffer texture attachment type: cubemap, +Z side + CUBEMAP_NEGATIVE_Z = 5, // Framebuffer texture attachment type: cubemap, -Z side + TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d + RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer +} + +CullMode :: enum c.int { + FRONT = 0, + BACK, +} + +Matrix :: rl.Matrix + +@(default_calling_convention="c", link_prefix="rl") +foreign lib { + //------------------------------------------------------------------------------------ + // Functions Declaration - Matrix operations + //------------------------------------------------------------------------------------ + MatrixMode :: proc(mode: c.int) --- // Choose the current matrix to be transformed + PushMatrix :: proc() --- // Push the current matrix to stack + PopMatrix :: proc() --- // Pop lattest inserted matrix from stack + LoadIdentity :: proc() --- // Reset current matrix to identity matrix + Translatef :: proc(x, y, z: f32) --- // Multiply the current matrix by a translation matrix + Rotatef :: proc(angleDeg: f32, x, y, z: f32) --- // Multiply the current matrix by a rotation matrix + Scalef :: proc(x, y, z: f32) --- // Multiply the current matrix by a scaling matrix + MultMatrixf :: proc(matf: [^]f32) --- // Multiply the current matrix by another matrix + Frustum :: proc(left, right, bottom, top, znear, zfar: f64) --- + Ortho :: proc(left, right, bottom, top, znear, zfar: f64) --- + Viewport :: proc(x, y, width, height: c.int) --- // Set the viewport area + + //------------------------------------------------------------------------------------ + // Functions Declaration - Vertex level operations + //------------------------------------------------------------------------------------ + Begin :: proc(mode: c.int) --- // Initialize drawing mode (how to organize vertex) + End :: proc() --- // Finish vertex providing + Vertex2i :: proc(x, y: c.int) --- // Define one vertex (position) - 2 int + Vertex2f :: proc(x, y: f32) --- // Define one vertex (position) - 2 f32 + Vertex3f :: proc(x, y, z: f32) --- // Define one vertex (position) - 3 f32 + TexCoord2f :: proc(x, y: f32) --- // Define one vertex (texture coordinate) - 2 f32 + Normal3f :: proc(x, y, z: f32) --- // Define one vertex (normal) - 3 f32 + Color4ub :: proc(r, g, b, a: u8) --- // Define one vertex (color) - 4 byte + Color3f :: proc(x, y, z: f32) --- // Define one vertex (color) - 3 f32 + Color4f :: proc(x, y, z, w: f32) --- // Define one vertex (color) - 4 f32 + + //------------------------------------------------------------------------------------ + // Functions Declaration - OpenGL style functions (common to 1.1, 3.3+, ES2) + // NOTE: This functions are used to completely abstract raylib code from OpenGL layer, + // some of them are direct wrappers over OpenGL calls, some others are custom + //------------------------------------------------------------------------------------ + + // Vertex buffers state + EnableVertexArray :: proc(vaoId: c.uint) -> bool --- // Enable vertex array (VAO, if supported) + DisableVertexArray :: proc() --- // Disable vertex array (VAO, if supported) + EnableVertexBuffer :: proc(id: c.uint) --- // Enable vertex buffer (VBO) + DisableVertexBuffer :: proc() --- // Disable vertex buffer (VBO) + EnableVertexBufferElement :: proc(id: c.uint) --- // Enable vertex buffer element (VBO element) + DisableVertexBufferElement :: proc() --- // Disable vertex buffer element (VBO element) + EnableVertexAttribute :: proc(index: c.uint) --- // Enable vertex attribute index + DisableVertexAttribute :: proc(index: c.uint) --- // Disable vertex attribute index + when GRAPHICS_API_OPENGL_11 { + EnableStatePointer :: proc(vertexAttribType: c.int, buffer: rawptr) --- + DisableStatePointer :: proc(vertexAttribType: c.int) --- + } + + // Textures state + ActiveTextureSlot :: proc(slot: c.int) --- // Select and active a texture slot + EnableTexture :: proc(id: c.uint) --- // Enable texture + DisableTexture :: proc() --- // Disable texture + EnableTextureCubemap :: proc(id: c.uint) --- // Enable texture cubemap + DisableTextureCubemap :: proc() --- // Disable texture cubemap + TextureParameters :: proc(id: c.uint, param: c.int, value: c.int) --- // Set texture parameters (filter, wrap) + CubemapParameters :: proc(id: i32, param: c.int, value: c.int) --- // Set cubemap parameters (filter, wrap) + + // Shader state + EnableShader :: proc(id: c.uint) --- // Enable shader program + DisableShader :: proc() --- // Disable shader program + + // Framebuffer state + EnableFramebuffer :: proc(id: c.uint) --- // Enable render texture (fbo) + DisableFramebuffer :: proc() --- // Disable render texture (fbo), return to default framebuffer + ActiveDrawBuffers :: proc(count: c.int) --- // Activate multiple draw color buffers + BlitFramebuffer :: proc(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight, bufferMask: c.int) --- // Blit active framebuffer to main framebuffer + + // General render state + DisableColorBlend :: proc() --- // Disable color blending + EnableDepthTest :: proc() --- // Enable depth test + DisableDepthTest :: proc() --- // Disable depth test + EnableDepthMask :: proc() --- // Enable depth write + DisableDepthMask :: proc() --- // Disable depth write + EnableBackfaceCulling :: proc() --- // Enable backface culling + DisableBackfaceCulling :: proc() --- // Disable backface culling + SetCullFace :: proc(mode: CullMode) --- // Set face culling mode + EnableScissorTest :: proc() --- // Enable scissor test + DisableScissorTest :: proc() --- // Disable scissor test + Scissor :: proc(x, y, width, height: c.int) --- // Scissor test + EnableWireMode :: proc() --- // Enable wire mode + EnablePointMode :: proc() --- // Enable point mode + DisableWireMode :: proc() --- // Disable wire and point modes + SetLineWidth :: proc(width: f32) --- // Set the line drawing width + GetLineWidth :: proc() -> f32 --- // Get the line drawing width + EnableSmoothLines :: proc() --- // Enable line aliasing + DisableSmoothLines :: proc() --- // Disable line aliasing + EnableStereoRender :: proc() --- // Enable stereo rendering + DisableStereoRender :: proc() --- // Disable stereo rendering + IsStereoRenderEnabled :: proc() -> bool --- // Check if stereo render is enabled + + + ClearColor :: proc(r, g, b, a: u8) --- // Clear color buffer with color + ClearScreenBuffers :: proc() --- // Clear used screen buffers (color and depth) + CheckErrors :: proc() --- // Check and log OpenGL error codes + SetBlendMode :: proc(mode: c.int) --- // Set blending mode + SetBlendFactors :: proc(glSrcFactor, glDstFactor, glEquation: c.int) --- // Set blending mode factor and equation (using OpenGL factors) + SetBlendFactorsSeparate :: proc(glSrcRGB, glDstRGB, glSrcAlpha, glDstAlpha, glEqRGB, glEqAlpha: c.int) --- // Set blending mode factors and equations separately (using OpenGL factors) + + //------------------------------------------------------------------------------------ + // Functions Declaration - rlgl functionality + //------------------------------------------------------------------------------------ + // rlgl initialization functions + @(link_prefix="rlgl") + Init :: proc(width, height: c.int) --- // Initialize rlgl (buffers, shaders, textures, states) + @(link_prefix="rlgl") + Close :: proc() --- // De-initialize rlgl (buffers, shaders, textures) + LoadExtensions :: proc(loader: rawptr) --- // Load OpenGL extensions (loader function required) + GetVersion :: proc() -> GlVersion --- // Get current OpenGL version + SetFramebufferWidth :: proc(width: c.int) --- // Set current framebuffer width + GetFramebufferWidth :: proc() -> c.int --- // Get default framebuffer width + SetFramebufferHeight :: proc(height: c.int) --- // Set current framebuffer height + GetFramebufferHeight :: proc() -> c.int --- // Get default framebuffer height + + + GetTextureIdDefault :: proc() -> c.uint --- // Get default texture id + GetShaderIdDefault :: proc() -> c.uint --- // Get default shader id + GetShaderLocsDefault :: proc() -> [^]c.int --- // Get default shader locations + + // Render batch management + // NOTE: rlgl provides a default render batch to behave like OpenGL 1.1 immediate mode + // but this render batch API is exposed in case of custom batches are required + LoadRenderBatch :: proc(numBuffers, bufferElements: c.int) -> RenderBatch --- // Load a render batch system + UnloadRenderBatch :: proc(batch: RenderBatch) --- // Unload render batch system + DrawRenderBatch :: proc(batch: ^RenderBatch) --- // Draw render batch data (Update->Draw->Reset) + SetRenderBatchActive :: proc(batch: ^RenderBatch) --- // Set the active render batch for rlgl (NULL for default internal) + DrawRenderBatchActive :: proc() --- // Update and draw internal render batch + CheckRenderBatchLimit :: proc(vCount: c.int) -> c.int --- // Check internal buffer overflow for a given number of vertex + + SetTexture :: proc(id: c.uint) --- // Set current texture for render batch and check buffers limits + + //------------------------------------------------------------------------------------------------------------------------ + + // Vertex buffers management + LoadVertexArray :: proc() -> c.uint --- // Load vertex array (vao) if supported + LoadVertexBuffer :: proc(buffer: rawptr, size: c.int, is_dynamic: bool) -> c.uint --- // Load a vertex buffer attribute + LoadVertexBufferElement :: proc(buffer: rawptr, size: c.int, is_dynamic: bool) -> c.uint --- // Load a new attributes element buffer + UpdateVertexBuffer :: proc(bufferId: c.uint, data: rawptr, dataSize: c.int, offset: c.int) --- // Update GPU buffer with new data + UpdateVertexBufferElements :: proc(id: c.uint, data: rawptr, dataSize: c.int, offset: c.int) --- // Update vertex buffer elements with new data + UnloadVertexArray :: proc(vaoId: c.uint) --- + UnloadVertexBuffer :: proc(vboId: c.uint) --- + SetVertexAttribute :: proc(index: c.uint, compSize: c.int, type: c.int, normalized: bool, stride: c.int, pointer: rawptr) --- + SetVertexAttributeDivisor :: proc(index: c.uint, divisor: c.int) --- + SetVertexAttributeDefault :: proc(locIndex: c.int, value: rawptr, attribType: c.int, count: c.int) --- // Set vertex attribute default value + DrawVertexArray :: proc(offset: c.int, count: c.int) --- + DrawVertexArrayElements :: proc(offset: c.int, count: c.int, buffer: rawptr) --- + DrawVertexArrayInstanced :: proc(offset: c.int, count: c.int, instances: c.int) --- + DrawVertexArrayElementsInstanced :: proc(offset: c.int, count: c.int, buffer: rawptr, instances: c.int) --- + + // Textures management + LoadTexture :: proc(data: rawptr, width, height: c.int, format: c.int, mipmapCount: c.int) -> c.uint --- // Load texture in GPU + LoadTextureDepth :: proc(width, height: c.int, useRenderBuffer: bool) -> c.uint --- // Load depth texture/renderbuffer (to be attached to fbo) + LoadTextureCubemap :: proc(data: rawptr, size: c.int, format: c.int) -> c.uint --- // Load texture cubemap + UpdateTexture :: proc(id: c.uint, offsetX, offsetY: c.int, width, height: c.int, format: c.int, data: rawptr) --- // Update GPU texture with new data + GetGlTextureFormats :: proc(format: c.int, glInternalFormat, glFormat, glType: ^c.uint) --- // Get OpenGL internal formats + GetPixelFormatName :: proc(format: c.uint) -> cstring --- // Get name string for pixel format + UnloadTexture :: proc(id: c.uint) --- // Unload texture from GPU memory + GenTextureMipmaps :: proc(id: c.uint, width, height: c.int, format: c.int, mipmaps: ^c.int) --- // Generate mipmap data for selected texture + ReadTexturePixels :: proc(id: c.uint, width, height: c.int, format: c.int) -> rawptr --- // Read texture pixel data + ReadScreenPixels :: proc(width, height: c.int) -> [^]byte --- // Read screen pixel data (color buffer) + + // Framebuffer management (fbo) + LoadFramebuffer :: proc(width, height: c.int) -> c.uint --- // Load an empty framebuffer + FramebufferAttach :: proc(fboId, texId: c.uint, attachType: c.int, texType: c.int, mipLevel: c.int) --- // Attach texture/renderbuffer to a framebuffer + FramebufferComplete :: proc(id: c.uint) -> bool --- // Verify framebuffer is complete + UnloadFramebuffer :: proc(id: c.uint) --- // Delete framebuffer from GPU + + // Shaders management + LoadShaderCode :: proc(vsCode, fsCode: cstring) -> c.uint --- // Load shader from code strings + CompileShader :: proc(shaderCode: cstring, type: c.int) -> c.uint --- // Compile custom shader and return shader id (type: VERTEX_SHADER, FRAGMENT_SHADER, COMPUTE_SHADER) + LoadShaderProgram :: proc(vShaderId, fShaderId: c.uint) -> c.uint --- // Load custom shader program + UnloadShaderProgram :: proc(id: c.uint) --- // Unload shader program + GetLocationUniform :: proc(shaderId: c.uint, uniformName: cstring) -> c.int --- // Get shader location uniform + GetLocationAttrib :: proc(shaderId: c.uint, attribName: cstring) -> c.int --- // Get shader location attribute + SetUniform :: proc(locIndex: c.int, value: rawptr, uniformType: c.int, count: c.int) --- // Set shader value uniform + SetUniformMatrix :: proc(locIndex: c.int, mat: Matrix) --- // Set shader value matrix + SetUniformSampler :: proc(locIndex: c.int, textureId: c.uint) --- // Set shader value sampler + SetShader :: proc(id: c.uint, locs: [^]c.int) --- // Set shader currently active (id and locations) + + // Compute shader management + LoadComputeShaderProgram :: proc(shaderId: c.uint) -> c.uint --- // Load compute shader program + ComputeShaderDispatch :: proc(groupX, groupY, groupZ: c.uint) --- // Dispatch compute shader (equivalent to *draw* for graphics pipeline) + + // Shader buffer storage object management (ssbo) + LoadShaderBuffer :: proc(size: c.uint, data: rawptr, usageHint: c.int) -> c.uint --- // Load shader storage buffer object (SSBO) + UnloadShaderBuffer :: proc(ssboId: c.uint) --- // Unload shader storage buffer object (SSBO) + UpdateShaderBuffer :: proc(id: c.uint, data: rawptr, dataSize: c.uint, offset: c.uint) --- // Update SSBO buffer data + BindShaderBuffer :: proc(id: c.uint, index: c.uint) --- // Bind SSBO buffer + ReadShaderBuffer :: proc(id: c.uint, dest: rawptr, count: c.uint, offset: c.uint) --- // Read SSBO buffer data (GPU->CPU) + CopyShaderBuffer :: proc(destId, srcId: c.uint, destOffset, srcOffset: c.uint, count: c.uint) --- // Copy SSBO data between buffers + GetShaderBufferSize :: proc(id: c.uint) -> c.uint --- // Get SSBO buffer size + + // Buffer management + BindImageTexture :: proc(id: c.uint, index: c.uint, format: c.int, readonly: bool) --- // Bind image texture + + // Matrix state management + GetMatrixModelview :: proc() -> Matrix --- // Get internal modelview matrix + GetMatrixProjection :: proc() -> Matrix --- // Get internal projection matrix + GetMatrixTransform :: proc() -> Matrix --- // Get internal accumulated transform matrix + GetMatrixProjectionStereo :: proc(eye: c.int) -> Matrix --- // Get internal projection matrix for stereo render (selected eye) + GetMatrixViewOffsetStereo :: proc(eye: c.int) -> Matrix --- // Get internal view offset matrix for stereo render (selected eye) + SetMatrixProjection :: proc(proj: Matrix) --- // Set a custom projection matrix (replaces internal projection matrix) + SetMatrixModelview :: proc(view: Matrix) --- // Set a custom modelview matrix (replaces internal modelview matrix) + SetMatrixProjectionStereo :: proc(right, left: Matrix) --- // Set eyes projection matrices for stereo rendering + SetMatrixViewOffsetStereo :: proc(right, left: Matrix) --- // Set eyes view offsets matrices for stereo rendering + + // Quick and dirty cube/quad buffers load->draw->unload + LoadDrawCube :: proc() --- // Load and draw a cube + LoadDrawQuad :: proc() --- // Load and draw a quad +}