diff --git a/build.c b/build.c index 946da24..b721335 100644 --- a/build.c +++ b/build.c @@ -44,5 +44,7 @@ typedef struct Context_Extra { // #include "oogabooga/examples/growing_array_example.c" // #include "oogabooga/examples/input_example.c" +// #include "oogabooga/examples/sanity_tests.c" + // This is where you swap in your own project! // #include "entry_yourepicgamename.c" diff --git a/changelog.txt b/changelog.txt index dd62c89..44cc9a2 100644 --- a/changelog.txt +++ b/changelog.txt @@ -6,6 +6,8 @@ - Reworked window initialization Window now starts of invisible and is made visible on first os_update(). This also gets rid of the weird window resize and reposition at startup. + - Renderer + - Added gfx_read_image_data() to read pixels from a Gfx_Image* ## v0.01.004 - Gamepad input, text wrapping, bug fixes diff --git a/oogabooga/examples/minimal_game_loop.c b/oogabooga/examples/minimal_game_loop.c index b09f51d..fe181c9 100644 --- a/oogabooga/examples/minimal_game_loop.c +++ b/oogabooga/examples/minimal_game_loop.c @@ -1,6 +1,11 @@ int entry(int argc, char **argv) { + // This is how we (optionally) configure the window. + // You can set this at any point in the runtime and it will + // be applied in os_update(). + // If you don't care, you can ignore all of this as it all + // has reasonable default values. window.title = STR("Minimal Game Example"); window.scaled_width = 1280; // We need to set the scaled size if we want to handle system scaling (DPI) window.scaled_height = 720; @@ -8,6 +13,7 @@ int entry(int argc, char **argv) { window.y = 90; window.clear_color = hex_to_rgba(0x6495EDff); window.allow_resize = true; + window.fullscreen = false; float64 last_time = os_get_elapsed_seconds(); while (!window.should_close) { diff --git a/oogabooga/examples/sanity_tests.c b/oogabooga/examples/sanity_tests.c new file mode 100644 index 0000000..79e40fb --- /dev/null +++ b/oogabooga/examples/sanity_tests.c @@ -0,0 +1,69 @@ + +int entry(int argc, char **argv) { + + + u32 w = 128; + u32 h = 128; + u32 tile_w = w/4; + u32 tile_h = w/4; + + u8 *pixels = (u8*)alloc(get_heap_allocator(), w * h * 4); + + for (u32 y = 0; y < h; y += 1) { + for (u32 x = 0; x < w; x += 1) { + u32 tile_x = x / tile_w; + u32 tile_y = y / tile_h; + + bool is_black_tile = (tile_x % 2 == tile_y % 2); + + for (u32 c = 0; c < 4; c += 1) { + if (is_black_tile) { + pixels[(y * w + x) * 4 + c] = 127; + } else { + pixels[(y * w + x) * 4 + c] = 255; + } + } + } + } + + Gfx_Image *img = make_image(w, h, 4, pixels, get_heap_allocator()); + + u8 *read_pixels = (u8*)alloc(get_heap_allocator(), w*h*4); + + gfx_read_image_data(img, 0, 0, w, h, read_pixels); + + gfx_set_image_data(img, 0, 0, w, h, read_pixels); + + assert(memcmp(read_pixels, pixels, w*h*4) == 0); + + float64 last_time = os_get_elapsed_seconds(); + while (!window.should_close) { + reset_temporary_storage(); + + float64 now = os_get_elapsed_seconds(); + if ((int)now != (int)last_time) log("%.2f FPS\n%.2fms", 1.0/(now-last_time), (now-last_time)*1000); + last_time = now; + + Matrix4 rect_xform = m4_scalar(1.0); + rect_xform = m4_rotate_z(rect_xform, (f32)now); + rect_xform = m4_translate(rect_xform, v3(-.25f, -.25f, 0)); + draw_image_xform(img, rect_xform, v2(.5f, .5f), COLOR_GREEN); + + draw_rect(v2(sin(now), -.8), v2(.5, .25), COLOR_RED); + + float aspect = (f32)window.width/(f32)window.height; + float mx = (input_frame.mouse_x/(f32)window.width * 2.0 - 1.0)*aspect; + float my = input_frame.mouse_y/(f32)window.height * 2.0 - 1.0; + + draw_line(v2(-.75, -.75), v2(mx, my), 0.005, COLOR_WHITE); + + if (is_key_just_pressed('F')) { + window.fullscreen = !window.fullscreen; + } + + os_update(); + gfx_update(); + } + + return 0; +} \ No newline at end of file diff --git a/oogabooga/gfx_impl_d3d11.c b/oogabooga/gfx_impl_d3d11.c index 58b8084..a0e3742 100644 --- a/oogabooga/gfx_impl_d3d11.c +++ b/oogabooga/gfx_impl_d3d11.c @@ -882,6 +882,7 @@ void gfx_init_image(Gfx_Image *image, void *initial_data) { desc.Height = image->height; desc.MipLevels = 1; desc.ArraySize = 1; + // #Hdr switch (image->channels) { case 1: desc.Format = DXGI_FORMAT_R8_UNORM; break; case 2: desc.Format = DXGI_FORMAT_R8G8_UNORM; break; @@ -897,7 +898,7 @@ void gfx_init_image(Gfx_Image *image, void *initial_data) { D3D11_SUBRESOURCE_DATA data_desc = ZERO(D3D11_SUBRESOURCE_DATA); data_desc.pSysMem = data; - data_desc.SysMemPitch = image->width * image->channels; + data_desc.SysMemPitch = image->width * image->channels; // #Hdr ID3D11Texture2D* texture = 0; HRESULT hr = ID3D11Device_CreateTexture2D(d3d11_device, &desc, &data_desc, &texture); @@ -927,16 +928,72 @@ void gfx_set_image_data(Gfx_Image *image, u32 x, u32 y, u32 w, u32 h, void *data HRESULT hr = ID3D11Resource_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; + D3D11_BOX region; + region.left = x; + region.right = x + w; + region.top = y; + region.bottom = y + h; + region.front = 0; + region.back = 1; + // #Hdr // #Incomplete bit-width 8 assumed - ID3D11DeviceContext_UpdateSubresource(d3d11_context, (ID3D11Resource*)texture, 0, &destBox, data, w * image->channels, 0); + ID3D11DeviceContext_UpdateSubresource(d3d11_context, (ID3D11Resource*)texture, 0, ®ion, data, w * image->channels, 0); + + ID3D11Resource_Release(resource); +} +void gfx_read_image_data(Gfx_Image *image, u32 x, u32 y, u32 w, u32 h, void *output) { + + D3D11_BOX region; + region.left = x; + region.right = x + w; + region.top = y; + region.bottom = y + h; + region.front = 0; + region.back = 1; + + ID3D11Resource *resource = 0; + ID3D11View_GetResource(image->gfx_handle, &resource); + + ID3D11Texture2D *texture = 0; + HRESULT hr = ID3D11Resource_QueryInterface(resource, &IID_ID3D11Texture2D, (void**)&texture); + d3d11_check_hr(hr); + + D3D11_TEXTURE2D_DESC desc; + texture->lpVtbl->GetDesc(texture, &desc); + + D3D11_TEXTURE2D_DESC staging_desc = desc; + staging_desc.Usage = D3D11_USAGE_STAGING; + staging_desc.BindFlags = 0; + staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + staging_desc.MiscFlags = 0; + + ID3D11Texture2D *staging_texture = 0; + hr = ID3D11Device_CreateTexture2D(d3d11_device, &staging_desc, 0, &staging_texture); + d3d11_check_hr(hr); + + ID3D11DeviceContext_CopySubresourceRegion( + d3d11_context, + (ID3D11Resource *)staging_texture, + 0, 0, 0, 0, + (ID3D11Resource *)texture, 0, + ®ion + ); + + D3D11_MAPPED_SUBRESOURCE mapped_texture; + hr = ID3D11DeviceContext_Map(d3d11_context, (ID3D11Resource *)staging_texture, 0, D3D11_MAP_READ, 0, &mapped_texture); + d3d11_check_hr(hr); + + // #Hdr + assert(mapped_texture.RowPitch == image->width * image->channels, "Unexpected row pitch in d3d11 texture"); + + // #Hdr + memcpy(output, mapped_texture.pData, image->width*image->height*image->channels); + + ID3D11DeviceContext_Unmap(d3d11_context, (ID3D11Resource *)staging_texture, 0); + + ID3D11Resource_Release(resource); + ID3D11Texture2D_Release(staging_texture); } void gfx_deinit_image(Gfx_Image *image) { ID3D11ShaderResourceView *view = image->gfx_handle; @@ -952,6 +1009,8 @@ void gfx_deinit_image(Gfx_Image *image) { } else { panic("Unhandled D3D11 resource deletion"); } + + ID3D11Resource_Release(resource); } bool diff --git a/oogabooga/gfx_interface.c b/oogabooga/gfx_interface.c index 8da5904..412b4c1 100644 --- a/oogabooga/gfx_interface.c +++ b/oogabooga/gfx_interface.c @@ -49,6 +49,8 @@ ogb_instance void gfx_init_image(Gfx_Image *image, void *data); ogb_instance void gfx_set_image_data(Gfx_Image *image, u32 x, u32 y, u32 w, u32 h, void *data); +ogb_instance void +gfx_read_image_data(Gfx_Image *image, u32 x, u32 y, u32 w, u32 h, void *output); ogb_instance void gfx_deinit_image(Gfx_Image *image);