Compare commits

...

29 Commits

Author SHA1 Message Date
flyingsolomon 0ee8e3904a Update code/platform_win32/4ed_dx11_render.cpp
Co-authored-by: Simon Anciaux <14198779+mrmixer@users.noreply.github.com>
2025-07-07 13:55:28 -07:00
fs 27b4626907 Added a check for badly defined scissor rectangles that caused a crash when resizing the window with dx11 backend 2025-07-07 13:55:14 -07:00
Peter Slattery 9e58435637 Fix font rendering on mac 2025-07-07 13:54:26 -07:00
Simon Anciaux 6469fe5a0f Removed test setup, and small clean up. 2025-07-07 13:25:15 -07:00
Ed Ye 0f5d46f5a4 remove old comment 2025-07-07 13:24:46 -07:00
Ed Ye 87d0eb9cf3 update opengl version to 3.2 on linux and update glsl version to 150 2025-07-07 13:24:06 -07:00
Edward Ye b834dbb451 change mac metal renderer to properly handle texture array 2025-07-07 13:23:11 -07:00
Simon Anciaux b2bdc515ec Mac version of the fix. Not working at the moment.
It needs an implementation for `free_texture` in 4ed_metal_render.mm (like `get_texture_of_dim`, and `fill_texture`).

We also need to make sure that the texture (`handle` parameter in get_texture_of_dim and fill_texture) is not zero as the original fix expect 0 to signify "invalid texture". More specifically in `4ed_font_provider_freetyp.cpp` there is `if (texture) { fill the texture }`
and in `4ed_font_set.cpp` there is `if ( slot->face->texture != 0 ) { graphics_free_texture(...);}`
2025-07-07 13:22:32 -07:00
Simon Anciaux 9b927bd410 Fixed slice index not being passed correctly. It was normalized instead of pass as an integer value.
This commit is setup for testing the texture array slices, it shouldn't be shipped to users.
2025-07-07 13:22:01 -07:00
Simon Anciaux bd7dac90ac Using 4coder string functions in code/4ed_api_parser_main.cpp instead of manually creating the strings.
Added "code/" in the string that point to the generator so it's easier to find the correct files.
Regenerated the 4 affected files (just to update the comment string).
2025-07-07 13:14:30 -07:00
Simon Anciaux 0e2b8d0df8 A user reported on the handmade network discord an issue where when you change the font size (e.g. ctrl + scroll up/down) 4coder was crashing. They were using the D3D11 renderer.
The crash is caused by dereferencing a invalid pointer to a texture. The D3D11 renderer as a arbitrary limit of 32 textures (there is normally no reason to have more than 2 or 3 textures in 4coder) because it needs to keep track of a few pointer in its state, while OpenGL doesn't (it just uses the id returned by `glGenTexture`). The code that recreate the texture atlas when changing the font size, never frees the previous texture, so both DirectX and OpenGL are leaking texture.

So this fix just frees the previous texture after 4coder successfully creates a new one. If for some reason the new texture can't be created, we continue using the last one. This is better than crashing as we can still use 4coder, but since we can't change font size anymore the editor might be stuck with a uncomfortable font size.

Unfortunately 4coder doesn't expose a way to delete texture outside the platform layer, and the font size code is not in the platform layer. So I had to expose a `free texture` function, which lead me to understand how 4coder generates API in the `generated` folder.

I updated the README to keep that information somewhere since it's not clear from the get go. The basic idea is that there are a few files that you can compile as a small standalone program and run to generate the files. For the graphics api, you need to compile `4ed_graphics_api.cpp`.

