From 83c0371dcb9257d08248c25c93b226132d0329d1 Mon Sep 17 00:00:00 2001 From: Charlie <66182434+asbott@users.noreply.github.com> Date: Sun, 7 Jul 2024 09:09:01 +0200 Subject: [PATCH] Rework Gfx_Image stuff --- oogabooga/drawing.c | 40 ------- oogabooga/examples/renderer_stress_test.c | 10 +- oogabooga/gfx_impl_d3d11.c | 122 +++++++++++++--------- oogabooga/gfx_interface.c | 101 +++++++++++++++++- oogabooga/hash_table.c | 5 +- oogabooga/tests.c | 2 - 6 files changed, 182 insertions(+), 98 deletions(-) diff --git a/oogabooga/drawing.c b/oogabooga/drawing.c index bd6f1bf..8fe7ace 100644 --- a/oogabooga/drawing.c +++ b/oogabooga/drawing.c @@ -83,9 +83,6 @@ typedef struct Draw_Frame { Matrix4 projection; Matrix4 view; - - Gfx_Handle garbage_stack[4096]; - u64 garbage_stack_count; } Draw_Frame; // This frame is passed to the platform layer and rendered in os_update. // Resets every frame. @@ -196,40 +193,3 @@ Draw_Quad *draw_image_xform(Gfx_Image *image, Matrix4 xform, Vector2 size, Vecto #define COLOR_WHITE ((Vector4){1.0, 1.0, 1.0, 1.0}) #define COLOR_BLACK ((Vector4){0.0, 0.0, 0.0, 1.0}) -Gfx_Image *load_image_from_disk(string path, Allocator allocator) { - string png; - bool ok = os_read_entire_file(path, &png, allocator); - if (!ok) return 0; - - Gfx_Image *image = alloc(allocator, sizeof(Gfx_Image)); - - // Use stb_image to load and decode the PNG - int width, height, channels; - stbi_set_flip_vertically_on_load(1); // stb_image can flip the image on load - unsigned char* stb_data = stbi_load_from_memory(png.data, png.count, &width, &height, &channels, STBI_rgb_alpha); - - if (!stb_data) { - dealloc(allocator, image); - dealloc_string(allocator, png); - return 0; - } - - image->data = stb_data; - image->width = width; - image->height = height; - image->gfx_handle = GFX_INVALID_HANDLE; // This is handled in gfx - image->allocator = allocator; - - dealloc_string(allocator, png); - - return image; -} - -void delete_image(Gfx_Image *image) { - stbi_image_free(image->data); // Free the image data allocated by stb_image - image->width = 0; - image->height = 0; - draw_frame.garbage_stack[draw_frame.garbage_stack_count] = image->gfx_handle; - draw_frame.garbage_stack_count += 1; - dealloc(image->allocator, image); -} \ No newline at end of file diff --git a/oogabooga/examples/renderer_stress_test.c b/oogabooga/examples/renderer_stress_test.c index 25aaa01..e641f58 100644 --- a/oogabooga/examples/renderer_stress_test.c +++ b/oogabooga/examples/renderer_stress_test.c @@ -15,6 +15,14 @@ int entry(int argc, char **argv) { Gfx_Image *hammer_image = load_image_from_disk(STR("oogabooga/examples/hammer.png"), get_heap_allocator()); assert(hammer_image, "Failed loading hammer.png"); + void *my_data = alloc(get_heap_allocator(), 32*32*4); + memset(my_data, 0xffffffff, 32*32*4); + Gfx_Image *my_image = make_image(32, 32, 4, my_data, get_heap_allocator()); + for (int *c = (int*)my_data; c < (int*)my_data+16*16; c += 1) { + *c = 0xff0000ff; + } + gfx_set_image_data(my_image, 0, 0, 16, 16, my_data); + seed_for_random = os_get_current_cycle_count(); const float64 fps_limit = 69000; @@ -94,7 +102,7 @@ int entry(int argc, char **argv) { Matrix4 hammer_xform = m4_scalar(1.0); hammer_xform = m4_rotate_z(hammer_xform, (f32)now); hammer_xform = m4_translate(hammer_xform, v3(-.25f, -.25f, 0)); - draw_image_xform(hammer_image, hammer_xform, v2(.5f, .5f), COLOR_RED); + draw_image_xform(my_image, hammer_xform, v2(.5f, .5f), COLOR_RED); Vector2 hover_position = v2_rotate_point_around_pivot(v2(-.5, -.5), v2(0, 0), (f32)now); Vector2 local_pivot = v2(.125f, .125f); diff --git a/oogabooga/gfx_impl_d3d11.c b/oogabooga/gfx_impl_d3d11.c index 40ce967..7c9f911 100644 --- a/oogabooga/gfx_impl_d3d11.c +++ b/oogabooga/gfx_impl_d3d11.c @@ -13,7 +13,7 @@ const Gfx_Handle GFX_INVALID_HANDLE = 0; string temp_win32_null_terminated_wide_to_fixed_utf8(const u16 *utf16); -typedef struct alignat(16) D3D11_Vertex { +typedef struct alignat(16) D3D11_Vertex { Vector4 color; Vector4 position; Vector2 uv; @@ -22,8 +22,8 @@ typedef struct alignat(16) D3D11_Vertex { ID3D11Debug *d3d11_debug = 0; -ID3D11Device* d3d11_device = 0; -ID3D11DeviceContext* d3d11_context = 0; +ID3D11Device *d3d11_device = 0; +ID3D11DeviceContext *d3d11_context = 0; ID3D11RenderTargetView *d3d11_window_render_target_view = 0; ID3D11Texture2D *d3d11_back_buffer = 0; D3D_DRIVER_TYPE d3d11_driver_type = 0; @@ -518,23 +518,6 @@ void gfx_update() { HRESULT hr; tm_scope_cycles("Frame setup") { - /// - // purge garbage - for (u64 i = 0; i < draw_frame.garbage_stack_count; i++) { - ID3D11ShaderResourceView *view = draw_frame.garbage_stack[i]; - ID3D11Resource *resource = 0; - VTABLE(GetResource, view, &resource); - - ID3D11Texture2D *texture = 0; - hr = VTABLE(QueryInterface, resource, &IID_ID3D11Texture2D, (void**)&texture); - if (SUCCEEDED(hr)) { - D3D11Release(view); - D3D11Release(texture); - log("Destroyed an image"); - } else { - panic("Unhandled D3D11 resource deletion"); - } - } /// // Maybe resize swap chain @@ -595,33 +578,6 @@ void gfx_update() { int texture_index = -1; if (q->image) { - if (!q->image->gfx_handle) { - D3D11_TEXTURE2D_DESC desc = ZERO(D3D11_TEXTURE2D_DESC); - desc.Width = q->image->width; - desc.Height = q->image->height; - desc.MipLevels = 1; - desc.ArraySize = 1; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - desc.SampleDesc.Count = 1; - desc.SampleDesc.Quality = 0; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - desc.CPUAccessFlags = 0; - desc.MiscFlags = 0; - - D3D11_SUBRESOURCE_DATA data = ZERO(D3D11_SUBRESOURCE_DATA); - data.pSysMem = q->image->data; - data.SysMemPitch = q->image->width * 4; // #Magicvalue assuming 4 channels - - ID3D11Texture2D* texture = 0; - HRESULT hr = VTABLE(CreateTexture2D, d3d11_device, &desc, &data, &texture); - win32_check_hr(hr); - - hr = VTABLE(CreateShaderResourceView, d3d11_device, (ID3D11Resource*)texture, 0, &q->image->gfx_handle); - win32_check_hr(hr); - - log_verbose("Created an image of width %d and height %d.", q->image->width, q->image->height); - } if (last_texture == q->image->gfx_handle) { texture_index = (int)(num_textures-1); @@ -742,3 +698,75 @@ void gfx_update() { reset_draw_frame(&draw_frame); } + + +void gfx_init_image(Gfx_Image *image, void *data) { + D3D11_TEXTURE2D_DESC desc = ZERO(D3D11_TEXTURE2D_DESC); + desc.Width = image->width; + desc.Height = image->height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + + D3D11_SUBRESOURCE_DATA data_desc = ZERO(D3D11_SUBRESOURCE_DATA); + data_desc.pSysMem = data; + data_desc.SysMemPitch = image->width * image->channels; + + ID3D11Texture2D* texture = 0; + HRESULT hr = VTABLE(CreateTexture2D, d3d11_device, &desc, &data_desc, &texture); + win32_check_hr(hr); + + hr = VTABLE(CreateShaderResourceView, d3d11_device, (ID3D11Resource*)texture, 0, &image->gfx_handle); + win32_check_hr(hr); + + log_verbose("Created an image of width %d and height %d.", image->width, image->height); +} +void gfx_set_image_data(Gfx_Image *image, u32 x, u32 y, u32 w, u32 h, void *data) { + assert(image && data, "Bad parameters passed to gfx_set_image_data"); + + ID3D11ShaderResourceView *view = image->gfx_handle; + ID3D11Resource *resource = NULL; + VTABLE(GetResource, view, &resource); + + assert(resource, "Invalid image passed to gfx_set_image_data"); + + assert(x+w < image->width && y+h < image->height, "Specified subregion in image is out of bounds"); + + ID3D11Texture2D *texture = NULL; + HRESULT hr = VTABLE(QueryInterface, resource, &IID_ID3D11Texture2D, (void**)&texture); + assert(SUCCEEDED(hr), "Expected gfx resource to be a texture but it wasn't"); + + D3D11_BOX destBox; + destBox.left = x; + destBox.right = x + w; + destBox.top = y; + destBox.bottom = y + h; + destBox.front = 0; + destBox.back = 1; + + // #Incomplete bit-width 8 assumed + VTABLE(UpdateSubresource, d3d11_context, resource, 0, &destBox, data, w * image->channels, 0); + + log_verbose("Updated image data at region (%u, %u) with dimensions (%u, %u).", x, y, w, h); +} +void gfx_deinit_image(Gfx_Image *image) { + ID3D11ShaderResourceView *view = image->gfx_handle; + ID3D11Resource *resource = 0; + VTABLE(GetResource, view, &resource); + + ID3D11Texture2D *texture = 0; + HRESULT hr = VTABLE(QueryInterface, resource, &IID_ID3D11Texture2D, (void**)&texture); + if (SUCCEEDED(hr)) { + D3D11Release(view); + D3D11Release(texture); + log("Destroyed an image"); + } else { + panic("Unhandled D3D11 resource deletion"); + } +} \ No newline at end of file diff --git a/oogabooga/gfx_interface.c b/oogabooga/gfx_interface.c index fe9ec58..4d3ca18 100644 --- a/oogabooga/gfx_interface.c +++ b/oogabooga/gfx_interface.c @@ -18,15 +18,106 @@ forward_global const Gfx_Handle GFX_INVALID_HANDLE; typedef struct Gfx_Image { - u32 width, height; - u8 *data; + u32 width, height, channels; Gfx_Handle gfx_handle; Allocator allocator; } Gfx_Image; +Gfx_Image *make_image(u32 width, u32 height, u32 channels, void *initial_data, Allocator allocator); +Gfx_Image *load_image_from_disk(string path, Allocator allocator); +void delete_image(Gfx_Image *image); + +void gfx_init_image(Gfx_Image *image, void *data); +void gfx_set_image_data(Gfx_Image *image, u32 x, u32 y, u32 w, u32 h, void *data); +void gfx_deinit_image(Gfx_Image *image); + +#define FONT_ATLAS_WIDTH 4096 +#define FONT_ATLAS_HEIGHT 4096 +#define MAX_FONT_HEIGHT 256 + +typedef struct Gfx_Font_Metrics { + u32 latin_ascent; + u32 latin_descent; + + u32 max_ascent; + u32 max_descent; + + u32 line_height; + u32 line_spacing; + +} Gfx_Font_Metrics; +typedef struct Gfx_Font_Atlas { + Gfx_Image image; + +} Gfx_Font_Atlas; +typedef struct Gfx_Font_Variation { + u32 height; + Gfx_Font_Metrics metrics; + u32 codepoint_range_per_atlas; + Hash_Table atlases; // u32 atlas_index, Gfx_Font_Atlas + bool initted; +} Gfx_Font_Variation; typedef struct Gfx_Font { - u32 width, height; - u8 *data; - Gfx_Handle gfx_handle; + Gfx_Font_Variation variations[MAX_FONT_HEIGHT]; // Variation per font height Allocator allocator; } Gfx_Font; + + + + +// initial_data can be null to leave image data uninitialized +Gfx_Image *make_image(u32 width, u32 height, u32 channels, void *initial_data, Allocator allocator) { + Gfx_Image *image = alloc(allocator, sizeof(Gfx_Image) + width*height*channels); + + assert(channels > 0 && channels <= 4, "Only 1, 2, 3 or 4 channels allowed on images. Got %d", channels); + + image->width = width; + image->height = height; + image->gfx_handle = GFX_INVALID_HANDLE; // This is handled in gfx + image->allocator = allocator; + image->channels = 4; + + gfx_init_image(image, initial_data); + + return image; +} + +Gfx_Image *load_image_from_disk(string path, Allocator allocator) { + string png; + bool ok = os_read_entire_file(path, &png, allocator); + if (!ok) return 0; + + Gfx_Image *image = alloc(allocator, sizeof(Gfx_Image)); + + int width, height, channels; + stbi_set_flip_vertically_on_load(1); + unsigned char* stb_data = stbi_load_from_memory(png.data, png.count, &width, &height, &channels, STBI_rgb_alpha); + + if (!stb_data) { + dealloc(allocator, image); + dealloc_string(allocator, png); + return 0; + } + + image->width = width; + image->height = height; + image->gfx_handle = GFX_INVALID_HANDLE; // This is handled in gfx + image->allocator = allocator; + image->channels = 4; + + dealloc_string(allocator, png); + + gfx_init_image(image, stb_data); + + stbi_image_free(stb_data); + + return image; +} + +void delete_image(Gfx_Image *image) { + // Free the image data allocated by stb_image + image->width = 0; + image->height = 0; + gfx_deinit_image(image); + dealloc(image->allocator, image); +} diff --git a/oogabooga/hash_table.c b/oogabooga/hash_table.c index 004ffd9..070de7f 100644 --- a/oogabooga/hash_table.c +++ b/oogabooga/hash_table.c @@ -1,7 +1,6 @@ -// Very naive implementation for now but it should be cache efficient so not really a -// problem until very large (in which case you should probably write your own data structure -// anyways) +// Very naive implementation but it should be very cache efficient so it's alright +// for non-excessive use for now. /* diff --git a/oogabooga/tests.c b/oogabooga/tests.c index 9d4ad4e..a385aac 100644 --- a/oogabooga/tests.c +++ b/oogabooga/tests.c @@ -1081,8 +1081,6 @@ void oogabooga_run_tests() { print("OK!\n"); - // #Temporary - // This makes entire os freeze in release lol #if CONFIGURATION != RELEASE print("Thread bombing allocator... "); Thread* threads[100];