Compare commits
29 Commits
7359649465
...
0ee8e3904a
Author | SHA1 | Date |
---|---|---|
|
0ee8e3904a | |
|
27b4626907 | |
|
9e58435637 | |
|
6469fe5a0f | |
|
0f5d46f5a4 | |
|
87d0eb9cf3 | |
|
b834dbb451 | |
|
b2bdc515ec | |
|
9b927bd410 | |
|
bd7dac90ac | |
|
0e2b8d0df8 | |
|
e040b1a29b | |
|
c194053b83 | |
|
e4a4910e6b | |
|
33c3428796 | |
|
dfce9bf369 | |
|
f3dc516704 | |
|
d926166630 | |
|
52124edcd8 | |
|
dadb7dc49a | |
|
84da2da350 | |
|
cbbd83affc | |
|
c81d9b4e97 | |
|
d0a18fcdb7 | |
|
7ce0a7d698 | |
|
b651847ba3 | |
|
e4b4e5c943 | |
|
911df4ce05 | |
|
f2abe27704 |
54
README.md
54
README.md
|
@ -26,5 +26,55 @@ Welcome to the 4coder community repository.
|
|||
1. `$ cd 4cc/code`
|
||||
2. `$ ./bin/build-linux.sh`
|
||||
|
||||
## Mac (Untested)
|
||||
1. The steps should be the same as linux but replace the `*-linux.sh` with their `*-mac.sh` equivalents.
|
||||
## Mac
|
||||
|
||||
> 4coder targets x86_64. If you are using a M1+ ARM CPU you need to prefix the build scripts commands with: `arch -arch x86_64`
|
||||
|
||||
1. Use the `package-mac.sh` script from the code directory (this builds a distribution in the `distributions` directory with all the non-binary dependencies)
|
||||
1. `$ cd 4cc/code`
|
||||
2. `$ ./bin/package-mac.sh`
|
||||
|
||||
2. You can also use the `build-mac.sh` script if you want just build the binaries, (this produces the build artifacts in the `build` directory, this doesn't produce a functional distribution)
|
||||
1. `$ cd 4cc/code`
|
||||
2. `$ ./bin/build-mac.sh`
|
||||
|
||||
### Older Macs, 10.15.7 Catalina
|
||||
|
||||
If you are using an older version of mac, such as 10.15.7 Catalina you need to install the realpath command:
|
||||
|
||||
1. `$ sudo port install coreutils`
|
||||
2. macports names the `realpath` command `grealpath`, so make a symbolic link in order to use build-mac.sh:
|
||||
`$ sudo ln -s /opt/local/bin/grealpath /opt/local/bin/realpath`
|
||||
|
||||
## Build script parameter
|
||||
|
||||
The build script accepts a parameter (mutually exclusive):
|
||||
- `/DDEV_BUILD` or `/DDEV_BUILD_X86` (default value) : build without optimizations.
|
||||
Produces debug symbols.
|
||||
Defines: `FRED_INTERNAL`, `FRED_SUPER`, `DO_CRAZY_EXPENSIVE_ASSERTS` (on Windows) macros.
|
||||
- `/DOPT_BUILD` or `/DOPT_BUILD_X86` (similar to `build_optimized` script): build with optimizations.
|
||||
Doesn't produce debug symbols.
|
||||
Defines `FRED_SUPER` macro.
|
||||
- `/DPACKAGE_SUPER_X64` or `/DPACKAGE_SUPER_X86` (similar to `package` script): package 4coder for distribution.
|
||||
Turns on optimizations.
|
||||
Produces debug symbols.
|
||||
Defines `SHIP_MODE`, `FRED_SUPER`, `DO_CRAZY_EXPENSIVE_ASSERTS` (on Windows) macros.
|
||||
- `/DPACKAGE_DEMO_X64` or `/DPACKAGE_DEMO_X86`: packages 4coder demo for distribution.
|
||||
Turns on optimizations.
|
||||
Produces debug symbols.
|
||||
Defines `SHIP_MODE`, `DO_CRAZY_EXPENSIVE_ASSERTS` (on Windows) macros.
|
||||
|
||||
## API generators
|
||||
|
||||
4coder uses several small programs to generate some headers and source files. Those do not run automatically, you must build them and run them when needed (which shouldn't really happen).
|
||||
|
||||
- `code\4ed_font_api.cpp` creates, in `code\generated`, `font_api.h`, `font_api.cpp`, `font_api_constructor.cpp` (not used) and `font_api_master_list.h` (not used);
|
||||
- `code\4ed_graphics_api.cpp` creates, in `code\generated`, `graphics_api.h` and `graphics_api.cpp`, `graphics_api_constructor.cpp` (not used) and `graphics_api_master_list.h` (not used);
|
||||
- `code\4ed_system_api.cpp` creates, in `code\custom\generated`, `system_api.h`, `system_api.cpp`, `system_api_constructor.cpp`, `system_api_master_list.h`;
|
||||
- `code\4ed_api_parser_main.cpp` is a little different, as it parses source files passed as parameters to search for functions and type preceded by `api(some_api_name)` and creates 4 files in the `code\custom\generated`. It is used to generate `custom_api.h`, `custom_api.cpp`, `custom_api_constructor.cpp` and `custom_api_master_list.h` by passing `code\4ed_api_implementation.cpp` as a parameter.
|
||||
|
||||
You need to compile one of those file and run it from the `code` directory.
|
||||
|
||||
There is also `code\4ed_api_check.cpp` to verify the generated file but it's not clear at the moment what to check against.
|
||||
|
||||
- `code\4ed_generate_keycodes.cpp` is also a bit appart as it generates `code\custom\generated\4coder_event_codes.h` which are keyboard key codes and some event hook ids.
|
||||
|
|
|
@ -208,7 +208,10 @@ api_get_callable_name(Arena *arena, String_Const_u8 api_name, String_Const_u8 na
|
|||
////////////////////////////////
|
||||
|
||||
function void
|
||||
generate_api_master_list(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FILE *out){
|
||||
generate_api_master_list(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FILE *out, String_Const_u8 generated_by){
|
||||
|
||||
fprintf(out, "/* Generated by \"%.*s\" */\n\n", string_expand(generated_by));
|
||||
|
||||
for (API_Call *call = api->first_call;
|
||||
call != 0;
|
||||
call = call->next){
|
||||
|
@ -236,7 +239,10 @@ generate_api_master_list(Arena *scratch, API_Definition *api, API_Generation_Fla
|
|||
}
|
||||
|
||||
function void
|
||||
generate_header(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FILE *out){
|
||||
generate_header(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FILE *out, String_Const_u8 generated_by){
|
||||
|
||||
fprintf(out, "/* Generated by \"%.*s\" */\n\n", string_expand(generated_by));
|
||||
|
||||
for (API_Call *call = api->first_call;
|
||||
call != 0;
|
||||
call = call->next){
|
||||
|
@ -264,6 +270,8 @@ generate_header(Arena *scratch, API_Definition *api, API_Generation_Flag flags,
|
|||
fprintf(out, ")\n");
|
||||
}
|
||||
|
||||
fprintf(out, "\n");
|
||||
|
||||
for (API_Call *call = api->first_call;
|
||||
call != 0;
|
||||
call = call->next){
|
||||
|
@ -289,6 +297,8 @@ generate_header(Arena *scratch, API_Definition *api, API_Generation_Flag flags,
|
|||
fprintf(out, ");\n");
|
||||
}
|
||||
|
||||
fprintf(out, "\n");
|
||||
|
||||
fprintf(out, "struct API_VTable_%.*s{\n", string_expand(api->name));
|
||||
for (API_Call *call = api->first_call;
|
||||
call != 0;
|
||||
|
@ -302,6 +312,8 @@ generate_header(Arena *scratch, API_Definition *api, API_Generation_Flag flags,
|
|||
}
|
||||
fprintf(out, "};\n");
|
||||
|
||||
fprintf(out, "\n");
|
||||
|
||||
fprintf(out, "#if defined(STATIC_LINK_API)\n");
|
||||
for (API_Call *call = api->first_call;
|
||||
call != 0;
|
||||
|
@ -328,6 +340,7 @@ generate_header(Arena *scratch, API_Definition *api, API_Generation_Flag flags,
|
|||
fprintf(out, ");\n");
|
||||
}
|
||||
fprintf(out, "#undef STATIC_LINK_API\n");
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, "#elif defined(DYNAMIC_LINK_API)\n");
|
||||
for (API_Call *call = api->first_call;
|
||||
call != 0;
|
||||
|
@ -343,7 +356,10 @@ generate_header(Arena *scratch, API_Definition *api, API_Generation_Flag flags,
|
|||
}
|
||||
|
||||
function void
|
||||
generate_cpp(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FILE *out){
|
||||
generate_cpp(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FILE *out, String_Const_u8 generated_by){
|
||||
|
||||
fprintf(out, "/* Generated by \"%.*s\" */\n\n", string_expand(generated_by));
|
||||
|
||||
fprintf(out, "function void\n");
|
||||
fprintf(out, "%.*s_api_fill_vtable(API_VTable_%.*s *vtable){\n",
|
||||
string_expand(api->name),
|
||||
|
@ -357,6 +373,7 @@ generate_cpp(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FIL
|
|||
string_expand(callable_name));
|
||||
}
|
||||
fprintf(out, "}\n");
|
||||
fprintf(out, "\n");
|
||||
|
||||
fprintf(out, "#if defined(DYNAMIC_LINK_API)\n");
|
||||
fprintf(out, "function void\n");
|
||||
|
@ -377,7 +394,10 @@ generate_cpp(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FIL
|
|||
}
|
||||
|
||||
function void
|
||||
generate_constructor(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FILE *out){
|
||||
generate_constructor(Arena *scratch, API_Definition *api, API_Generation_Flag flags, FILE *out, String_Const_u8 generated_by){
|
||||
|
||||
fprintf(out, "/* Generated by \"%.*s\" */\n\n", string_expand(generated_by));
|
||||
|
||||
fprintf(out, "function API_Definition*\n");
|
||||
fprintf(out, "%.*s_api_construct(Arena *arena){\n",
|
||||
string_expand(api->name));
|
||||
|
@ -418,7 +438,7 @@ generate_constructor(Arena *scratch, API_Definition *api, API_Generation_Flag fl
|
|||
////////////////////////////////
|
||||
|
||||
function b32
|
||||
api_definition_generate_api_includes(Arena *arena, API_Definition *api, Generated_Group group, API_Generation_Flag flags){
|
||||
api_definition_generate_api_includes(Arena *arena, API_Definition *api, Generated_Group group, API_Generation_Flag flags, String_Const_u8 generated_by){
|
||||
// NOTE(allen): Arrange output files
|
||||
|
||||
String_Const_u8 path_to_self = string_u8_litexpr(__FILE__);
|
||||
|
@ -494,10 +514,10 @@ api_definition_generate_api_includes(Arena *arena, API_Definition *api, Generate
|
|||
|
||||
// NOTE(allen): Generate output
|
||||
|
||||
generate_api_master_list(arena, api, flags, out_file_ml);
|
||||
generate_header(arena, api, flags, out_file_h);
|
||||
generate_cpp(arena, api, flags, out_file_cpp);
|
||||
generate_constructor(arena, api, flags, out_file_con);
|
||||
generate_api_master_list(arena, api, flags, out_file_ml, generated_by);
|
||||
generate_header(arena, api, flags, out_file_h, generated_by);
|
||||
generate_cpp(arena, api, flags, out_file_cpp, generated_by);
|
||||
generate_constructor(arena, api, flags, out_file_con, generated_by);
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ int
|
|||
main(void){
|
||||
Arena arena = make_arena_malloc();
|
||||
API_Definition *api = define_api(&arena);
|
||||
if (!api_definition_generate_api_includes(&arena, api, get_api_group(), 0)){
|
||||
if (!api_definition_generate_api_includes(&arena, api, get_api_group(), 0, SCu8(GENERATED_BY))){
|
||||
return(1);
|
||||
}
|
||||
return(0);
|
||||
|
|
|
@ -37,9 +37,21 @@ main(int argc, char **argv){
|
|||
exit(1);
|
||||
}
|
||||
|
||||
String_Const_u8 exe = SCu8("code/4ed_api_parser_main.exe");
|
||||
u32 command_line_length = exe.size;
|
||||
|
||||
for (i32 i = 1; i < argc; i+=1){
|
||||
command_line_length += 1 + cstring_length(argv[i]);
|
||||
}
|
||||
|
||||
String_u8 command_line = string_u8_push(&arena, command_line_length );
|
||||
string_append(&command_line, exe);
|
||||
|
||||
API_Definition_List list = {};
|
||||
for (i32 i = 1; i < argc; i += 1){
|
||||
char *file_name = argv[i];
|
||||
string_append_character(&command_line, ' ');
|
||||
string_append(&command_line, SCu8(argv[i]));
|
||||
FILE *file = fopen(file_name, "rb");
|
||||
if (file == 0){
|
||||
printf("error: could not open input file: '%s'\n", argv[i]);
|
||||
|
@ -57,7 +69,7 @@ main(int argc, char **argv){
|
|||
for (API_Definition *node = list.first;
|
||||
node != 0;
|
||||
node = node->next){
|
||||
api_definition_generate_api_includes(&arena, node, GeneratedGroup_Custom, APIGeneration_NoAPINameOnCallables);
|
||||
api_definition_generate_api_includes(&arena, node, GeneratedGroup_Custom, APIGeneration_NoAPINameOnCallables, SCu8(command_line));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -368,6 +368,9 @@ edit_change_current_history_state(Thread_Context *tctx, Models *models, Editing_
|
|||
}
|
||||
|
||||
file->state.current_record_index = current;
|
||||
if (file->state.saved_record_index == current){
|
||||
RemFlag(file->state.dirty, DirtyState_UnsavedChanges);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -155,6 +155,7 @@ save_file_to_name(Thread_Context *tctx, Models *models, Editing_File *file, u8 *
|
|||
File_Attributes new_attributes = system_save_file(scratch, (char*)file_name, saveable_string);
|
||||
if (new_attributes.last_write_time > 0 &&
|
||||
using_actual_file_name){
|
||||
file->state.saved_record_index = file->state.current_record_index;
|
||||
file->state.save_state = FileSaveState_SavedWaitingForNotification;
|
||||
file_clear_dirty_flags(file);
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ struct Editing_File_State{
|
|||
History history;
|
||||
i32 current_record_index;
|
||||
|
||||
i32 saved_record_index;
|
||||
|
||||
Dirty_State dirty;
|
||||
File_Save_State save_state;
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
// TOP
|
||||
|
||||
#define GENERATED_BY "code/4ed_font_api.cpp"
|
||||
|
||||
#include "4ed_api_definition_main.cpp"
|
||||
|
||||
function API_Definition*
|
||||
|
|
|
@ -125,27 +125,50 @@ ft__bad_rect_pack_end_line(Bad_Rect_Pack *pack){
|
|||
|
||||
internal Vec3_i32
|
||||
ft__bad_rect_pack_next(Bad_Rect_Pack *pack, Vec2_i32 dim){
|
||||
|
||||
Vec3_i32 result = { };
|
||||
|
||||
// NOTE(simon, 28/02/24): Does this character fit in the texture if it's the only character ?
|
||||
if ( dim.x <= pack->max_dim.x && dim.y <= pack->max_dim.y ){
|
||||
if (pack->current_line_h < dim.y){
|
||||
pack->current_line_h = dim.y;
|
||||
|
||||
b8 end_line = false;
|
||||
|
||||
if ( pack->p.x + dim.x > pack->max_dim.x ) {
|
||||
// NOTE(simon, 28/02/24): Can't fit the character horizontally.
|
||||
end_line = true;
|
||||
}
|
||||
if (pack->current_line_h > pack->max_dim.y){
|
||||
|
||||
if ( pack->current_line_h < dim.y && pack->p.y + dim.y > pack->max_dim.y ) {
|
||||
// NOTE(simon, 28/02/24): Character doesn't fit in the current line height, AND we
|
||||
// can't grow the line height.
|
||||
end_line = true;
|
||||
}
|
||||
|
||||
if ( end_line ) {
|
||||
ft__bad_rect_pack_end_line( pack );
|
||||
}
|
||||
|
||||
if ( pack->p.y + dim.y > pack->max_dim.y ) {
|
||||
Assert( end_line );
|
||||
// NOTE(simon, 28/02/24): We ended a line. There isn't enough space on a new line to
|
||||
// fit the character vertically. We need to go to the next texture in the array.
|
||||
// In a new texture the character is guaranteed to fit, because of the outer most if.
|
||||
pack->p.y = 0;
|
||||
pack->dim.z += 1;
|
||||
pack->p.z += 1;
|
||||
|
||||
// NOTE(simon, 28/02/24): There are no checks on the z axis range, but texture arrays
|
||||
// have a limit. At the moment it's 2048 on both OpenGL and DirectX.
|
||||
}
|
||||
else{
|
||||
if (pack->p.x + dim.x > pack->max_dim.x){
|
||||
ft__bad_rect_pack_end_line(pack);
|
||||
}
|
||||
|
||||
// NOTE(simon, 28/02/24): We are now sure that the character will fit.
|
||||
pack->current_line_h = Max(pack->current_line_h, dim.y);
|
||||
|
||||
result = pack->p;
|
||||
pack->p.x += dim.x;
|
||||
pack->current_line_h = Max(pack->current_line_h, dim.y);
|
||||
pack->dim.x = clamp_bot(pack->dim.x, pack->p.x);
|
||||
}
|
||||
pack->dim.x = Max( pack->dim.x, pack->p.x );
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
|
@ -306,6 +329,11 @@ ft__font_make_face(Arena *arena, Face_Description *description, f32 scale_factor
|
|||
|
||||
Texture_Kind texture_kind = TextureKind_Mono;
|
||||
u32 texture = graphics_get_texture(pack.dim, texture_kind);
|
||||
|
||||
/* NOTE simon (06/01/25): This assumes that every platforms don't use 0 as a valid texture id.
|
||||
This is valid for OpenGL and the DX11 implementaion. Someone needs to check the MAC versions. */
|
||||
if (texture != 0 ){
|
||||
|
||||
face->texture_kind = texture_kind;
|
||||
face->texture = texture;
|
||||
|
||||
|
@ -320,7 +348,6 @@ ft__font_make_face(Arena *arena, Face_Description *description, f32 scale_factor
|
|||
face->white.uv.y1 = (face->white.uv.y0 + face->white.uv.y1)/texture_dim.y;
|
||||
face->white.uv.x0 = face->white.uv.x0/texture_dim.x;
|
||||
face->white.uv.y0 = face->white.uv.y0/texture_dim.y;
|
||||
face->white.w /= texture_dim.z;
|
||||
}
|
||||
|
||||
for (u16 i = 0; i < index_count; i += 1){
|
||||
|
@ -331,7 +358,6 @@ ft__font_make_face(Arena *arena, Face_Description *description, f32 scale_factor
|
|||
face->bounds[i].uv.y1 = (face->bounds[i].uv.y0 + face->bounds[i].uv.y1)/texture_dim.y;
|
||||
face->bounds[i].uv.x0 = face->bounds[i].uv.x0/texture_dim.x;
|
||||
face->bounds[i].uv.y0 = face->bounds[i].uv.y0/texture_dim.y;
|
||||
face->bounds[i].w /= texture_dim.z;
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -360,6 +386,11 @@ ft__font_make_face(Arena *arena, Face_Description *description, f32 scale_factor
|
|||
26*met->normal_uppercase_advance +
|
||||
10*met->decimal_digit_advance)/62.f;
|
||||
}
|
||||
|
||||
} else {
|
||||
pop_array(arena, Face, 1);
|
||||
face = 0;
|
||||
}
|
||||
}
|
||||
|
||||
FT_Done_FreeType(ft);
|
||||
|
@ -368,4 +399,3 @@ ft__font_make_face(Arena *arena, Face_Description *description, f32 scale_factor
|
|||
}
|
||||
|
||||
// BOTTOM
|
||||
|
||||
|
|
|
@ -153,6 +153,9 @@ font_set_modify_face(Font_Set *set, Face_ID id, Face_Description *description){
|
|||
Arena arena = make_arena_system();
|
||||
Face *face = font_make_face(&arena, description, set->scale_factor);
|
||||
if (face != 0){
|
||||
if (slot->face->texture != 0){
|
||||
graphics_free_texture(slot->face->texture);
|
||||
}
|
||||
linalloc_clear(&slot->arena);
|
||||
slot->arena = arena;
|
||||
slot->face = face;
|
||||
|
|
|
@ -188,6 +188,9 @@ main(void){
|
|||
exit(1);
|
||||
}
|
||||
|
||||
fprintf(out, "/* Generated by: " __FILE__ );
|
||||
fprintf(out, " */\n" );
|
||||
|
||||
generate_codes(&arena, &key_list, out);
|
||||
generate_codes(&arena, &mouse_list, out);
|
||||
generate_codes(&arena, &core_list, out);
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
// TOP
|
||||
|
||||
#define GENERATED_BY "code/4ed_graphics_api.cpp"
|
||||
|
||||
#include "4ed_api_definition_main.cpp"
|
||||
|
||||
function API_Definition*
|
||||
|
@ -30,6 +32,11 @@ define_api(Arena *arena){
|
|||
api_param(arena, call, "void*", "data");
|
||||
}
|
||||
|
||||
{
|
||||
API_Call *call = api_call(arena, api, "free_texture", "void");
|
||||
api_param(arena, call, "u32", "texid");
|
||||
}
|
||||
|
||||
return(api);
|
||||
}
|
||||
|
||||
|
|
|
@ -132,6 +132,9 @@ end_render_section(Render_Target *target){
|
|||
|
||||
internal void
|
||||
draw_rectangle_outline(Render_Target *target, Rect_f32 rect, f32 roundness, f32 thickness, u32 color){
|
||||
|
||||
if ( rect_overlap(rect, target->current_clip_box) ) {
|
||||
|
||||
if (roundness < epsilon_f32){
|
||||
roundness = 0.f;
|
||||
}
|
||||
|
@ -152,8 +155,10 @@ draw_rectangle_outline(Render_Target *target, Rect_f32 rect, f32 roundness, f32
|
|||
vertices[i].color = color;
|
||||
vertices[i].half_thickness = half_thickness;
|
||||
}
|
||||
|
||||
draw__write_vertices_in_current_group(target, vertices, ArrayCount(vertices));
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
draw_rectangle(Render_Target *target, Rect_f32 rect, f32 roundness, u32 color){
|
||||
|
@ -194,6 +199,14 @@ draw_font_glyph(Render_Target *target, Face *face, u32 codepoint, Vec2_f32 p,
|
|||
vertices[2].xy = p_x_min + y_max;
|
||||
vertices[5].xy = p_x_max + y_max;
|
||||
|
||||
/* NOTE simon (26/09/24): We don't use rect_overlap here because the text rect is not guaranteed to be axis aligned. */
|
||||
b32 draw = rect_contains_point( target->current_clip_box, vertices[ 0 ].xy );
|
||||
draw = draw || rect_contains_point( target->current_clip_box, vertices[ 1 ].xy );
|
||||
draw = draw || rect_contains_point( target->current_clip_box, vertices[ 2 ].xy );
|
||||
draw = draw || rect_contains_point( target->current_clip_box, vertices[ 5 ].xy );
|
||||
|
||||
if ( draw ) {
|
||||
|
||||
#if 0
|
||||
Vec2_f32 xy_min = p + bounds.xy_off.x0*x_axis + bounds.xy_off.y0*y_axis;
|
||||
Vec2_f32 xy_max = p + bounds.xy_off.x1*x_axis + bounds.xy_off.y1*y_axis;
|
||||
|
@ -242,6 +255,7 @@ draw_font_glyph(Render_Target *target, Face *face, u32 codepoint, Vec2_f32 p,
|
|||
|
||||
draw__write_vertices_in_current_group(target, vertices, ArrayCount(vertices));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
// TOP
|
||||
|
||||
#define GENERATED_BY "code/4ed_system_api.cpp"
|
||||
|
||||
#include "4ed_api_definition_main.cpp"
|
||||
|
||||
function API_Definition*
|
||||
|
|
|
@ -185,7 +185,7 @@ get_defines_from_flags(Arena *arena, u32 flags){
|
|||
"-wd4611 -wd4189 -WX -GR- -EHa- -nologo -FC"
|
||||
|
||||
#define CL_LIBS_COMMON \
|
||||
"user32.lib winmm.lib gdi32.lib opengl32.lib comdlg32.lib userenv.lib "
|
||||
"user32.lib winmm.lib gdi32.lib comdlg32.lib userenv.lib "
|
||||
#define CL_LIBS_X64 CL_LIBS_COMMON FOREIGN_WIN "\\x64\\freetype.lib"
|
||||
#define CL_LIBS_X86 CL_LIBS_COMMON FOREIGN_WIN "\\x86\\freetype.lib"
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* Generated by: 4ed_generate_keycodes.cpp */
|
||||
enum{
|
||||
KeyCode_A = 1,
|
||||
KeyCode_B = 2,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* Generated by "code/4ed_api_parser_main.exe 4ed_api_implementation.cpp" */
|
||||
|
||||
function void
|
||||
custom_api_fill_vtable(API_VTable_custom *vtable){
|
||||
vtable->global_set_setting = global_set_setting;
|
||||
|
@ -180,6 +182,7 @@ vtable->buffer_find_all_matches = buffer_find_all_matches;
|
|||
vtable->get_core_profile_list = get_core_profile_list;
|
||||
vtable->get_custom_layer_boundary_docs = get_custom_layer_boundary_docs;
|
||||
}
|
||||
|
||||
#if defined(DYNAMIC_LINK_API)
|
||||
function void
|
||||
custom_api_read_vtable(API_VTable_custom *vtable){
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* Generated by "code/4ed_api_parser_main.exe 4ed_api_implementation.cpp" */
|
||||
|
||||
#define custom_global_set_setting_sig() b32 custom_global_set_setting(Application_Links* app, Global_Setting_ID setting, i64 value)
|
||||
#define custom_global_get_screen_rectangle_sig() Rect_f32 custom_global_get_screen_rectangle(Application_Links* app)
|
||||
#define custom_get_thread_context_sig() Thread_Context* custom_get_thread_context(Application_Links* app)
|
||||
|
@ -177,6 +179,7 @@
|
|||
#define custom_buffer_find_all_matches_sig() String_Match_List custom_buffer_find_all_matches(Application_Links* app, Arena* arena, Buffer_ID buffer, i32 string_id, Range_i64 range, String_Const_u8 needle, Character_Predicate* predicate, Scan_Direction direction)
|
||||
#define custom_get_core_profile_list_sig() Profile_Global_List* custom_get_core_profile_list(Application_Links* app)
|
||||
#define custom_get_custom_layer_boundary_docs_sig() Doc_Cluster* custom_get_custom_layer_boundary_docs(Application_Links* app, Arena* arena)
|
||||
|
||||
typedef b32 custom_global_set_setting_type(Application_Links* app, Global_Setting_ID setting, i64 value);
|
||||
typedef Rect_f32 custom_global_get_screen_rectangle_type(Application_Links* app);
|
||||
typedef Thread_Context* custom_get_thread_context_type(Application_Links* app);
|
||||
|
@ -356,6 +359,7 @@ typedef void custom_animate_in_n_milliseconds_type(Application_Links* app, u32 n
|
|||
typedef String_Match_List custom_buffer_find_all_matches_type(Application_Links* app, Arena* arena, Buffer_ID buffer, i32 string_id, Range_i64 range, String_Const_u8 needle, Character_Predicate* predicate, Scan_Direction direction);
|
||||
typedef Profile_Global_List* custom_get_core_profile_list_type(Application_Links* app);
|
||||
typedef Doc_Cluster* custom_get_custom_layer_boundary_docs_type(Application_Links* app, Arena* arena);
|
||||
|
||||
struct API_VTable_custom{
|
||||
custom_global_set_setting_type *global_set_setting;
|
||||
custom_global_get_screen_rectangle_type *global_get_screen_rectangle;
|
||||
|
@ -537,6 +541,7 @@ custom_buffer_find_all_matches_type *buffer_find_all_matches;
|
|||
custom_get_core_profile_list_type *get_core_profile_list;
|
||||
custom_get_custom_layer_boundary_docs_type *get_custom_layer_boundary_docs;
|
||||
};
|
||||
|
||||
#if defined(STATIC_LINK_API)
|
||||
internal b32 global_set_setting(Application_Links* app, Global_Setting_ID setting, i64 value);
|
||||
internal Rect_f32 global_get_screen_rectangle(Application_Links* app);
|
||||
|
@ -718,6 +723,7 @@ internal String_Match_List buffer_find_all_matches(Application_Links* app, Arena
|
|||
internal Profile_Global_List* get_core_profile_list(Application_Links* app);
|
||||
internal Doc_Cluster* get_custom_layer_boundary_docs(Application_Links* app, Arena* arena);
|
||||
#undef STATIC_LINK_API
|
||||
|
||||
#elif defined(DYNAMIC_LINK_API)
|
||||
global custom_global_set_setting_type *global_set_setting = 0;
|
||||
global custom_global_get_screen_rectangle_type *global_get_screen_rectangle = 0;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* Generated by "code/4ed_api_parser_main.exe 4ed_api_implementation.cpp" */
|
||||
|
||||
function API_Definition*
|
||||
custom_api_construct(Arena *arena){
|
||||
API_Definition *result = begin_api(arena, "custom");
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* Generated by "code/4ed_api_parser_main.exe 4ed_api_implementation.cpp" */
|
||||
|
||||
api(custom) function b32 global_set_setting(Application_Links* app, Global_Setting_ID setting, i64 value);
|
||||
api(custom) function Rect_f32 global_get_screen_rectangle(Application_Links* app);
|
||||
api(custom) function Thread_Context* get_thread_context(Application_Links* app);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* Generated by "code/4ed_system_api.cpp" */
|
||||
|
||||
function void
|
||||
system_api_fill_vtable(API_VTable_system *vtable){
|
||||
vtable->error_box = system_error_box;
|
||||
|
@ -58,6 +60,7 @@ vtable->set_key_mode = system_set_key_mode;
|
|||
vtable->set_source_mixer = system_set_source_mixer;
|
||||
vtable->set_destination_mixer = system_set_destination_mixer;
|
||||
}
|
||||
|
||||
#if defined(DYNAMIC_LINK_API)
|
||||
function void
|
||||
system_api_read_vtable(API_VTable_system *vtable){
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* Generated by "code/4ed_system_api.cpp" */
|
||||
|
||||
#define system_error_box_sig() void system_error_box(char* msg)
|
||||
#define system_get_path_sig() String_Const_u8 system_get_path(Arena* arena, System_Path_Code path_code)
|
||||
#define system_get_canonical_sig() String_Const_u8 system_get_canonical(Arena* arena, String_Const_u8 name)
|
||||
|
@ -55,6 +57,7 @@
|
|||
#define system_set_key_mode_sig() void system_set_key_mode(Key_Mode mode)
|
||||
#define system_set_source_mixer_sig() void system_set_source_mixer(void* ctx, Audio_Mix_Sources_Function* mix_func)
|
||||
#define system_set_destination_mixer_sig() void system_set_destination_mixer(Audio_Mix_Destination_Function* mix_func)
|
||||
|
||||
typedef void system_error_box_type(char* msg);
|
||||
typedef String_Const_u8 system_get_path_type(Arena* arena, System_Path_Code path_code);
|
||||
typedef String_Const_u8 system_get_canonical_type(Arena* arena, String_Const_u8 name);
|
||||
|
@ -112,6 +115,7 @@ typedef Input_Modifier_Set system_get_keyboard_modifiers_type(Arena* arena);
|
|||
typedef void system_set_key_mode_type(Key_Mode mode);
|
||||
typedef void system_set_source_mixer_type(void* ctx, Audio_Mix_Sources_Function* mix_func);
|
||||
typedef void system_set_destination_mixer_type(Audio_Mix_Destination_Function* mix_func);
|
||||
|
||||
struct API_VTable_system{
|
||||
system_error_box_type *error_box;
|
||||
system_get_path_type *get_path;
|
||||
|
@ -171,6 +175,7 @@ system_set_key_mode_type *set_key_mode;
|
|||
system_set_source_mixer_type *set_source_mixer;
|
||||
system_set_destination_mixer_type *set_destination_mixer;
|
||||
};
|
||||
|
||||
#if defined(STATIC_LINK_API)
|
||||
internal void system_error_box(char* msg);
|
||||
internal String_Const_u8 system_get_path(Arena* arena, System_Path_Code path_code);
|
||||
|
@ -230,6 +235,7 @@ internal void system_set_key_mode(Key_Mode mode);
|
|||
internal void system_set_source_mixer(void* ctx, Audio_Mix_Sources_Function* mix_func);
|
||||
internal void system_set_destination_mixer(Audio_Mix_Destination_Function* mix_func);
|
||||
#undef STATIC_LINK_API
|
||||
|
||||
#elif defined(DYNAMIC_LINK_API)
|
||||
global system_error_box_type *system_error_box = 0;
|
||||
global system_get_path_type *system_get_path = 0;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* Generated by "code/4ed_system_api.cpp" */
|
||||
|
||||
function API_Definition*
|
||||
system_api_construct(Arena *arena){
|
||||
API_Definition *result = begin_api(arena, "system");
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/* Generated by "code/4ed_system_api.cpp" */
|
||||
|
||||
api(system) function void error_box(char* msg);
|
||||
api(system) function String_Const_u8 get_path(Arena* arena, System_Path_Code path_code);
|
||||
api(system) function String_Const_u8 get_canonical(Arena* arena, String_Const_u8 name);
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
/* Generated by "code/4ed_font_api.cpp" */
|
||||
|
||||
function void
|
||||
font_api_fill_vtable(API_VTable_font *vtable){
|
||||
vtable->make_face = font_make_face;
|
||||
}
|
||||
|
||||
#if defined(DYNAMIC_LINK_API)
|
||||
function void
|
||||
font_api_read_vtable(API_VTable_font *vtable){
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
/* Generated by "code/4ed_font_api.cpp" */
|
||||
|
||||
#define font_make_face_sig() Face* font_make_face(Arena* arena, Face_Description* description, f32 scale_factor)
|
||||
|
||||
typedef Face* font_make_face_type(Arena* arena, Face_Description* description, f32 scale_factor);
|
||||
|
||||
struct API_VTable_font{
|
||||
font_make_face_type *make_face;
|
||||
};
|
||||
|
||||
#if defined(STATIC_LINK_API)
|
||||
internal Face* font_make_face(Arena* arena, Face_Description* description, f32 scale_factor);
|
||||
#undef STATIC_LINK_API
|
||||
|
||||
#elif defined(DYNAMIC_LINK_API)
|
||||
global font_make_face_type *font_make_face = 0;
|
||||
#undef DYNAMIC_LINK_API
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
/* Generated by "code/4ed_graphics_api.cpp" */
|
||||
|
||||
function void
|
||||
graphics_api_fill_vtable(API_VTable_graphics *vtable){
|
||||
vtable->get_texture = graphics_get_texture;
|
||||
vtable->fill_texture = graphics_fill_texture;
|
||||
vtable->free_texture = graphics_free_texture;
|
||||
}
|
||||
|
||||
#if defined(DYNAMIC_LINK_API)
|
||||
function void
|
||||
graphics_api_read_vtable(API_VTable_graphics *vtable){
|
||||
graphics_get_texture = vtable->get_texture;
|
||||
graphics_fill_texture = vtable->fill_texture;
|
||||
graphics_free_texture = vtable->free_texture;
|
||||
}
|
||||
#undef DYNAMIC_LINK_API
|
||||
#endif
|
||||
|
|
|
@ -1,17 +1,28 @@
|
|||
/* Generated by "code/4ed_graphics_api.cpp" */
|
||||
|
||||
#define graphics_get_texture_sig() u32 graphics_get_texture(Vec3_i32 dim, Texture_Kind texture_kind)
|
||||
#define graphics_fill_texture_sig() b32 graphics_fill_texture(Texture_Kind texture_kind, u32 texture, Vec3_i32 p, Vec3_i32 dim, void* data)
|
||||
#define graphics_free_texture_sig() void graphics_free_texture(u32 texid)
|
||||
|
||||
typedef u32 graphics_get_texture_type(Vec3_i32 dim, Texture_Kind texture_kind);
|
||||
typedef b32 graphics_fill_texture_type(Texture_Kind texture_kind, u32 texture, Vec3_i32 p, Vec3_i32 dim, void* data);
|
||||
typedef void graphics_free_texture_type(u32 texid);
|
||||
|
||||
struct API_VTable_graphics{
|
||||
graphics_get_texture_type *get_texture;
|
||||
graphics_fill_texture_type *fill_texture;
|
||||
graphics_free_texture_type *free_texture;
|
||||
};
|
||||
|
||||
#if defined(STATIC_LINK_API)
|
||||
internal u32 graphics_get_texture(Vec3_i32 dim, Texture_Kind texture_kind);
|
||||
internal b32 graphics_fill_texture(Texture_Kind texture_kind, u32 texture, Vec3_i32 p, Vec3_i32 dim, void* data);
|
||||
internal void graphics_free_texture(u32 texid);
|
||||
#undef STATIC_LINK_API
|
||||
|
||||
#elif defined(DYNAMIC_LINK_API)
|
||||
global graphics_get_texture_type *graphics_get_texture = 0;
|
||||
global graphics_fill_texture_type *graphics_fill_texture = 0;
|
||||
global graphics_free_texture_type *graphics_free_texture = 0;
|
||||
#undef DYNAMIC_LINK_API
|
||||
#endif
|
||||
|
|
|
@ -57,7 +57,7 @@ struct Metal_Texture_Slot_List{
|
|||
Metal_Texture_Slot *last_free_slot;
|
||||
};
|
||||
|
||||
global_const u32 metal__invalid_texture_slot_locator = (u32)-1;
|
||||
global_const u32 metal__invalid_texture_slot_locator = 0;
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
|
@ -67,6 +67,7 @@ global_const u32 metal__invalid_texture_slot_locator = (u32)-1;
|
|||
- (u32)get_texture_of_dim:(Vec3_i32)dim kind:(Texture_Kind)kind;
|
||||
- (b32)fill_texture:(u32)texture kind:(Texture_Kind)kind pos:(Vec3_i32)p dim:(Vec3_i32)dim data:(void*)data;
|
||||
- (void)bind_texture:(u32)handle encoder:(id<MTLRenderCommandEncoder>)render_encoder;
|
||||
- (void)free_texture:(u32)handle;
|
||||
- (Metal_Texture_Slot*)get_texture_slot_at_locator:(Metal_Texture_Slot_Locator)locator;
|
||||
- (Metal_Texture_Slot*)get_texture_slot_at_handle:(u32)handle;
|
||||
|
||||
|
@ -284,6 +285,11 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
// NOTE(yuval): Initialize the texture slot list
|
||||
block_zero_struct(&_texture_slots);
|
||||
|
||||
// NOTE(simon): Other platforms use 0 as invalid handle, so we allocate the first texture here (it should be 0),
|
||||
// and never use it so we can use 0 as the invalid handle.
|
||||
u32 reserved_texture_slot_do_not_use = [self get_texture_of_dim:V3i32(2, 2, 1) kind:TextureKind_Mono];
|
||||
Assert( reserved_texture_slot_do_not_use == 0 );
|
||||
|
||||
// NOTE(yuval): Create the fallback texture
|
||||
_target->fallback_texture_id = [self get_texture_of_dim:V3i32(2, 2, 1)
|
||||
kind:TextureKind_Mono];
|
||||
|
@ -510,7 +516,7 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
texture_descriptor.pixelFormat = MTLPixelFormatR8Unorm;
|
||||
texture_descriptor.width = dim.x;
|
||||
texture_descriptor.height = dim.y;
|
||||
texture_descriptor.depth = dim.z;
|
||||
texture_descriptor.arrayLength = dim.z;
|
||||
|
||||
// NOTE(yuval): Create the texture from the device using the descriptor and add it to the textures array.
|
||||
Metal_Texture texture = [_device newTextureWithDescriptor:texture_descriptor];
|
||||
|
@ -531,16 +537,20 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
Metal_Texture texture = texture_slot->texture;
|
||||
|
||||
if (texture != 0){
|
||||
// https://developer.apple.com/documentation/metal/mtlregion
|
||||
// for 2d texture origin.z is 0, and depth is 1
|
||||
MTLRegion replace_region = {
|
||||
{(NSUInteger)p.x, (NSUInteger)p.y, (NSUInteger)p.z},
|
||||
{(NSUInteger)dim.x, (NSUInteger)dim.y, (NSUInteger)dim.z}
|
||||
{(NSUInteger)p.x, (NSUInteger)p.y, 0},
|
||||
{(NSUInteger)dim.x, (NSUInteger)dim.y, 1}
|
||||
};
|
||||
|
||||
// NOTE(yuval): Fill the texture with data
|
||||
[texture replaceRegion:replace_region
|
||||
mipmapLevel:0
|
||||
slice:p.z
|
||||
withBytes:data
|
||||
bytesPerRow:dim.x];
|
||||
bytesPerRow:dim.x
|
||||
bytesPerImage:0];
|
||||
|
||||
result = true;
|
||||
}
|
||||
|
@ -561,6 +571,14 @@ metal__make_buffer(u32 size, id<MTLDevice> device){
|
|||
}
|
||||
}
|
||||
|
||||
- (void)free_texture:(u32)handle{
|
||||
Metal_Texture_Slot *texture_slot = [self get_texture_slot_at_handle:handle];
|
||||
if (texture_slot){
|
||||
sll_queue_push(_texture_slots.first_free_slot, _texture_slots.last_free_slot, texture_slot);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (Metal_Texture_Slot*)get_texture_slot_at_locator:(Metal_Texture_Slot_Locator)locator{
|
||||
Metal_Texture_Slot *result = 0;
|
||||
|
||||
|
|
|
@ -53,6 +53,11 @@ gl__fill_texture(Texture_Kind texture_kind, u32 texture, Vec3_i32 p, Vec3_i32 di
|
|||
return(result);
|
||||
}
|
||||
|
||||
internal void
|
||||
gl__free_texture(u32 texture){
|
||||
glDeleteTextures(1, &texture);
|
||||
}
|
||||
|
||||
internal void
|
||||
gl__error_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const char *message, const void *userParam){
|
||||
switch (id){
|
||||
|
@ -68,7 +73,7 @@ gl__error_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsiz
|
|||
}
|
||||
}
|
||||
|
||||
char *gl__header = R"foo(#version 130
|
||||
char *gl__header = R"foo(#version 150
|
||||
)foo";
|
||||
|
||||
char *gl__vertex = R"foo(
|
||||
|
|
|
@ -588,6 +588,11 @@ graphics_fill_texture_sig(){
|
|||
return(gl__fill_texture(texture_kind, texture, p, dim, data));
|
||||
}
|
||||
|
||||
internal
|
||||
graphics_free_texture_sig(){
|
||||
gl__free_texture(texid);
|
||||
}
|
||||
|
||||
////////////////////////////
|
||||
|
||||
internal Face*
|
||||
|
@ -700,8 +705,8 @@ glx_create_context(GLXFBConfig fb_config){
|
|||
ctx = glXCreateNewContext(linuxvars.dpy, fb_config, GLX_RGBA_TYPE, 0, True);
|
||||
} else {
|
||||
static const int context_attribs[] = {
|
||||
GLX_CONTEXT_MAJOR_VERSION_ARB, 2,
|
||||
GLX_CONTEXT_MINOR_VERSION_ARB, 1,
|
||||
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
|
||||
GLX_CONTEXT_MINOR_VERSION_ARB, 2,
|
||||
GLX_CONTEXT_PROFILE_MASK_ARB , GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
#if GL_DEBUG_MODE
|
||||
GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_DEBUG_BIT_ARB,
|
||||
|
@ -709,7 +714,6 @@ glx_create_context(GLXFBConfig fb_config){
|
|||
None
|
||||
};
|
||||
|
||||
//LOG("Creating GL 2.1 context... ");
|
||||
ctx = glXCreateContextAttribsARB(linuxvars.dpy, fb_config, 0, True, context_attribs);
|
||||
}
|
||||
|
||||
|
|
|
@ -976,6 +976,11 @@ graphics_fill_texture_sig(){
|
|||
return(result);
|
||||
}
|
||||
|
||||
function
|
||||
graphics_free_texture_sig(){
|
||||
renderer->free_texture(renderer, texid);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
/******************/
|
||||
|
|
|
@ -44,6 +44,12 @@ mac_fill_texture_sig(mac_metal__fill_texture){
|
|||
return(result);
|
||||
}
|
||||
|
||||
function
|
||||
mac_free_texture_sig(mac_metal__free_texture){
|
||||
Mac_Metal *metal = (Mac_Metal*)renderer;
|
||||
[metal->renderer free_texture:texture];
|
||||
}
|
||||
|
||||
function Mac_Metal*
|
||||
mac_metal__init(NSWindow *window, Render_Target *target){
|
||||
// NOTE(yuval): Create the Mac Metal rendere
|
||||
|
@ -52,6 +58,7 @@ mac_metal__init(NSWindow *window, Render_Target *target){
|
|||
metal->base.render = mac_metal__render;
|
||||
metal->base.get_texture = mac_metal__get_texture;
|
||||
metal->base.fill_texture = mac_metal__fill_texture;
|
||||
metal->base.free_texture = mac_metal__free_texture;
|
||||
|
||||
// NOTE(yuval): Create the Metal view
|
||||
NSView *content_view = [window contentView];
|
||||
|
|
|
@ -144,6 +144,11 @@ mac_fill_texture_sig(mac_gl__fill_texture){
|
|||
return(result);
|
||||
}
|
||||
|
||||
function
|
||||
mac_free_texture_sig(mac_gl__free_texture){
|
||||
gl__free_texture(texture);
|
||||
}
|
||||
|
||||
function Mac_OpenGL*
|
||||
mac_gl__init(NSWindow *window, Render_Target *target){
|
||||
// NOTE(yuval): Create the Mac OpenGL Renderer
|
||||
|
@ -152,6 +157,7 @@ mac_gl__init(NSWindow *window, Render_Target *target){
|
|||
gl->base.render = mac_gl__render;
|
||||
gl->base.get_texture = mac_gl__get_texture;
|
||||
gl->base.fill_texture = mac_gl__fill_texture;
|
||||
gl->base.free_texture = mac_gl__free_texture;
|
||||
|
||||
// NOTE(yuval): Create the OpenGL view
|
||||
NSView *content_view = [window contentView];
|
||||
|
|
|
@ -18,6 +18,9 @@ typedef mac_get_texture_sig(mac_get_texture_type);
|
|||
#define mac_fill_texture_sig(name) b32 name(Mac_Renderer *renderer, Texture_Kind texture_kind, u32 texture, Vec3_i32 p, Vec3_i32 dim, void* data)
|
||||
typedef mac_fill_texture_sig(mac_fill_texture_type);
|
||||
|
||||
#define mac_free_texture_sig(name) void name(Mac_Renderer *renderer, u32 texture)
|
||||
typedef mac_free_texture_sig(mac_free_texture_type);
|
||||
|
||||
typedef i32 Mac_Renderer_Kind;
|
||||
enum{
|
||||
MacRenderer_OpenGL,
|
||||
|
@ -31,6 +34,7 @@ struct Mac_Renderer{
|
|||
|
||||
mac_get_texture_type *get_texture;
|
||||
mac_fill_texture_type *fill_texture;
|
||||
mac_free_texture_type *free_texture;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
|
|
|
@ -0,0 +1,658 @@
|
|||
|
||||
#define DX11_MAX_TEXTURE_COUNT 32
|
||||
|
||||
struct DX11Texture {
|
||||
ID3D11Texture2D* pointer;
|
||||
ID3D11ShaderResourceView* view;
|
||||
};
|
||||
|
||||
struct GL_Program {
|
||||
ID3D11VertexShader* vertex;
|
||||
ID3D11InputLayout* layout;
|
||||
ID3D11PixelShader* pixel;
|
||||
b8 valid;
|
||||
};
|
||||
|
||||
struct DX11 {
|
||||
b8 initialized;
|
||||
ID3D11Device1* device;
|
||||
ID3D11DeviceContext1* context;
|
||||
IDXGISwapChain1* swap_chain;
|
||||
ID3D11SamplerState* sampler;
|
||||
ID3D11RenderTargetView* render_target_view;
|
||||
GL_Program gpu_program;
|
||||
ID3D11Buffer* vertex_buffer;
|
||||
ID3D11Buffer* constants_buffer;
|
||||
|
||||
// NOTE(simon, 28/02/24): To keep the API the same since the OpenGL texture handle are store in
|
||||
// other places than the graphics parts (e.g. in the font Face struct), we create an array of
|
||||
// textures, and use the indices as texture handles.
|
||||
DX11Texture textures[ DX11_MAX_TEXTURE_COUNT + 1 ];
|
||||
// NOTE(simon, 28/02/24): The first slot in the array should not be used so we can consider an
|
||||
// index of 0 to be invalid. OpenGL should not return 0 for texture handle, so we sort of do
|
||||
// the same.
|
||||
u32 texture_count;
|
||||
};
|
||||
|
||||
global DX11 g_dx11 = { };
|
||||
|
||||
// NOTE(simon, 28/02/24): Passing 0 for texid use the reserved texture in the array, and passing a
|
||||
// resource view of zero unbinds the resource.
|
||||
internal void
|
||||
gl__bind_texture(Render_Target *t, i32 texid){
|
||||
if (t->bound_texture != texid){
|
||||
DX11Texture* texture = g_dx11.textures + texid;
|
||||
g_dx11.context->PSSetShaderResources( 0, 1, &texture->view );
|
||||
t->bound_texture = texid;
|
||||
}
|
||||
}
|
||||
|
||||
internal void
|
||||
gl__bind_any_texture(Render_Target *t){
|
||||
if (t->bound_texture == 0){
|
||||
Assert(t->fallback_texture_id != 0);
|
||||
DX11Texture* texture = g_dx11.textures + t->fallback_texture_id;
|
||||
g_dx11.context->PSSetShaderResources( 0, 1, &texture->view );
|
||||
t->bound_texture = t->fallback_texture_id;
|
||||
}
|
||||
}
|
||||
|
||||
internal u32
|
||||
gl__get_texture(Vec3_i32 dim, Texture_Kind texture_kind){
|
||||
|
||||
u32 texid = 0;
|
||||
|
||||
if ( g_dx11.texture_count < ArrayCount( g_dx11.textures ) ) {
|
||||
|
||||
texid = g_dx11.texture_count;
|
||||
g_dx11.texture_count++;
|
||||
|
||||
} else {
|
||||
|
||||
for ( u32 i = 1; i < g_dx11.texture_count; i++ ) {
|
||||
|
||||
DX11Texture* texture = g_dx11.textures + i;
|
||||
|
||||
if ( !texture->pointer && !texture->view ) {
|
||||
texid = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( texid ) {
|
||||
|
||||
DX11Texture* texture = g_dx11.textures + texid;
|
||||
Assert( texture->pointer == 0 );
|
||||
Assert( texture->view == 0 );
|
||||
|
||||
D3D11_TEXTURE2D_DESC texture_desc = { 0 };
|
||||
texture_desc.Width = dim.x;
|
||||
texture_desc.Height = dim.y;
|
||||
texture_desc.MipLevels = 1;
|
||||
texture_desc.ArraySize = dim.z;
|
||||
texture_desc.Format = DXGI_FORMAT_A8_UNORM;
|
||||
texture_desc.SampleDesc.Count = 1;
|
||||
texture_desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
texture_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
texture_desc.CPUAccessFlags = 0; // D3D11_CPU_ACCESS_WRITE;
|
||||
|
||||
// NOTE(simon, 28/02/24): I initialize the texture with zeros. In practice it doesn't seem
|
||||
// to matter, but since the shader use a bilinear filter, the unitialized data in the
|
||||
// texture could change the result of the filtering for texel at the edge of a character.
|
||||
// I did some tests with the rectangle packer to have a border around character but got the
|
||||
// exact same render, so It doesn't matter much.
|
||||
D3D11_SUBRESOURCE_DATA* texture_data = push_array_zero( &win32vars.frame_arena, D3D11_SUBRESOURCE_DATA, dim.z );
|
||||
u8* initial_data = push_array_zero( &win32vars.frame_arena, u8, dim.x * dim.y );
|
||||
|
||||
for ( i32 i = 0; i < dim.z; i++ ) {
|
||||
texture_data[ i ].pSysMem = initial_data;
|
||||
texture_data[ i ].SysMemPitch = dim.x;
|
||||
}
|
||||
|
||||
HRESULT hr = g_dx11.device->CreateTexture2D( &texture_desc, texture_data, &texture->pointer );
|
||||
|
||||
pop_array( &win32vars.frame_arena, u8, dim.x * dim.y );
|
||||
pop_array( &win32vars.frame_arena, D3D11_SUBRESOURCE_DATA, dim.z );
|
||||
|
||||
if ( SUCCEEDED( hr ) ) {
|
||||
hr = g_dx11.device->CreateShaderResourceView( ( ID3D11Resource* ) texture->pointer, 0, &texture->view );
|
||||
}
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
|
||||
// NOTE(simon, 28/02/24): When we fail, we donc decrement the texture count, but the
|
||||
// loop at the beginning of the function will reuse texture when
|
||||
// texture_count == DX11_MAX_TEXTURE_COUNT.
|
||||
texid = 0;
|
||||
|
||||
if ( texture->pointer ) {
|
||||
texture->pointer->Release( );
|
||||
texture->pointer = 0;
|
||||
}
|
||||
|
||||
if ( texture->view ) {
|
||||
texture->view->Release( );
|
||||
texture->view = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(texid);
|
||||
}
|
||||
|
||||
internal b32
|
||||
gl__fill_texture(Texture_Kind texture_kind, u32 texid, Vec3_i32 p, Vec3_i32 dim, void *data){
|
||||
|
||||
// NOTE(simon, 28/02/24): The OpenGL version always returns false.
|
||||
b32 result = false;
|
||||
|
||||
// NOTE(simon, 28/02/24): In the OpenGL version, if we pass zero as texture handle, the
|
||||
// function works on the currently bound texture. In directx we need to get the texture pointer.
|
||||
// We could retrieve that from Render_Target->bound_texture, but we don't have that as a
|
||||
// parameter to this function and don't want to change the signature since it's used by the
|
||||
// font rendering code and other platforms. Fortunately the only call that specified 0 for the
|
||||
// texture handle was for the creation of the fallback texture in gl_render, and we can modify
|
||||
// that call to pass the fallback texture handle.
|
||||
Assert( texid != 0 );
|
||||
|
||||
if (dim.x > 0 && dim.y > 0 && dim.z > 0){
|
||||
|
||||
DX11Texture* texture = g_dx11.textures + texid;
|
||||
|
||||
D3D11_BOX box = { };
|
||||
box.left = p.x;
|
||||
box.right = p.x + dim.x;
|
||||
box.top = p.y;
|
||||
box.bottom = p.y + dim.y;
|
||||
box.front = 0;
|
||||
box.back = 1;
|
||||
|
||||
u32 sub_resource_index = D3D11CalcSubresource( 0 /* MipSlice */, p.z /* ArraySlice */, 1 /* MipLevels */ );
|
||||
g_dx11.context->UpdateSubresource( texture->pointer, sub_resource_index, &box, data, dim.x, dim.x * dim.y );
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal void gl__free_texture( u32 texid ) {
|
||||
|
||||
if ( texid ) {
|
||||
|
||||
DX11Texture* texture = g_dx11.textures + texid;
|
||||
|
||||
if ( texture->view ) {
|
||||
texture->view->Release( );
|
||||
texture->view = 0;
|
||||
}
|
||||
|
||||
if ( texture->pointer ) {
|
||||
texture->pointer->Release( );
|
||||
texture->pointer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *gl__vertex = R"foo(
|
||||
|
||||
// NOTE(simon, 28/02/24): The layout of this is (constants are store in 16 bytes vectors (4 floats))
|
||||
// vector1: view_m._11, view_m._12, 0, 0
|
||||
// vector2: view_m._21, view_m._22, view_t.x, view_t.y
|
||||
cbuffer constants : register( b0 ) {
|
||||
row_major float2x2 view_m;
|
||||
float2 view_t;
|
||||
}
|
||||
|
||||
struct input_t {
|
||||
float2 vertex_p : POSITION;
|
||||
float3 vertex_t : UVW;
|
||||
float4 vertex_c : COLOR;
|
||||
float vertex_ht : THICKNESS;
|
||||
};
|
||||
|
||||
struct output_t {
|
||||
float4 position : SV_POSITION;
|
||||
float4 color : COLOR;
|
||||
float3 uvw : UVW;
|
||||
float2 xy : XY;
|
||||
float2 adjusted_half_dim: HALF_DIM;
|
||||
float half_thickness : THICKNESS;
|
||||
};
|
||||
|
||||
output_t main(input_t input) {
|
||||
|
||||
output_t output;
|
||||
|
||||
output.position = float4( mul( view_m, ( input.vertex_p - view_t ) ), 0.0, 1.0 );
|
||||
// NOTE(simon, 28/02/24): The input colors are BGRA, we need them as RGBA.
|
||||
output.color = input.vertex_c.zyxw;
|
||||
output.uvw = input.vertex_t;
|
||||
output.xy = input.vertex_p;
|
||||
output.half_thickness = input.vertex_ht;
|
||||
|
||||
float2 center = input.vertex_t.xy;
|
||||
float2 half_dim = abs( input.vertex_p - center );
|
||||
output.adjusted_half_dim = half_dim - input.vertex_t.zz + float2( 0.5, 0.5 );
|
||||
|
||||
return output;
|
||||
}
|
||||
)foo";
|
||||
|
||||
char *gl__fragment = R"foo(
|
||||
|
||||
struct input_t {
|
||||
float4 position : SV_POSITION;
|
||||
float4 color : COLOR;
|
||||
float3 uvw : UVW;
|
||||
float2 xy : XY;
|
||||
float2 adjusted_half_dim: HALF_DIM;
|
||||
float half_thickness : THICKNESS;
|
||||
};
|
||||
|
||||
Texture2DArray alpha : register( t0 );
|
||||
SamplerState alpha_sampler : register( s0 );
|
||||
|
||||
float rectangle_sd( float2 p, float2 b ) {
|
||||
|
||||
float2 d = abs( p ) - b;
|
||||
return( length( max( d, float2( 0.0, 0.0 ) ) ) + min( max( d.x, d.y ), 0.0 ) );
|
||||
}
|
||||
|
||||
float4 main( input_t input ) : SV_TARGET {
|
||||
|
||||
float has_thickness = step( 0.49, input.half_thickness );
|
||||
float does_not_have_thickness = 1.0 - has_thickness;
|
||||
|
||||
float sample_value = alpha.Sample( alpha_sampler, input.uvw ).a;
|
||||
sample_value *= does_not_have_thickness;
|
||||
|
||||
float2 center = input.uvw.xy;
|
||||
float roundness = input.uvw.z;
|
||||
float sd = rectangle_sd( input.xy - center, input.adjusted_half_dim );
|
||||
sd = sd - roundness;
|
||||
sd = abs( sd + input.half_thickness ) - input.half_thickness;
|
||||
float shape_value = 1.0 - smoothstep(-1.0, 0.0, sd);
|
||||
shape_value *= has_thickness;
|
||||
|
||||
float4 result = float4( input.color.xyz, input.color.a * ( sample_value + shape_value ) );
|
||||
return result;
|
||||
}
|
||||
)foo";
|
||||
|
||||
// NOTE(simon, 28/02/24): This function is not generic. It can compile any shader, but the vertex
|
||||
// input layout is fixed. 4coder only has one vertex format and shader, so we could remove this
|
||||
// function and move its content in the win32_gl_create_window. I removed the header parameter as
|
||||
// it's not useful in directx.
|
||||
internal GL_Program
|
||||
gl__make_program( char* vertex, char* pixel ) {
|
||||
|
||||
GL_Program result = { };
|
||||
|
||||
u32 vertex_length = 0;
|
||||
|
||||
while ( vertex && vertex[ vertex_length ] != 0 ) {
|
||||
vertex_length++;
|
||||
}
|
||||
|
||||
u32 pixel_length = 0;
|
||||
|
||||
while ( pixel && pixel[ pixel_length ] != 0 ) {
|
||||
pixel_length++;
|
||||
}
|
||||
|
||||
ID3DBlob* vs_blob = 0;
|
||||
ID3DBlob* vs_error_blob = 0;
|
||||
ID3D11VertexShader* vertex_shader = 0;
|
||||
ID3D11InputLayout* input_layout = 0;
|
||||
|
||||
ID3DBlob* ps_blob = 0;
|
||||
ID3DBlob* ps_error_blob = 0;
|
||||
ID3D11PixelShader* pixel_shader = 0;
|
||||
|
||||
do {
|
||||
|
||||
HRESULT hr = D3DCompile( vertex, vertex_length, 0, 0, 0, "main", "vs_5_0", 0, 0, &vs_blob, &vs_error_blob );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( "Failed to compile vertex shader.\n" );
|
||||
|
||||
if ( vs_error_blob ) {
|
||||
u8* error_message = ( u8* ) vs_error_blob->GetBufferPointer( );
|
||||
u32 length = ( u32 ) vs_error_blob->GetBufferSize( );
|
||||
log_os( "vertex shader error:\n%.*s\n", length, error_message );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
hr = g_dx11.device->CreateVertexShader( vs_blob->GetBufferPointer( ), vs_blob->GetBufferSize( ), 0, &vertex_shader );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( "Failed to create a vertex shader.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
D3D11_INPUT_ELEMENT_DESC layout_desc[ 4 ] = { };
|
||||
|
||||
layout_desc[ 0 ].SemanticName = "POSITION";
|
||||
layout_desc[ 0 ].Format = DXGI_FORMAT_R32G32_FLOAT;
|
||||
layout_desc[ 0 ].AlignedByteOffset = 0;
|
||||
layout_desc[ 0 ].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
|
||||
|
||||
layout_desc[ 1 ].SemanticName = "UVW";
|
||||
layout_desc[ 1 ].Format = DXGI_FORMAT_R32G32B32_FLOAT;
|
||||
layout_desc[ 1 ].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
|
||||
layout_desc[ 1 ].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
|
||||
|
||||
layout_desc[ 2 ].SemanticName = "COLOR";
|
||||
layout_desc[ 2 ].Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
layout_desc[ 2 ].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
|
||||
layout_desc[ 2 ].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
|
||||
|
||||
layout_desc[ 3 ].SemanticName = "THICKNESS";
|
||||
layout_desc[ 3 ].Format = DXGI_FORMAT_R32_FLOAT;
|
||||
layout_desc[ 3 ].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
|
||||
layout_desc[ 3 ].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
|
||||
|
||||
hr = g_dx11.device->CreateInputLayout( layout_desc, ArrayCount( layout_desc ), vs_blob->GetBufferPointer( ), vs_blob->GetBufferSize( ), &input_layout );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( "Failed to create input layout.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
hr = D3DCompile( pixel, pixel_length, 0, 0, 0, "main", "ps_5_0", 0, 0, &ps_blob, &ps_error_blob );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( "Failed to compile pixel shader.\n" );
|
||||
|
||||
if ( ps_error_blob ) {
|
||||
u8* error_message = ( u8* ) ps_error_blob->GetBufferPointer( );
|
||||
u32 length = ( u32 ) ps_error_blob->GetBufferSize( );
|
||||
log_os( "pixel shader error:\n%.*s\n", length, error_message );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
hr = g_dx11.device->CreatePixelShader( ps_blob->GetBufferPointer( ), ps_blob->GetBufferSize( ), 0, &pixel_shader );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( "Failed to create a pixel shader.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
result.vertex = vertex_shader;
|
||||
result.layout = input_layout;
|
||||
result.pixel = pixel_shader;
|
||||
result.valid = true;
|
||||
|
||||
} while ( 0 );
|
||||
|
||||
if ( vs_blob ) {
|
||||
vs_blob->Release( );
|
||||
vs_blob = 0;
|
||||
}
|
||||
|
||||
if ( vs_error_blob ) {
|
||||
vs_error_blob->Release( );
|
||||
vs_error_blob = 0;
|
||||
}
|
||||
|
||||
if ( ps_blob ) {
|
||||
ps_blob->Release( );
|
||||
ps_blob = 0;
|
||||
}
|
||||
|
||||
if ( ps_error_blob ) {
|
||||
ps_error_blob->Release( );
|
||||
ps_error_blob = 0;
|
||||
}
|
||||
|
||||
if ( !result.valid ) {
|
||||
|
||||
if ( vertex_shader ) {
|
||||
vertex_shader->Release( );
|
||||
vertex_shader = 0;
|
||||
}
|
||||
|
||||
if ( input_layout ) {
|
||||
input_layout->Release( );
|
||||
input_layout = 0;
|
||||
}
|
||||
|
||||
if ( pixel_shader ) {
|
||||
pixel_shader->Release( );
|
||||
pixel_shader = 0;
|
||||
}
|
||||
|
||||
os_popup_error( "Error", "Shader compilation failed." );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal void
|
||||
gl_render(Render_Target *t){
|
||||
Font_Set *font_set = (Font_Set*)t->font_set;
|
||||
|
||||
local_persist b32 first_call = true;
|
||||
|
||||
if (first_call){
|
||||
|
||||
// NOTE(simon, 28/02/24): Most of the code here has been moved in win32_gl_create_window
|
||||
// because if that code fails we should exit the application directly.
|
||||
first_call = false;
|
||||
|
||||
u32 stride = sizeof( Render_Vertex );
|
||||
u32 offset = 0;
|
||||
|
||||
g_dx11.context->IASetVertexBuffers( 0, 1, &g_dx11.vertex_buffer, &stride, &offset );
|
||||
g_dx11.context->IASetInputLayout( g_dx11.gpu_program.layout );
|
||||
g_dx11.context->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
|
||||
|
||||
g_dx11.context->VSSetShader( g_dx11.gpu_program.vertex, 0, 0 );
|
||||
g_dx11.context->VSSetConstantBuffers( 0, 1, &g_dx11.constants_buffer );
|
||||
|
||||
g_dx11.context->PSSetShader( g_dx11.gpu_program.pixel, 0, 0 );
|
||||
g_dx11.context->PSSetSamplers( 0, 1, &g_dx11.sampler );
|
||||
|
||||
{
|
||||
t->fallback_texture_id = gl__get_texture(V3i32(2, 2, 1), TextureKind_Mono);
|
||||
u8 white_block[] = { 0xFF, 0xFF, 0xFF, 0xFF, };
|
||||
// NOTE(simon, 28/02/24): Passing the fallback texture, because we can't rely on the
|
||||
// fact that gl__get_texture has bound the fallback texture.
|
||||
gl__fill_texture(TextureKind_Mono, t->fallback_texture_id, V3i32(0, 0, 0), V3i32(2, 2, 1), white_block);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(simon, 28/02/24): OMSetRenderTargets needs to be set each frame when using a FLIP swap
|
||||
// chain.
|
||||
g_dx11.context->OMSetRenderTargets( 1, &g_dx11.render_target_view, 0 );
|
||||
|
||||
i32 width = t->width;
|
||||
i32 height = t->height;
|
||||
|
||||
// NOTE(simon, 28/02/24): Viewport (0, 0) is top left in directx. Important for viewport and
|
||||
// scissor calls.
|
||||
|
||||
D3D11_VIEWPORT viewport = {
|
||||
0, // TopLeftX
|
||||
0, // TopLeftY
|
||||
( float ) width, // Width
|
||||
( float ) height, // Height
|
||||
0, // MinDepth
|
||||
1// MaxDepth
|
||||
};
|
||||
|
||||
g_dx11.context->RSSetViewports( 1, &viewport );
|
||||
|
||||
D3D11_RECT scissor = {
|
||||
0, // left
|
||||
0, // top
|
||||
width, // right
|
||||
height // bottom
|
||||
};
|
||||
|
||||
g_dx11.context->RSSetScissorRects( 1, &scissor );
|
||||
|
||||
float magenta[ 4 ] = { 1.0f, 0.0f, 1.0f, 1.0f };
|
||||
g_dx11.context->ClearRenderTargetView( g_dx11.render_target_view, magenta );
|
||||
|
||||
// NOTE(simon, 28/02/24): The constants (uniforms) were set in the render loop in the OpenGL
|
||||
// version. But since they don't vary between draw calls I moved the code before the render
|
||||
// loop.
|
||||
D3D11_MAPPED_SUBRESOURCE constants_map = { };
|
||||
HRESULT hr = g_dx11.context->Map( ( ID3D11Resource* ) g_dx11.constants_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &constants_map );
|
||||
|
||||
// NOTE(simon, 28/02/24): The layout of the constants buffer was a bit confusing. This link
|
||||
// explains a little about how data is laid out:
|
||||
// https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules
|
||||
// The article doesn't explain anything about matrices. What I found out while making this work
|
||||
// is that each row or column (depending on if we use column or row major matrices) of a matrix
|
||||
// needs to start on a new 16 bytes vector. For a 2 by 2 matrix, this means that there are two
|
||||
// register elements at the end of the first vector that aren't used.
|
||||
// Another thing is that the second vector only needs the first two elements for the matrix,
|
||||
// so the two elements we want to put next can be in the same vector.
|
||||
|
||||
// NOTE(simon, 28/02/24): The code here could be shorter, but I prefer to make it clear what's
|
||||
// happening.
|
||||
f32 view_m[ 4 ] = {
|
||||
2.0f / width, 0,
|
||||
0, -2.0f / height
|
||||
};
|
||||
f32 view_t[ 2 ] = { width / 2.0f, height / 2.0f };
|
||||
|
||||
f32* vector_1 = ( f32* ) constants_map.pData;
|
||||
f32* vector_2 = vector_1 + 4;
|
||||
|
||||
vector_1[ 0 ] = view_m[ 0 ];
|
||||
vector_1[ 1 ] = view_m[ 1 ];
|
||||
vector_1[ 2 ] = 0; // Padding
|
||||
vector_1[ 3 ] = 0; // Padding
|
||||
|
||||
vector_2[ 0 ] = view_m[ 2 ];
|
||||
vector_2[ 1 ] = view_m[ 3 ];
|
||||
vector_2[ 2 ] = view_t[ 0 ];
|
||||
vector_2[ 3 ] = view_t[ 1 ];
|
||||
|
||||
g_dx11.context->Unmap( ( ID3D11Resource* ) g_dx11.constants_buffer, 0 );
|
||||
|
||||
gl__bind_texture( t, 0 );
|
||||
|
||||
for (Render_Free_Texture *free_texture = t->free_texture_first;
|
||||
free_texture != 0;
|
||||
free_texture = free_texture->next){
|
||||
|
||||
gl__free_texture( free_texture->tex_id );
|
||||
}
|
||||
|
||||
t->free_texture_first = 0;
|
||||
t->free_texture_last = 0;
|
||||
|
||||
D3D11_BUFFER_DESC vertex_buffer_desc = { };
|
||||
g_dx11.vertex_buffer->GetDesc( &vertex_buffer_desc );
|
||||
|
||||
for (Render_Group *group = t->group_first;
|
||||
group != 0;
|
||||
group = group->next){
|
||||
Rect_i32 box = Ri32(group->clip_box);
|
||||
|
||||
// NOTE(FS): Ignore this group if we our scissor rectangle is not well defined
|
||||
if ((rect_width(box) <= 0) || (rect_height(box) <= 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
D3D11_RECT group_scissor = { };
|
||||
group_scissor.left = box.x0;
|
||||
group_scissor.right = box.x1;
|
||||
group_scissor.top = box.y0;
|
||||
group_scissor.bottom = box.y1;
|
||||
|
||||
g_dx11.context->RSSetScissorRects( 1, &group_scissor );
|
||||
|
||||
i32 vertex_count = group->vertex_list.vertex_count;
|
||||
if (vertex_count > 0){
|
||||
Face *face = font_set_face_from_id(font_set, group->face_id);
|
||||
if (face != 0){
|
||||
gl__bind_texture(t, face->texture);
|
||||
}
|
||||
else{
|
||||
gl__bind_any_texture(t);
|
||||
}
|
||||
|
||||
// NOTE(simon, 29/03/24): 4coder doesn't appear to clip character outside the screen
|
||||
// horizontally. Even with line wrapping enabled, you can have cases where the line
|
||||
// won't wrap, for example "{0,0,0,0,...}" with a lot of zero and no space will not
|
||||
// wrap. The consequence of that is that we might send a lot of vertex data that's
|
||||
// offscreen and the assumption about the vertex buffer size I made, can be wrong.
|
||||
// So in this loop we release the previous vertex and create a new one when necessary.
|
||||
u32 size_required = vertex_count * sizeof( Render_Vertex );
|
||||
|
||||
if ( size_required > vertex_buffer_desc.ByteWidth ) {
|
||||
|
||||
u32 new_size = vertex_buffer_desc.ByteWidth * 2;
|
||||
|
||||
while ( new_size < size_required ) {
|
||||
new_size *= 2;
|
||||
}
|
||||
|
||||
// NOTE(simon, 29/03/24): Create a new buffer and only release the previous one if
|
||||
// the creation succeeded. If the creation fails, we skip this vertex group, which
|
||||
// means the user will see an empty panel, but at least we won't stop rendering.
|
||||
D3D11_BUFFER_DESC new_vertex_buffer_desc = vertex_buffer_desc;
|
||||
new_vertex_buffer_desc.ByteWidth = new_size;
|
||||
ID3D11Buffer* new_vertex_buffer = 0;
|
||||
hr = g_dx11.device->CreateBuffer( &new_vertex_buffer_desc, 0, &new_vertex_buffer );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
g_dx11.vertex_buffer->Release( );
|
||||
g_dx11.vertex_buffer = new_vertex_buffer;
|
||||
vertex_buffer_desc.ByteWidth = new_size;
|
||||
|
||||
u32 stride = sizeof( Render_Vertex );
|
||||
u32 offset = 0;
|
||||
g_dx11.context->IASetVertexBuffers( 0, 1, &g_dx11.vertex_buffer, &stride, &offset );
|
||||
}
|
||||
|
||||
// NOTE(simon, 28/02/24): We fill the buffer, draw what we filled and then do the next
|
||||
// group, which allows to always start drawing from vertex 0. Alternatively we could
|
||||
// do a pass to fill the vertex buffer completly so we only map the vertex buffer once,
|
||||
// and then a second pass that just execute the draw calls. It doesn't seems necessary
|
||||
// since we have less than 10 draw call.
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE vertex_map = { };
|
||||
hr = g_dx11.context->Map( ( ID3D11Resource* ) g_dx11.vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &vertex_map );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
// NOTE(simon, 28/02/24): It's improbable that Map will fail, but if it does we
|
||||
// just stop rendering, and we'll try on the next frame. We could just skip the
|
||||
// group and try with the next (using 'continue' instead of 'break'), but Map would
|
||||
// probably fail again. Waiting for the next frame "might" work. I don't really
|
||||
// know. We could also just exit the application assuming we won't be able to
|
||||
// render anything.
|
||||
break;
|
||||
}
|
||||
|
||||
u8* bytes = ( u8* ) vertex_map.pData;
|
||||
|
||||
for (Render_Vertex_Array_Node *node = group->vertex_list.first;
|
||||
node != 0;
|
||||
node = node->next){
|
||||
|
||||
i32 size = node->vertex_count*sizeof(*node->vertices);
|
||||
memcpy( bytes, node->vertices, size );
|
||||
bytes += size;
|
||||
}
|
||||
|
||||
g_dx11.context->Unmap( ( ID3D11Resource* ) g_dx11.vertex_buffer, 0 );
|
||||
|
||||
g_dx11.context->Draw( vertex_count, 0 );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,8 @@
|
|||
// #define FPS 144
|
||||
// #define frame_useconds (1000000 / FPS)
|
||||
|
||||
// #define WIN32_DX11
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "4coder_base_types.h"
|
||||
|
@ -56,7 +58,6 @@
|
|||
#define function static
|
||||
|
||||
#include "win32_utf8.h"
|
||||
#include "win32_gl.h"
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
|
@ -447,6 +448,7 @@ win32_post_clipboard(Arena *scratch, char *text, i32 len){
|
|||
SetClipboardData(CF_TEXT, memory_handle);
|
||||
}
|
||||
CloseClipboard();
|
||||
win32vars.clipboard_sequence = GetClipboardSequenceNumber();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -455,13 +457,9 @@ system_get_clipboard_sig(){
|
|||
String_Const_u8 result = {};
|
||||
DWORD new_number = GetClipboardSequenceNumber();
|
||||
if (new_number != win32vars.clipboard_sequence){
|
||||
win32vars.clipboard_sequence = new_number;
|
||||
|
||||
for (i32 R = 0; R < 8; ++R){
|
||||
result = win32_read_clipboard_contents(win32vars.tctx, arena);
|
||||
if (result.str == 0){
|
||||
break;
|
||||
}
|
||||
if (result.str != 0){
|
||||
win32vars.clipboard_sequence = new_number;
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
|
@ -649,18 +647,18 @@ system_cli_end_update_sig(){
|
|||
|
||||
function void
|
||||
os_popup_error(char *title, char *message){
|
||||
MessageBoxA(0, title, message, MB_OK);
|
||||
MessageBoxA(0, message, title, MB_OK);
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
#include "4ed_font_provider_freetype.h"
|
||||
#include "4ed_font_provider_freetype.cpp"
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include "opengl/4ed_opengl_defines.h"
|
||||
#define GL_FUNC(N,R,P) typedef R (CALL_CONVENTION N##_Function)P; N##_Function *N = 0;
|
||||
#include "opengl/4ed_opengl_funcs.h"
|
||||
#include "opengl/4ed_opengl_render.cpp"
|
||||
#if defined( WIN32_DX11 )
|
||||
#include "win32_dx11.cpp"
|
||||
#else
|
||||
#include "win32_opengl.cpp"
|
||||
#endif
|
||||
|
||||
internal
|
||||
graphics_get_texture_sig(){
|
||||
|
@ -672,6 +670,11 @@ graphics_fill_texture_sig(){
|
|||
return(gl__fill_texture(texture_kind, texture, p, dim, data));
|
||||
}
|
||||
|
||||
internal
|
||||
graphics_free_texture_sig(){
|
||||
gl__free_texture(texid);
|
||||
}
|
||||
|
||||
internal
|
||||
font_make_face_sig(){
|
||||
return(ft__font_make_face(arena, description, scale_factor));
|
||||
|
@ -779,13 +782,13 @@ win32_keycode_init(void){
|
|||
keycode_lookup_table[VK_NUMPAD8] = KeyCode_NumPad8;
|
||||
keycode_lookup_table[VK_NUMPAD9] = KeyCode_NumPad9;
|
||||
|
||||
for (i32 i = 0xDF; i < 0xFF; i += 1){
|
||||
keycode_lookup_table[i] = KeyCode_Ex0 + 1;
|
||||
for (i32 i = 0; i < 30; i += 1){
|
||||
keycode_lookup_table[0xDF + i] = KeyCode_Ex0 + i;
|
||||
}
|
||||
}
|
||||
|
||||
internal b32
|
||||
keycode_physical_translaion_is_wrong(u64 vk){
|
||||
keycode_physical_translation_is_wrong(u64 vk){
|
||||
b32 result = false;
|
||||
switch (vk){
|
||||
case VK_UP: case VK_DOWN: case VK_LEFT: case VK_RIGHT:
|
||||
|
@ -823,6 +826,73 @@ win32_resize(i32 width, i32 height){
|
|||
if (width > 0 && height > 0){
|
||||
target.width = width;
|
||||
target.height = height;
|
||||
|
||||
#if defined( WIN32_DX11 )
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
ID3D11Texture2D* frame_buffer = 0;
|
||||
|
||||
do {
|
||||
|
||||
if ( g_dx11.initialized ) {
|
||||
|
||||
if ( g_dx11.render_target_view ) {
|
||||
g_dx11.render_target_view->Release( );
|
||||
g_dx11.render_target_view = 0;
|
||||
}
|
||||
|
||||
hr = g_dx11.swap_chain->ResizeBuffers( 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0 );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( "Failed to resize the swap chain buffers.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
hr = g_dx11.swap_chain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), ( void** ) &frame_buffer );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( "Failled to get the swap chain back buffer.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
D3D11_RENDER_TARGET_VIEW_DESC render_target_view_desc = { 0 };
|
||||
// NOTE(simon, 28/02/24): 4coder checks for sRGB support but never actually enables
|
||||
// it in the OpenGL version (never calls glEnable( GL_FRAMEBUFFER_SRGB ) ).
|
||||
// Note that enabling it would require to convert collors
|
||||
// passed to the shader to linear (when using sRBG back buffer, the shader values
|
||||
// must be linear values). This would be more problematic than just passing linear
|
||||
// values as the blending wouldn't produce the same result as with sRGB off.
|
||||
// render_target_view_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
||||
render_target_view_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
render_target_view_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
|
||||
|
||||
hr = g_dx11.device->CreateRenderTargetView( ( ID3D11Resource* ) frame_buffer, &render_target_view_desc, &g_dx11.render_target_view );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( "Failed to create a render target view.\n" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} while ( 0 );
|
||||
|
||||
if ( frame_buffer ) {
|
||||
frame_buffer->Release( );
|
||||
frame_buffer = 0;
|
||||
}
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
|
||||
if ( g_dx11.render_target_view ) {
|
||||
g_dx11.render_target_view->Release( );
|
||||
g_dx11.render_target_view = 0;
|
||||
}
|
||||
|
||||
// NOTE(simon, 28/02/24): Failing here means no rendering possible, so we exit.
|
||||
exit( 1 );
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1157,7 +1227,7 @@ win32_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
|
|||
u64 vk = wParam;
|
||||
|
||||
if (win32vars.key_mode == KeyMode_Physical &&
|
||||
!keycode_physical_translaion_is_wrong(vk)){
|
||||
!keycode_physical_translation_is_wrong(vk)){
|
||||
UINT scan_code = ((lParam >> 16) & bitmask_8);
|
||||
vk = MapVirtualKeyEx(scan_code, MAPVK_VSC_TO_VK_EX, win32vars.kl_universal);
|
||||
}
|
||||
|
@ -1432,249 +1502,7 @@ win32_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
|
|||
|
||||
//-
|
||||
|
||||
internal b32
|
||||
win32_wgl_good(Void_Func *f){
|
||||
return(f != 0 &&
|
||||
f != (Void_Func*)1 &&
|
||||
f != (Void_Func*)2 &&
|
||||
f != (Void_Func*)3 &&
|
||||
f != (Void_Func*)-1);
|
||||
}
|
||||
|
||||
typedef HGLRC (CALL_CONVENTION wglCreateContextAttribsARB_Function)(HDC,HGLRC,i32*);
|
||||
typedef BOOL (CALL_CONVENTION wglChoosePixelFormatARB_Function)(HDC,i32*,f32*,u32,i32*,u32*);
|
||||
typedef char* (CALL_CONVENTION wglGetExtensionsStringEXT_Function)();
|
||||
typedef VOID (CALL_CONVENTION wglSwapIntervalEXT_Function)(i32);
|
||||
|
||||
global wglCreateContextAttribsARB_Function *wglCreateContextAttribsARB = 0;
|
||||
global wglChoosePixelFormatARB_Function *wglChoosePixelFormatARB = 0;
|
||||
global wglGetExtensionsStringEXT_Function *wglGetExtensionsStringEXT = 0;
|
||||
global wglSwapIntervalEXT_Function *wglSwapIntervalEXT = 0;
|
||||
|
||||
internal b32
|
||||
win32_gl_create_window(HWND *wnd_out, HGLRC *context_out, DWORD style, RECT rect){
|
||||
HINSTANCE this_instance = GetModuleHandle(0);
|
||||
|
||||
local_persist b32 srgb_support = false;
|
||||
local_persist b32 register_success = true;
|
||||
local_persist b32 first_call = true;
|
||||
if (first_call){
|
||||
log_os(" GL bootstrapping...\n");
|
||||
|
||||
first_call = false;
|
||||
|
||||
// NOTE(allen): Create the GL bootstrap window
|
||||
log_os(" registering bootstrap class...\n");
|
||||
WNDCLASSW wglclass = {};
|
||||
wglclass.lpfnWndProc = DefWindowProcW;
|
||||
wglclass.hInstance = this_instance;
|
||||
wglclass.lpszClassName = L"wgl-loader";
|
||||
if (RegisterClassW(&wglclass) == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" creating bootstrap window...\n");
|
||||
HWND wglwindow = CreateWindowW(wglclass.lpszClassName, L"", 0, 0, 0, 0, 0,
|
||||
0, 0, this_instance, 0);
|
||||
if (wglwindow == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
// NOTE(allen): Create the GL bootstrap context
|
||||
log_os(" setting bootstrap pixel format...\n");
|
||||
|
||||
HDC wgldc = GetDC(wglwindow);
|
||||
|
||||
PIXELFORMATDESCRIPTOR format = {};
|
||||
format.nSize = sizeof(format);
|
||||
format.nVersion = 1;
|
||||
format.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
|
||||
format.iPixelType = PFD_TYPE_RGBA;
|
||||
format.cColorBits = 32;
|
||||
format.cAlphaBits = 8;
|
||||
format.cDepthBits = 24;
|
||||
format.iLayerType = PFD_MAIN_PLANE;
|
||||
i32 suggested_format_index = ChoosePixelFormat(wgldc, &format);
|
||||
if (!SetPixelFormat(wgldc, suggested_format_index, &format)){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" creating bootstrap GL context...\n");
|
||||
HGLRC wglcontext = wglCreateContext(wgldc);
|
||||
if (wglcontext == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" making bootstrap GL context current...\n");
|
||||
if (!wglMakeCurrent(wgldc, wglcontext)){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
// NOTE(allen): Load wgl extensions
|
||||
log_os(" loading wgl extensions...\n");
|
||||
|
||||
#define LoadWGL(f,l) Stmnt((f) = (f##_Function*)wglGetProcAddress(#f); \
|
||||
(l) = (l) && win32_wgl_good((Void_Func*)(f));)
|
||||
|
||||
b32 load_success = true;
|
||||
LoadWGL(wglCreateContextAttribsARB, load_success);
|
||||
LoadWGL(wglChoosePixelFormatARB, load_success);
|
||||
LoadWGL(wglGetExtensionsStringEXT, load_success);
|
||||
|
||||
if (!load_success){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" checking wgl extensions...\n");
|
||||
char *extensions_c = wglGetExtensionsStringEXT();
|
||||
String_Const_u8 extensions = SCu8((u8*)extensions_c);
|
||||
|
||||
{
|
||||
String_Const_u8 s = string_skip_whitespace(extensions);
|
||||
for (;s.size > 0;){
|
||||
u64 end = string_find_first_whitespace(s);
|
||||
String_Const_u8 m = string_prefix(s, end);
|
||||
if (string_match(m, string_u8_litexpr("WGL_EXT_framebuffer_sRGB")) ||
|
||||
string_match(m, string_u8_litexpr("WGL_ARB_framebuffer_sRGB"))){
|
||||
srgb_support = true;
|
||||
}
|
||||
else if (string_match(m, string_u8_litexpr("WGL_EXT_swap_interval"))){
|
||||
b32 wgl_swap_interval_ext = true;
|
||||
LoadWGL(wglSwapIntervalEXT, wgl_swap_interval_ext);
|
||||
if (!wgl_swap_interval_ext){
|
||||
wglSwapIntervalEXT = 0;
|
||||
}
|
||||
}
|
||||
s = string_skip_whitespace(string_skip(s, end));
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(allen): Load gl functions
|
||||
log_os(" loading core GL functions...\n");
|
||||
|
||||
#define GL_FUNC(f,R,P) LoadWGL(f,load_success);
|
||||
#include "opengl/4ed_opengl_funcs.h"
|
||||
|
||||
if (!load_success){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
// NOTE(allen): Cleanup the GL bootstrap resources
|
||||
log_os(" cleaning up boostrap resources...\n");
|
||||
|
||||
ReleaseDC(wglwindow, wgldc);
|
||||
DestroyWindow(wglwindow);
|
||||
wglDeleteContext(wglcontext);
|
||||
|
||||
// NOTE(allen): Register the graphics window class
|
||||
log_os(" registering graphics class...\n");
|
||||
|
||||
WNDCLASSW wndclass = {};
|
||||
wndclass.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
|
||||
wndclass.lpfnWndProc = win32_proc;
|
||||
wndclass.hIcon = LoadIconW(GetModuleHandle(0), L"main");
|
||||
wndclass.hInstance = this_instance;
|
||||
wndclass.lpszClassName = L"GRAPHICS-WINDOW-NAME";
|
||||
if (RegisterClassW(&wndclass) == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
}
|
||||
fail_register:;
|
||||
|
||||
b32 result = false;
|
||||
if (register_success){
|
||||
// NOTE(allen): Create the graphics window
|
||||
log_os(" creating graphics window...\n");
|
||||
|
||||
HWND wnd = CreateWindowExW(0, L"GRAPHICS-WINDOW-NAME", L"GRAPHICS", style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top,
|
||||
0, 0, this_instance, 0);
|
||||
|
||||
*wnd_out = 0;
|
||||
*context_out = 0;
|
||||
if (wnd != 0){
|
||||
log_os(" setting graphics pixel format...\n");
|
||||
|
||||
HDC dc = GetDC(wnd);
|
||||
|
||||
PIXELFORMATDESCRIPTOR format = {};
|
||||
|
||||
i32 pixel_attrib_list[] = {
|
||||
/* 0*/WGL_DRAW_TO_WINDOW_ARB, TRUE,
|
||||
/* 2*/WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
|
||||
/* 4*/WGL_SUPPORT_OPENGL_ARB, TRUE,
|
||||
/* 6*/WGL_DOUBLE_BUFFER_ARB, false,
|
||||
/* 8*/WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
|
||||
/*10*/WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, GL_TRUE,
|
||||
/*12*/0,
|
||||
};
|
||||
if (!srgb_support){
|
||||
pixel_attrib_list[10] = 0;
|
||||
}
|
||||
|
||||
i32 suggested_format_index = 0;
|
||||
u32 ignore = 0;
|
||||
if (!wglChoosePixelFormatARB(dc, pixel_attrib_list, 0, 1, &suggested_format_index, &ignore)){
|
||||
goto fail_window_init;
|
||||
}
|
||||
|
||||
DescribePixelFormat(dc, suggested_format_index, sizeof(format), &format);
|
||||
if (!SetPixelFormat(dc, suggested_format_index, &format)){
|
||||
goto fail_window_init;
|
||||
}
|
||||
|
||||
log_os(" setting graphics attributes...\n");
|
||||
|
||||
i32 context_attrib_list[] = {
|
||||
/*0*/WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
||||
/*2*/WGL_CONTEXT_MINOR_VERSION_ARB, 2,
|
||||
/*4*/WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
|
||||
#if GL_DEBUG_MODE
|
||||
|WGL_CONTEXT_DEBUG_BIT_ARB
|
||||
#endif
|
||||
,
|
||||
/*6*/WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
/*8*/0
|
||||
};
|
||||
|
||||
log_os(" creating graphics GL context...\n");
|
||||
HGLRC context = wglCreateContextAttribsARB(dc, 0, context_attrib_list);
|
||||
if (context == 0){
|
||||
goto fail_window_init;
|
||||
}
|
||||
|
||||
log_os(" making graphics GL context current...\n");
|
||||
wglMakeCurrent(dc, context);
|
||||
|
||||
|
||||
if (wglSwapIntervalEXT != 0){
|
||||
log_os(" setting swap interval...\n");
|
||||
wglSwapIntervalEXT(1);
|
||||
}
|
||||
*wnd_out = wnd;
|
||||
*context_out = context;
|
||||
result = true;
|
||||
|
||||
if (false){
|
||||
fail_window_init:;
|
||||
DWORD error = GetLastError();
|
||||
ReleaseDC(wnd, dc);
|
||||
DestroyWindow(wnd);
|
||||
SetLastError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
//-
|
||||
|
||||
|
@ -1918,15 +1746,21 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS
|
|||
window_style |= WS_MAXIMIZE;
|
||||
}
|
||||
log_os(" windowed dimensions: %d, %d\n"
|
||||
" initially maximized: %d",
|
||||
" initially maximized: %d\n",
|
||||
window_rect.right - window_rect.left,
|
||||
window_rect.bottom - window_rect.top,
|
||||
((window_style & WS_MAXIMIZE) != 0));
|
||||
|
||||
#if defined( WIN32_DX11 )
|
||||
if( !win32_gl_create_window( &win32vars.window_handle, window_style, window_rect ) ) {
|
||||
exit(1);
|
||||
}
|
||||
#else
|
||||
HGLRC window_opengl_context = 0;
|
||||
if (!win32_gl_create_window(&win32vars.window_handle, &window_opengl_context, window_style, window_rect)){
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
log_os(" window created successfully\n");
|
||||
|
||||
|
@ -2210,10 +2044,15 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS
|
|||
win32vars.lctrl_lalt_is_altgr = (b8)result.lctrl_lalt_is_altgr;
|
||||
|
||||
// NOTE(allen): render
|
||||
#if defined( WIN32_DX11 )
|
||||
gl_render(&target);
|
||||
g_dx11.swap_chain->Present( 1, 0 );
|
||||
#else
|
||||
HDC hdc = GetDC(win32vars.window_handle);
|
||||
gl_render(&target);
|
||||
SwapBuffers(hdc);
|
||||
ReleaseDC(win32vars.window_handle, hdc);
|
||||
#endif
|
||||
|
||||
// NOTE(allen): toggle full screen
|
||||
if (win32vars.do_toggle){
|
||||
|
@ -2249,6 +2088,10 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS
|
|||
win32vars.first = false;
|
||||
}
|
||||
|
||||
#if defined( WIN32_DX11 ) && !SHIP_MODE
|
||||
win32_gl_cleanup( );
|
||||
#endif
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,544 @@
|
|||
|
||||
#pragma comment(lib, "d3d11.lib")
|
||||
#pragma comment(lib, "dxgi.lib")
|
||||
#pragma comment(lib, "d3dcompiler.lib")
|
||||
|
||||
#include <initguid.h>
|
||||
#include <d3d11_1.h>
|
||||
#include <dxgi1_3.h>
|
||||
#include <d3dcompiler.h>
|
||||
|
||||
#if !SHIP_MODE
|
||||
#include <dxgidebug.h>
|
||||
IDXGIDebug1* dxgi_debug;
|
||||
#endif
|
||||
|
||||
#include "4ed_dx11_render.cpp"
|
||||
|
||||
internal LRESULT CALL_CONVENTION
|
||||
win32_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
internal b32
|
||||
win32_gl_create_window(HWND *wnd_out, DWORD style, RECT rect){
|
||||
|
||||
local_persist b32 srgb_support = false;
|
||||
local_persist b32 first_call = true;
|
||||
|
||||
b32 result = false;
|
||||
|
||||
*wnd_out = 0;
|
||||
|
||||
Assert( g_dx11.initialized == 0 );
|
||||
Assert( g_dx11.device == 0 );
|
||||
Assert( g_dx11.context == 0 );
|
||||
Assert( g_dx11.swap_chain == 0 );
|
||||
Assert( g_dx11.sampler == 0 );
|
||||
Assert( g_dx11.render_target_view == 0 );
|
||||
Assert( g_dx11.gpu_program.vertex == 0 );
|
||||
Assert( g_dx11.gpu_program.layout == 0 );
|
||||
Assert( g_dx11.gpu_program.pixel == 0 );
|
||||
Assert( g_dx11.gpu_program.valid == 0 );
|
||||
Assert( g_dx11.vertex_buffer == 0 );
|
||||
Assert( g_dx11.constants_buffer == 0 );
|
||||
Assert( g_dx11.texture_count == 0 );
|
||||
|
||||
g_dx11 = { };
|
||||
|
||||
HINSTANCE this_instance = GetModuleHandle(0);
|
||||
|
||||
HWND wnd = 0;
|
||||
|
||||
ID3D11Device* base_device = 0;
|
||||
ID3D11DeviceContext* base_device_context = 0;
|
||||
IDXGIDevice1* dxgi_device = 0;
|
||||
IDXGIAdapter* dxgi_adapter = 0;
|
||||
IDXGIFactory2* dxgi_factory = 0;
|
||||
|
||||
ID3D11BlendState* blend_state = 0;
|
||||
ID3D11RasterizerState1* rasterizer_state = 0;
|
||||
|
||||
do {
|
||||
|
||||
// NOTE(simon, 28/02/24): There is nothing in the code suggesting that this function could
|
||||
// be called several time. If it is called several time, we would need to make sure that
|
||||
// we cleaned up previous dx11 resources. The reason this function could be called twice
|
||||
// would be if it failed previously, and in that case we clean up everything before
|
||||
// exiting the function so we should be good. Still we assume it's only ever call once.
|
||||
Assert( first_call );
|
||||
|
||||
if (first_call){
|
||||
|
||||
first_call = false;
|
||||
|
||||
log_os( " Registering graphics class...\n" );
|
||||
|
||||
WNDCLASSW wndclass = {};
|
||||
wndclass.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
|
||||
wndclass.lpfnWndProc = win32_proc;
|
||||
wndclass.hIcon = LoadIconW(GetModuleHandle(0), L"main");
|
||||
wndclass.hInstance = this_instance;
|
||||
wndclass.lpszClassName = L"GRAPHICS-WINDOW-NAME";
|
||||
if (RegisterClassW(&wndclass) == 0){
|
||||
log_os(" Failed.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log_os( " Creating graphics window...\n" );
|
||||
|
||||
wnd = CreateWindowExW(0, L"GRAPHICS-WINDOW-NAME", L"GRAPHICS", style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top,
|
||||
0, 0, this_instance, 0);
|
||||
|
||||
if (wnd == 0) {
|
||||
log_os( " Failed.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
log_os( " Creating a d3d11 hardware device and context...\n" );
|
||||
// NOTE(simon, 28/02/24): We are creating a directx 11.1 device and context (supported
|
||||
// since windows 8).
|
||||
D3D_FEATURE_LEVEL feature_levels[ ] = { D3D_FEATURE_LEVEL_11_1 };
|
||||
|
||||
u32 device_flags = 0;
|
||||
// device_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
|
||||
|
||||
#if !SHIP_MODE
|
||||
device_flags |= D3D11_CREATE_DEVICE_DEBUG;
|
||||
#endif
|
||||
|
||||
HRESULT hr = D3D11CreateDevice( 0, D3D_DRIVER_TYPE_HARDWARE, 0, device_flags, feature_levels, ArrayCount( feature_levels ), D3D11_SDK_VERSION, &base_device, 0, &base_device_context );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
log_os( " Creating a d3d11 software (WARP) device and context...\n" );
|
||||
// NOTE(simon, 28/02/24): Try creating a high performance software device as a fallback.
|
||||
hr = D3D11CreateDevice( 0, D3D_DRIVER_TYPE_WARP, 0, device_flags, feature_levels, ArrayCount( feature_levels ), D3D11_SDK_VERSION, &base_device, 0, &base_device_context );
|
||||
}
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
log_os( " Creating a ID3D11Device1...\n" );
|
||||
hr = base_device->QueryInterface( __uuidof( ID3D11Device1 ), ( void** ) &g_dx11.device );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
log_os( " Creating a ID3D11DeviceContext1...\n" );
|
||||
hr = base_device_context->QueryInterface( __uuidof( ID3D11DeviceContext1 ), ( void** ) &g_dx11.context );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
ID3D11Device1* device = g_dx11.device;
|
||||
ID3D11DeviceContext1* context = g_dx11.context;
|
||||
|
||||
#if !SHIP_MODE
|
||||
log_os( " Getting ID3D11InfoQueue. This is not important if you're not debugging graphics...\n" );
|
||||
ID3D11InfoQueue* info;
|
||||
hr = device->QueryInterface( __uuidof( ID3D11InfoQueue ), ( void** ) &info );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
} else {
|
||||
info->SetBreakOnSeverity( D3D11_MESSAGE_SEVERITY_CORRUPTION, TRUE );
|
||||
info->SetBreakOnSeverity( D3D11_MESSAGE_SEVERITY_ERROR, TRUE );
|
||||
info->Release( );
|
||||
}
|
||||
|
||||
log_os( " Getting IDXGIDebug1. This is not important if you're not debugging graphics...\n" );
|
||||
hr = DXGIGetDebugInterface1( 0, __uuidof( IDXGIDebug1 ), ( void** ) &dxgi_debug );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
}
|
||||
#endif
|
||||
|
||||
// NOTE(simon, 28/02/24): sRGB should be supported by any hardware now, but there was a
|
||||
// check so I added one. The OpenGL version never enables sRGB.
|
||||
DXGI_FORMAT back_buffer_format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
||||
UINT format_support = 0;
|
||||
|
||||
if ( SUCCEEDED( device->CheckFormatSupport( back_buffer_format, &format_support ) ) ) {
|
||||
u32 required = D3D11_FORMAT_SUPPORT_RENDER_TARGET | D3D11_FORMAT_SUPPORT_DISPLAY;
|
||||
srgb_support = ( ( format_support & required ) == required );
|
||||
}
|
||||
|
||||
if ( !srgb_support ) {
|
||||
log_os( " sRBG back buffer not supported.\n" );
|
||||
back_buffer_format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
} else {
|
||||
log_os( " sRBG back buffer supported.\n" );
|
||||
}
|
||||
|
||||
log_os( " Getting a IDXGIFactory2...\n" );
|
||||
|
||||
log_os( " Qurey for the IDXGIDevice1...\n" );
|
||||
hr = device->QueryInterface( __uuidof( IDXGIDevice1 ), ( void** ) &dxgi_device );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
log_os( " Getting the IDXGIAdapter...\n" );
|
||||
dxgi_device->GetAdapter( &dxgi_adapter );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
log_os( " Getting the IDXGIFactor2...\n" );
|
||||
dxgi_adapter->GetParent( __uuidof( IDXGIFactory2 ), ( void** ) &dxgi_factory );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = { };
|
||||
// NOTE(simon, 28/02/24): Can't request sRGB format here when using FLIP_* swap chain. It's
|
||||
// requested when creating the render target view.
|
||||
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
swap_chain_desc.SampleDesc.Count = 1;
|
||||
swap_chain_desc.SampleDesc.Quality = 0;
|
||||
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
swap_chain_desc.BufferCount = 2;
|
||||
swap_chain_desc.Scaling = DXGI_SCALING_NONE;
|
||||
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
|
||||
log_os( " Creating a IDXGISwapChain1...\n" );
|
||||
hr = dxgi_factory->CreateSwapChainForHwnd( device, wnd, &swap_chain_desc, 0, 0, &g_dx11.swap_chain );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
log_os( " Prevent DXGI handling ALT + ENTER...\n" );
|
||||
hr = dxgi_factory->MakeWindowAssociation( wnd, DXGI_MWA_NO_ALT_ENTER );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
}
|
||||
|
||||
// NOTE(simon, 28/02/24): We setup alpha blending here as it's always on in 4coder.
|
||||
D3D11_BLEND_DESC blend_state_desc = { };
|
||||
blend_state_desc.RenderTarget[ 0 ].BlendEnable = TRUE;
|
||||
blend_state_desc.RenderTarget[ 0 ].SrcBlend = D3D11_BLEND_SRC_ALPHA;
|
||||
blend_state_desc.RenderTarget[ 0 ].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
|
||||
blend_state_desc.RenderTarget[ 0 ].BlendOp = D3D11_BLEND_OP_ADD;
|
||||
blend_state_desc.RenderTarget[ 0 ].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
|
||||
blend_state_desc.RenderTarget[ 0 ].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
|
||||
blend_state_desc.RenderTarget[ 0 ].BlendOpAlpha = D3D11_BLEND_OP_ADD;
|
||||
blend_state_desc.RenderTarget[ 0 ].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
|
||||
|
||||
log_os( " Creating a blend state...\n" );
|
||||
hr = device->CreateBlendState( &blend_state_desc, &blend_state );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
context->OMSetBlendState( blend_state, 0, 0xffffffff );
|
||||
|
||||
// NOTE(simon, 28/02/24): Enable scissor and disable backface culling.
|
||||
D3D11_RASTERIZER_DESC1 rasterizer_desc = { };
|
||||
rasterizer_desc.FillMode = D3D11_FILL_SOLID;
|
||||
rasterizer_desc.CullMode = D3D11_CULL_NONE;
|
||||
rasterizer_desc.DepthClipEnable = TRUE;
|
||||
rasterizer_desc.ScissorEnable = TRUE;
|
||||
|
||||
log_os( " Creating a rasterizer state...\n" );
|
||||
hr = device->CreateRasterizerState1( &rasterizer_desc, &rasterizer_state );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
context->RSSetState( rasterizer_state );
|
||||
|
||||
// NOTE(simon, 28/02/24): Not setting depth stencil as 4coder doesn't use it.
|
||||
// NOTE(simon, 28/02/24): Swap interval is a parameter of swap_chain->present.
|
||||
|
||||
D3D11_SAMPLER_DESC linear_desc = { };
|
||||
linear_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
linear_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
linear_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
linear_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
linear_desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
|
||||
linear_desc.MinLOD = 0;
|
||||
linear_desc.MaxLOD = D3D11_FLOAT32_MAX;
|
||||
|
||||
log_os( " Creating a sampler state...\n" );
|
||||
hr = device->CreateSamplerState( &linear_desc, &g_dx11.sampler );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE(simon, 28/02/24): We create the vertex buffer, constants buffers and shader here
|
||||
// because if we can't create them we won't be able to render anything and so we should
|
||||
// just exit the program.
|
||||
|
||||
D3D11_BUFFER_DESC vertex_buffer_desc = { };
|
||||
// NOTE(simon, 28/02/24): Reserving 400K vertices which is about 11 megabytes and would
|
||||
// allow 100K characters. On a 1080p monitor, with 4 by 10 pixels characters we would need
|
||||
// (1920/4)*(1080/10) = 51840 characters to fill the screen.
|
||||
vertex_buffer_desc.ByteWidth = 400000 * sizeof( Render_Vertex );
|
||||
vertex_buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
vertex_buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
||||
vertex_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
|
||||
log_os( " Creating a vertex buffer...\n" );
|
||||
hr = device->CreateBuffer( &vertex_buffer_desc, 0, &g_dx11.vertex_buffer );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
D3D11_BUFFER_DESC constants_buffer_desc = { };
|
||||
// NOTE(simon, 28/02/24): constants buffer size needs to be a multiple of 16.
|
||||
// NOTE(simon, 28/02/24): The layout is explained where we set the values in the buffer in
|
||||
// gl_render.
|
||||
constants_buffer_desc.ByteWidth = 32;
|
||||
constants_buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
constants_buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
||||
constants_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
|
||||
log_os( " Creating a constants buffer...\n" );
|
||||
hr = device->CreateBuffer( &constants_buffer_desc, 0, &g_dx11.constants_buffer );
|
||||
|
||||
if ( FAILED( hr ) ) {
|
||||
log_os( " Failed.\n" );
|
||||
break;
|
||||
}
|
||||
|
||||
g_dx11.gpu_program = gl__make_program( gl__vertex, gl__fragment );
|
||||
|
||||
if ( !g_dx11.gpu_program.valid ) {
|
||||
break;
|
||||
}
|
||||
|
||||
*wnd_out = wnd;
|
||||
// NOTE(simon, 28/02/24): Reserve the first texture slot as a invalid/unbind texture.
|
||||
g_dx11.texture_count = 1;
|
||||
g_dx11.initialized = true;
|
||||
result = true;
|
||||
|
||||
} while ( 0 );
|
||||
|
||||
if ( !result ) {
|
||||
|
||||
if ( wnd ) {
|
||||
DestroyWindow( wnd );
|
||||
( *wnd_out ) = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.context ) {
|
||||
g_dx11.context->OMSetBlendState( 0, 0, 0xffffffff );
|
||||
g_dx11.context->RSSetState( 0 );
|
||||
}
|
||||
|
||||
if ( g_dx11.constants_buffer ) {
|
||||
g_dx11.constants_buffer->Release( );
|
||||
g_dx11.constants_buffer = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.vertex_buffer ) {
|
||||
g_dx11.vertex_buffer->Release( );
|
||||
g_dx11.vertex_buffer = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.gpu_program.valid ) {
|
||||
|
||||
if ( g_dx11.gpu_program.vertex ) {
|
||||
g_dx11.gpu_program.vertex->Release( );
|
||||
g_dx11.gpu_program.vertex = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.gpu_program.layout ) {
|
||||
g_dx11.gpu_program.layout->Release( );
|
||||
g_dx11.gpu_program.layout = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.gpu_program.pixel ) {
|
||||
g_dx11.gpu_program.pixel->Release( );
|
||||
g_dx11.gpu_program.pixel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(simon, 28/02/24): No render target view at this point as it's created in the
|
||||
// WM_SIZE message.
|
||||
|
||||
if ( g_dx11.sampler ) {
|
||||
g_dx11.sampler->Release( );
|
||||
g_dx11.sampler = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.swap_chain ) {
|
||||
g_dx11.swap_chain->Release( );
|
||||
g_dx11.swap_chain = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.context ) {
|
||||
g_dx11.context->Release( );
|
||||
g_dx11.context = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.device ) {
|
||||
g_dx11.device->Release( );
|
||||
g_dx11.device = 0;
|
||||
}
|
||||
|
||||
g_dx11.initialized = false;
|
||||
|
||||
#if SHIP_MODE
|
||||
os_popup_error( "Error", "Window creation failed.");
|
||||
#endif
|
||||
}
|
||||
|
||||
if( base_device ) {
|
||||
base_device->Release( );
|
||||
base_device = 0;
|
||||
}
|
||||
|
||||
if ( base_device_context ) {
|
||||
base_device_context->Release( );
|
||||
base_device_context = 0;
|
||||
}
|
||||
|
||||
if ( dxgi_device ) {
|
||||
dxgi_device->Release( );
|
||||
dxgi_device = 0;
|
||||
}
|
||||
|
||||
if ( dxgi_adapter ) {
|
||||
dxgi_adapter->Release( );
|
||||
dxgi_adapter = 0;
|
||||
}
|
||||
|
||||
if ( dxgi_factory ) {
|
||||
dxgi_factory->Release( );
|
||||
dxgi_factory = 0;
|
||||
}
|
||||
|
||||
if ( blend_state ) {
|
||||
blend_state->Release( );
|
||||
blend_state = 0;
|
||||
}
|
||||
|
||||
if ( rasterizer_state ) {
|
||||
rasterizer_state->Release( );
|
||||
rasterizer_state = 0;
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
#if !SHIP_MODE
|
||||
|
||||
// NOTE(simon, 28/02/24): Only call this when working on 4coder, to make sure we don't do something
|
||||
// stupid. In SHIP_MODE we let the os clean up resources.
|
||||
internal void
|
||||
win32_gl_cleanup( void ) {
|
||||
|
||||
if ( dxgi_debug && g_dx11.initialized ) {
|
||||
|
||||
OutputDebugString( L"win32_gl_cleanup start report...\n" );
|
||||
dxgi_debug->ReportLiveObjects( DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL );
|
||||
|
||||
g_dx11.initialized = false;
|
||||
|
||||
if ( g_dx11.context ) {
|
||||
g_dx11.context->OMSetBlendState( 0, 0, 0xffffffff );
|
||||
g_dx11.context->RSSetState( 0 );
|
||||
}
|
||||
|
||||
for ( u32 i = 1; i < g_dx11.texture_count; i++ ) {
|
||||
|
||||
DX11Texture* texture = g_dx11.textures + i;
|
||||
|
||||
if ( texture->view ) {
|
||||
texture->view->Release( );
|
||||
texture->view = 0;
|
||||
}
|
||||
|
||||
if ( texture->pointer ) {
|
||||
texture->pointer->Release( );
|
||||
texture->pointer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
g_dx11.texture_count = 0;
|
||||
|
||||
if ( g_dx11.constants_buffer ) {
|
||||
g_dx11.constants_buffer->Release( );
|
||||
g_dx11.constants_buffer = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.vertex_buffer ) {
|
||||
g_dx11.vertex_buffer->Release( );
|
||||
g_dx11.vertex_buffer = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.gpu_program.valid ) {
|
||||
|
||||
if ( g_dx11.gpu_program.vertex ) {
|
||||
g_dx11.gpu_program.vertex->Release( );
|
||||
g_dx11.gpu_program.vertex = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.gpu_program.layout ) {
|
||||
g_dx11.gpu_program.layout->Release( );
|
||||
g_dx11.gpu_program.layout = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.gpu_program.pixel ) {
|
||||
g_dx11.gpu_program.pixel->Release( );
|
||||
g_dx11.gpu_program.pixel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( g_dx11.render_target_view ) {
|
||||
g_dx11.render_target_view->Release( );
|
||||
g_dx11.render_target_view = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.sampler ) {
|
||||
g_dx11.sampler->Release( );
|
||||
g_dx11.sampler = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.swap_chain ) {
|
||||
g_dx11.swap_chain->Release( );
|
||||
g_dx11.swap_chain = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.context ) {
|
||||
g_dx11.context->Release( );
|
||||
g_dx11.context = 0;
|
||||
}
|
||||
|
||||
if ( g_dx11.device ) {
|
||||
g_dx11.device->Release( );
|
||||
g_dx11.device = 0;
|
||||
}
|
||||
|
||||
OutputDebugString( L"win32_gl_cleanup end report (nothing printed after this line means everything is OK)...\n" );
|
||||
dxgi_debug->ReportLiveObjects( DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_ALL );
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,257 @@
|
|||
|
||||
#pragma comment(lib, "OpenGL32.lib")
|
||||
|
||||
#include "win32_gl.h"
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include "opengl/4ed_opengl_defines.h"
|
||||
#define GL_FUNC(N,R,P) typedef R (CALL_CONVENTION N##_Function)P; N##_Function *N = 0;
|
||||
#include "opengl/4ed_opengl_funcs.h"
|
||||
#include "opengl/4ed_opengl_render.cpp"
|
||||
|
||||
internal b32
|
||||
win32_wgl_good(Void_Func *f){
|
||||
return(f != 0 &&
|
||||
f != (Void_Func*)1 &&
|
||||
f != (Void_Func*)2 &&
|
||||
f != (Void_Func*)3 &&
|
||||
f != (Void_Func*)-1);
|
||||
}
|
||||
|
||||
typedef HGLRC (CALL_CONVENTION wglCreateContextAttribsARB_Function)(HDC,HGLRC,i32*);
|
||||
typedef BOOL (CALL_CONVENTION wglChoosePixelFormatARB_Function)(HDC,i32*,f32*,u32,i32*,u32*);
|
||||
typedef char* (CALL_CONVENTION wglGetExtensionsStringEXT_Function)();
|
||||
typedef VOID (CALL_CONVENTION wglSwapIntervalEXT_Function)(i32);
|
||||
|
||||
global wglCreateContextAttribsARB_Function *wglCreateContextAttribsARB = 0;
|
||||
global wglChoosePixelFormatARB_Function *wglChoosePixelFormatARB = 0;
|
||||
global wglGetExtensionsStringEXT_Function *wglGetExtensionsStringEXT = 0;
|
||||
global wglSwapIntervalEXT_Function *wglSwapIntervalEXT = 0;
|
||||
|
||||
internal LRESULT CALL_CONVENTION
|
||||
win32_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
internal b32
|
||||
win32_gl_create_window(HWND *wnd_out, HGLRC *context_out, DWORD style, RECT rect){
|
||||
HINSTANCE this_instance = GetModuleHandle(0);
|
||||
|
||||
local_persist b32 srgb_support = false;
|
||||
local_persist b32 register_success = true;
|
||||
local_persist b32 first_call = true;
|
||||
if (first_call){
|
||||
log_os(" GL bootstrapping...\n");
|
||||
|
||||
first_call = false;
|
||||
|
||||
// NOTE(allen): Create the GL bootstrap window
|
||||
log_os(" registering bootstrap class...\n");
|
||||
WNDCLASSW wglclass = {};
|
||||
wglclass.lpfnWndProc = DefWindowProcW;
|
||||
wglclass.hInstance = this_instance;
|
||||
wglclass.lpszClassName = L"wgl-loader";
|
||||
if (RegisterClassW(&wglclass) == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" creating bootstrap window...\n");
|
||||
HWND wglwindow = CreateWindowW(wglclass.lpszClassName, L"", 0, 0, 0, 0, 0,
|
||||
0, 0, this_instance, 0);
|
||||
if (wglwindow == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
// NOTE(allen): Create the GL bootstrap context
|
||||
log_os(" setting bootstrap pixel format...\n");
|
||||
|
||||
HDC wgldc = GetDC(wglwindow);
|
||||
|
||||
PIXELFORMATDESCRIPTOR format = {};
|
||||
format.nSize = sizeof(format);
|
||||
format.nVersion = 1;
|
||||
format.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
|
||||
format.iPixelType = PFD_TYPE_RGBA;
|
||||
format.cColorBits = 32;
|
||||
format.cAlphaBits = 8;
|
||||
format.cDepthBits = 24;
|
||||
format.iLayerType = PFD_MAIN_PLANE;
|
||||
i32 suggested_format_index = ChoosePixelFormat(wgldc, &format);
|
||||
if (!SetPixelFormat(wgldc, suggested_format_index, &format)){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" creating bootstrap GL context...\n");
|
||||
HGLRC wglcontext = wglCreateContext(wgldc);
|
||||
if (wglcontext == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" making bootstrap GL context current...\n");
|
||||
if (!wglMakeCurrent(wgldc, wglcontext)){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
// NOTE(allen): Load wgl extensions
|
||||
log_os(" loading wgl extensions...\n");
|
||||
|
||||
#define LoadWGL(f,l) Stmnt((f) = (f##_Function*)wglGetProcAddress(#f); \
|
||||
(l) = (l) && win32_wgl_good((Void_Func*)(f));)
|
||||
|
||||
b32 load_success = true;
|
||||
LoadWGL(wglCreateContextAttribsARB, load_success);
|
||||
LoadWGL(wglChoosePixelFormatARB, load_success);
|
||||
LoadWGL(wglGetExtensionsStringEXT, load_success);
|
||||
|
||||
if (!load_success){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
log_os(" checking wgl extensions...\n");
|
||||
char *extensions_c = wglGetExtensionsStringEXT();
|
||||
String_Const_u8 extensions = SCu8((u8*)extensions_c);
|
||||
|
||||
{
|
||||
String_Const_u8 s = string_skip_whitespace(extensions);
|
||||
for (;s.size > 0;){
|
||||
u64 end = string_find_first_whitespace(s);
|
||||
String_Const_u8 m = string_prefix(s, end);
|
||||
if (string_match(m, string_u8_litexpr("WGL_EXT_framebuffer_sRGB")) ||
|
||||
string_match(m, string_u8_litexpr("WGL_ARB_framebuffer_sRGB"))){
|
||||
srgb_support = true;
|
||||
}
|
||||
else if (string_match(m, string_u8_litexpr("WGL_EXT_swap_interval"))){
|
||||
b32 wgl_swap_interval_ext = true;
|
||||
LoadWGL(wglSwapIntervalEXT, wgl_swap_interval_ext);
|
||||
if (!wgl_swap_interval_ext){
|
||||
wglSwapIntervalEXT = 0;
|
||||
}
|
||||
}
|
||||
s = string_skip_whitespace(string_skip(s, end));
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(allen): Load gl functions
|
||||
log_os(" loading core GL functions...\n");
|
||||
|
||||
#define GL_FUNC(f,R,P) LoadWGL(f,load_success);
|
||||
#include "opengl/4ed_opengl_funcs.h"
|
||||
|
||||
if (!load_success){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
|
||||
// NOTE(allen): Cleanup the GL bootstrap resources
|
||||
log_os(" cleaning up boostrap resources...\n");
|
||||
|
||||
ReleaseDC(wglwindow, wgldc);
|
||||
DestroyWindow(wglwindow);
|
||||
wglDeleteContext(wglcontext);
|
||||
|
||||
// NOTE(allen): Register the graphics window class
|
||||
log_os(" registering graphics class...\n");
|
||||
|
||||
WNDCLASSW wndclass = {};
|
||||
wndclass.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
|
||||
wndclass.lpfnWndProc = win32_proc;
|
||||
wndclass.hIcon = LoadIconW(GetModuleHandle(0), L"main");
|
||||
wndclass.hInstance = this_instance;
|
||||
wndclass.lpszClassName = L"GRAPHICS-WINDOW-NAME";
|
||||
if (RegisterClassW(&wndclass) == 0){
|
||||
register_success = false;
|
||||
goto fail_register;
|
||||
}
|
||||
}
|
||||
fail_register:;
|
||||
|
||||
b32 result = false;
|
||||
if (register_success){
|
||||
// NOTE(allen): Create the graphics window
|
||||
log_os(" creating graphics window...\n");
|
||||
|
||||
HWND wnd = CreateWindowExW(0, L"GRAPHICS-WINDOW-NAME", L"GRAPHICS", style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top,
|
||||
0, 0, this_instance, 0);
|
||||
|
||||
*wnd_out = 0;
|
||||
*context_out = 0;
|
||||
if (wnd != 0){
|
||||
log_os(" setting graphics pixel format...\n");
|
||||
|
||||
HDC dc = GetDC(wnd);
|
||||
|
||||
PIXELFORMATDESCRIPTOR format = {};
|
||||
|
||||
i32 pixel_attrib_list[] = {
|
||||
/* 0*/WGL_DRAW_TO_WINDOW_ARB, TRUE,
|
||||
/* 2*/WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
|
||||
/* 4*/WGL_SUPPORT_OPENGL_ARB, TRUE,
|
||||
/* 6*/WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
|
||||
/* 8*/WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
|
||||
/*10*/WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, GL_TRUE,
|
||||
/*12*/0,
|
||||
};
|
||||
if (!srgb_support){
|
||||
pixel_attrib_list[10] = 0;
|
||||
}
|
||||
|
||||
i32 suggested_format_index = 0;
|
||||
u32 ignore = 0;
|
||||
if (!wglChoosePixelFormatARB(dc, pixel_attrib_list, 0, 1, &suggested_format_index, &ignore)){
|
||||
goto fail_window_init;
|
||||
}
|
||||
|
||||
DescribePixelFormat(dc, suggested_format_index, sizeof(format), &format);
|
||||
if (!SetPixelFormat(dc, suggested_format_index, &format)){
|
||||
goto fail_window_init;
|
||||
}
|
||||
|
||||
log_os(" setting graphics attributes...\n");
|
||||
|
||||
i32 context_attrib_list[] = {
|
||||
/*0*/WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
||||
/*2*/WGL_CONTEXT_MINOR_VERSION_ARB, 2,
|
||||
/*4*/WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB
|
||||
#if GL_DEBUG_MODE
|
||||
|WGL_CONTEXT_DEBUG_BIT_ARB
|
||||
#endif
|
||||
,
|
||||
/*6*/WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
/*8*/0
|
||||
};
|
||||
|
||||
log_os(" creating graphics GL context...\n");
|
||||
HGLRC context = wglCreateContextAttribsARB(dc, 0, context_attrib_list);
|
||||
if (context == 0){
|
||||
goto fail_window_init;
|
||||
}
|
||||
|
||||
log_os(" making graphics GL context current...\n");
|
||||
wglMakeCurrent(dc, context);
|
||||
|
||||
|
||||
if (wglSwapIntervalEXT != 0){
|
||||
log_os(" setting swap interval...\n");
|
||||
wglSwapIntervalEXT(1);
|
||||
}
|
||||
*wnd_out = wnd;
|
||||
*context_out = context;
|
||||
result = true;
|
||||
|
||||
if (false){
|
||||
fail_window_init:;
|
||||
DWORD error = GetLastError();
|
||||
ReleaseDC(wnd, dc);
|
||||
DestroyWindow(wnd);
|
||||
SetLastError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
Loading…
Reference in New Issue