I took some time to also modify a little the generator so that the generated file contains the name of the file that generated them, and added a bit of indentation to make the file a bit more readable (even if we're not supposed to modify them).
2025-07-07 13:11:48 -07:00
BYP e040b1a29b undo can clear dirty state 2025-07-07 13:10:15 -07:00
Simon Anciaux c194053b83 4ed_generate_keycodes.cpp adds a comment at the top of the file it generate to specify which file generated it. Added proper indentation in the generated file. Note that this executable needs to be manually ran. The build system doesn't run it.
win32_keycode_init only initialize 30 KeyCode_Ex values (0 to 29) as the code generating the enum only generates that amount (explicitly loops from 0 to < 30).
2025-07-07 13:09:34 -07:00
Simon Anciaux e4a4910e6b Use the actual clip rectangle.
Use rect_overlap in draw_rectangle_outline.
Reorganized a bit to test earlier to avoid unnecessary work.
2025-07-07 13:09:10 -07:00
Simon Anciaux 33c3428796 Clipboard fix
Fixed duplicated entries in 4coder internal clipboard (Fix from VDK on the 4coder discord).
Removed the unnecessary loop that was there to try to fix the clipboard missing entries (which was fixed previously by removing the `next_clipboard_is_self` variable).
2025-07-07 13:07:35 -07:00
Simon Anciaux dfce9bf369 Fix wrong initialization of keycode lookup table
Up until now, several keys were assigned the same key value because the `keycode_lookup_table` array wasn't properly initialized. The values at the end of the array were all set to `KeyCode_Ex0 + 1` which is 112. This would cause several key to be treated as the same key.

In my case the key next to the left shift key ( `<` and `>` on AZERTY) was getting the same value as the `Right Windows key`. But I suppose that the left and right windows key would get the same value too (I can't check this as I have only 1 windows key on my keyboard).

This fix just properly initialize the array. I also fixed a typo in a function name: `keycode_physical_translaion_is_wrong` => `keycode_physical_translation_is_wrong`.
2025-07-07 13:06:29 -07:00
Simon Anciaux f3dc516704 Disable DXGI monitoring the message queue for Alt+Enter fullscreen switch. Switching that way instead of calling toggle_fullscreen, causes issue when trying to Alt+Tab. Also users might want to use Alt+Enter for other things.
Changed how DXGI factory is handled. I retrieve the one associated with the current DXGI device and adapter instead of creating a new one.
Fixed missing new line in the log at 1 location.
2025-07-07 13:05:28 -07:00
Simon Anciaux d926166630 Changed the fix a little to test only 4 vertex per characters (instead of 6).
Removed the "draw = true;" left from testing.
2025-07-07 13:05:18 -07:00
Simon Anciaux 52124edcd8 Avoid rendering rectangles (and characters) that are outside the window bounds.
While debugging an issue with the dx11 backend, I noticed that 4coder only clipped rectangles that are outside the screen vertically. Another potential issue (it might be the intended behavior) causes some things to not work with line wrapping, which means that we could have very long horizontal lines, causing a lot of vertices being sent through the render pipeline that will just be clipped after the vertex shader. This causes higher than necessary GPU memory usage, and low performance for things that aren't rendered in the end.

An example of problematic is:
```c
u8 data[ ] = {0,0,0,0,...,0}; // With a lot of zero, and no space between them.
```

This fix just checks that at least one vertex of each rectangle we try to draw is in the screen. It would be better to use the current clip rect, but it's not available at that point in the pipeline. If we want we can modify the API a little to pass it down, as a parameter to those functions.

Using RenderDoc, I saw the vertex count going from over 500K to 13K with this change, and the performance were from 57ms to 25ms. So perfs are not great (but still an improvement), but testing the bounds of 500K vertices is not free. Fixing the line wrapping issue would probably help getting back to reasonable frame rate.
2025-07-07 13:05:06 -07:00
Simon Anciaux dadb7dc49a Fixed crashing when 4coder tries to render more vertex than the vertex buffer reserved. We create a new buffer to accommodate the required size, and if that fails, we skip rendering the current vertex group. 2025-07-07 13:04:14 -07:00
Simon Anciaux 84da2da350 Changed `true` to `GL_TRUE` for `WGL_DOUBLE_BUFFER_ARB` to use OpenGL wording. 2025-07-07 13:03:52 -07:00
Simon Anciaux cbbd83affc Commented the DirectX 11 define to default to OpenGL. 2025-07-07 13:03:44 -07:00
Simon Anciaux c81d9b4e97 Renamed files from direcx to dx11. 2025-07-07 13:03:34 -07:00
Simon Anciaux d0a18fcdb7 Renamed "directx" to dx11 in several variable names, types and defines.
Added date to NOTEs and TODOs.
Tried to make log strings more useful, even if I don't know how you display things log with log_os.
2025-07-07 13:03:26 -07:00
Simon Anciaux 7ce0a7d698 code/platform_win32/4ed_directx_render.cpp: Reuse freed textures. Rename variables so that texture handle are always named texid. 2025-07-07 13:03:19 -07:00
Simon Anciaux b651847ba3 4ed_directx_render.cpp: Initialize textures to zero to avoid having random pixel values that changes the result of linear sampling on the edge of characters. 2025-07-07 13:03:10 -07:00
Simon Anciaux e4b4e5c943 code/platform_win32/4ed_directx_render.cpp: Fixed the way texture array are updated. Note that the font provider rectangle packer contains a bug, and it will never produce several texture slice, it will only grow the texture on y to accommodate for more characters.
code/platform_win32/win32_opengl.cpp: Enabled double buffering to fix graphical glitches on AMD cards.
2025-07-07 13:02:52 -07:00
Simon Anciaux 911df4ce05 code/bin/4ed_build.cpp: Removed OpenGL.lib. It's added with #pragma comment in win32_opengl.cpp so that we don't need to do special things to keep OpenGL and DirectX both working.
code/platform_win32/win32_4ed.cpp:
- Modification to be able to choose between OpenGL and DirectX. You choose between the two by defining WIN32_DIRECTX macro. If you don't define it it uses OpenGL.
- Note that win32_gl_create_window and other part of the file that depend on OpenGL or DirectX have been moved to win32_opengl.cpp and win32_directx.cpp.
- Fixed os_popup_error using the title as the message and the message as the title.

code/platform_win32/win32_opengl.cpp: Contains previous code that was in win32_4ed.cpp.

code/platform_win32/win32_directx.cpp: Code for creating a DirectX window and context.

code/platform_win32/4ed_directx_render.cpp: Imlementation of the 4coder renderer using DirectX.
2025-07-07 13:02:42 -07:00
Simon Anciaux f2abe27704 4ed_font_provider_freetype.cpp: Rewrote the ft__bad_rect_pack_next function. There were several issues with the previous function that didn't manifest because the default size for the font atlas is 1024 * 1024 and the default 4coder only use about 1/8 of it.
To see the issues, you can set the font atlas size to 128 * 128 by changing line 325 "ft__bad_rect_pack_init(&pack, V2i32(1024, 1024));" to "ft__bad_rect_pack_init(&pack, V2i32(128, 128));".

The first issue was that the max_dim.y parameter was not respected. The dimension produced would always grow on Y to accommodate for more characters. And so the whole texture array thing was never use.

A second issue was that when a character didn't fit on the x axis, we created a new line, but never check that the new line fitted in the current texture slice.

A third issue was that when we ended a line because a character didn't fit vertically, we grew the line with a line height equal to the height of the character that didn't fit.
2025-07-07 12:59:37 -07:00
39 changed files with 4134 additions and 2582 deletions

View File

@ -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.

View File

@ -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);
////////////////////////////////

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}

View 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;

View File

@ -9,6 +9,8 @@
// TOP
#define GENERATED_BY "code/4ed_font_api.cpp"
#include "4ed_api_definition_main.cpp"
function API_Definition*

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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));
}
}
////////////////////////////////

View File

@ -9,6 +9,8 @@
// TOP
#define GENERATED_BY "code/4ed_system_api.cpp"
#include "4ed_api_definition_main.cpp"
function API_Definition*

View File

@ -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"

View File

@ -1,3 +1,4 @@
/* Generated by: 4ed_generate_keycodes.cpp */
enum{
KeyCode_A = 1,
KeyCode_B = 2,

View File

@ -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){

View File

@ -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;

View File

@ -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");

View File

@ -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);

View File

@ -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){

View File

@ -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;

View File

@ -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");

View File

@ -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);

View File

@ -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){

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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(

View File

@ -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);
}

View File

@ -976,6 +976,11 @@ graphics_fill_texture_sig(){
return(result);
}
function
graphics_free_texture_sig(){
renderer->free_texture(renderer, texid);
}
////////////////////////////////
/******************/

View File

@ -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];

View File

@ -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];

View File

@ -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;
};
////////////////////////////////

View File

@ -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 );
}
}
}

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}