render to texture

This commit is contained in:
jacob 2025-01-17 19:29:57 -06:00
parent a015143f5c
commit acced9dfed
8 changed files with 289 additions and 203 deletions

View File

@ -20,7 +20,7 @@ struct draw_startup_receipt draw_startup(struct renderer_startup_receipt *render
(UNUSED)renderer_sr;
(UNUSED)font_sr;
u32 pixel_white = 0xFFFFFFFF;
G.solid_white = renderer_texture_alloc((struct image_rgba) { .width = 1, .height = 1, .pixels = &pixel_white } );
G.solid_white = renderer_texture_image_alloc((struct image_rgba) { .width = 1, .height = 1, .pixels = &pixel_white } );
return (struct draw_startup_receipt) { 0 };
}
@ -28,7 +28,7 @@ struct draw_startup_receipt draw_startup(struct renderer_startup_receipt *render
* Texture
* ========================== */
void draw_quad_sprite_ex(struct renderer_canvas *canvas, struct renderer_handle texture, struct sprite_tag sprite, struct clip_rect clip, u32 tint0, u32 tint1, struct quad quad)
void draw_quad_texture_ex(struct renderer_canvas *canvas, struct renderer_handle texture, struct sprite_tag sprite, struct clip_rect clip, u32 tint0, u32 tint1, struct quad quad)
{
struct renderer_cmd_parameters cmd_params = ZI;
cmd_params.kind = SHADER_TEXTURE;
@ -76,9 +76,9 @@ void draw_quad_sprite_ex(struct renderer_canvas *canvas, struct renderer_handle
indices[5] = offset + 3;
}
void draw_quad_sprite(struct renderer_canvas *canvas, struct draw_sprite_params params, struct quad quad)
void draw_quad_texture(struct renderer_canvas *canvas, struct draw_texture_params params, struct quad quad)
{
draw_quad_sprite_ex(canvas, renderer_handle_nil(), params.sprite, params.clip, params.tint, params.tint, quad);
draw_quad_texture_ex(canvas, params.texture, params.sprite, params.clip, params.tint, params.tint, quad);
}
/* ========================== *
@ -144,7 +144,7 @@ void draw_circle(struct renderer_canvas *canvas, struct v2 pos, f32 radius, u32
void draw_quad(struct renderer_canvas *canvas, struct quad quad, u32 color)
{
draw_quad_sprite_ex(canvas, G.solid_white, sprite_tag_nil(), CLIP_ALL, color, color, quad);
draw_quad_texture_ex(canvas, G.solid_white, sprite_tag_nil(), CLIP_ALL, color, color, quad);
}
/* ========================== *
@ -154,7 +154,7 @@ void draw_quad(struct renderer_canvas *canvas, struct quad quad, u32 color)
void draw_gradient_line(struct renderer_canvas *canvas, struct v2 start, struct v2 end, f32 thickness, u32 color_start, u32 color_end)
{
struct quad quad = quad_from_line(start, end, thickness);
draw_quad_sprite_ex(canvas, G.solid_white, sprite_tag_nil(), CLIP_ALL, color_start, color_end, quad);
draw_quad_texture_ex(canvas, G.solid_white, sprite_tag_nil(), CLIP_ALL, color_start, color_end, quad);
}
void draw_line(struct renderer_canvas *canvas, struct v2 start, struct v2 end, f32 thickness, u32 color)
@ -365,7 +365,7 @@ void draw_text_ex(struct renderer_canvas *canvas, struct font *font, struct v2 p
};
struct quad quad = quad_from_rect(RECT(x, y, width, height));
draw_quad_sprite_ex(canvas, font->image_renderer_handle, sprite_tag_nil(), clip, 0xFFFFFFFF, 0xFFFFFFFF, quad);
draw_quad_texture_ex(canvas, font->image_renderer_handle, sprite_tag_nil(), clip, 0xFFFFFFFF, 0xFFFFFFFF, quad);
draw_pos.x += glyph->advance * scale;
}

View File

@ -6,13 +6,14 @@ struct font;
struct renderer_startup_receipt;
struct font_startup_receipt;
#define DRAW_SPRITE_PARAMS(...) ((struct draw_sprite_params) { \
#define DRAW_TEXTURE_PARAMS(...) ((struct draw_texture_params) { \
.tint = COLOR_WHITE, \
.clip = CLIP_ALL, \
__VA_ARGS__ \
})
struct draw_sprite_params {
struct draw_texture_params {
struct renderer_handle texture; /* Overrides sprite if set */
struct sprite_tag sprite;
struct clip_rect clip;
u32 tint;
@ -22,8 +23,8 @@ struct draw_startup_receipt { i32 _; };
struct draw_startup_receipt draw_startup(struct renderer_startup_receipt *renderer_sr,
struct font_startup_receipt *font_sr);
void draw_quad_sprite_ex(struct renderer_canvas *canvas, struct renderer_handle texture, struct sprite_tag sprite, struct clip_rect clip, u32 tint0, u32 tint1, struct quad quad);
void draw_quad_sprite(struct renderer_canvas *canvas, struct draw_sprite_params params, struct quad quad);
void draw_quad_texture_ex(struct renderer_canvas *canvas, struct renderer_handle texture, struct sprite_tag sprite, struct clip_rect clip, u32 tint0, u32 tint1, struct quad quad);
void draw_quad_texture(struct renderer_canvas *canvas, struct draw_texture_params params, struct quad quad);
void draw_poly(struct renderer_canvas *canvas, struct v2_array array, u32 color);
void draw_circle(struct renderer_canvas *canvas, struct v2 pos, f32 radius, u32 color, u32 detail);

View File

@ -120,7 +120,7 @@ INTERNAL WORK_TASK_FUNC_DEF(font_load_asset_task, vparams)
resource_close(res);
/* Send texture to GPU */
struct renderer_handle image_renderer_handle = renderer_texture_alloc(result.image_data);
struct renderer_handle image_renderer_handle = renderer_texture_image_alloc(result.image_data);
/* Allocate store memory */
struct font *font = NULL;

View File

@ -62,11 +62,6 @@ struct renderer_canvas *renderer_canvas_alloc(void);
void renderer_canvas_release(struct renderer_canvas *canvas);
/* Set the canvas view xform to be used when presenting the canvas.
* NOTE: `view` should be in row-major order.
*/
void renderer_canvas_set_view(struct renderer_canvas *canvas, struct xform view);
/* Pushes array of vertices based on `vertices_count` & `indices_count`.
* Sets `vertices_out` and `indices_out` to start of the pushed arrays, to be filled out by the caller.
* Returns the index of the first vertex. Each inserted index should be incremented by this.
@ -83,15 +78,25 @@ void renderer_canvas_ensure_cmd(struct renderer_canvas *canvas, struct renderer_
void renderer_canvas_send_to_gpu(struct renderer_canvas *canvas);
void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_count, struct v2 screen_size, struct rect viewport, i32 vsync, struct sprite_scope *sprite_scope);
void renderer_render_to_texture(struct renderer_handle target_handle, struct renderer_canvas *canvas, u32 clear_color, struct xform view, struct rect viewport, struct sprite_scope *sprite_scope);
void renderer_render_to_backbuffer(struct v2 backbuffer_size, struct renderer_canvas *canvas, u32 clear_color, struct xform view, struct rect viewport, struct sprite_scope *sprite_scope);
void renderer_present_backbuffer(i32 vsync);
/* ========================== *
* Texture
* ========================== */
INLINE struct renderer_handle renderer_handle_nil(void) { return (struct renderer_handle) { 0 }; }
INLINE b32 renderer_handle_is_nil(struct renderer_handle h) { return h.v[0] == 0; }
/* Allocates a texture on the GPU filled with supplied image data */
struct renderer_handle renderer_texture_image_alloc(struct image_rgba data);
/* Allocates a texture on the GPU that can be rendered to */
struct renderer_handle renderer_texture_target_alloc(struct v2 size);
struct renderer_handle renderer_texture_alloc(struct image_rgba data);
void renderer_texture_release(struct renderer_handle handle);
b32 renderer_texture_is_nil(struct renderer_handle handle);

View File

@ -20,6 +20,7 @@
#pragma warning(pop)
#pragma comment(lib, "d3d11")
#pragma comment(lib, "dxgi")
#pragma comment(lib, "dxguid")
#pragma comment(lib, "d3dcompiler")
@ -84,12 +85,10 @@ struct renderer_canvas {
struct cmd_store cpu_cmd_store;
struct cmd_store gpu_cmd_store;
struct xform view;
b32 valid; /* False if uninitialized (in sparse array) */
};
INTERNAL void renderer_capture_image_for_profiler(f32 width, f32 height);
INTERNAL void renderer_capture_image_for_profiler(void);
/* ========================== *
* Global state
@ -124,11 +123,9 @@ GLOBAL struct {
ID3D11DeviceContext *devcon;
IDXGISwapChain1 *swapchain;
ID3D11RenderTargetView *backbuffer_view;
/* Here for caching/comparison */
/* For caching/comparison */
struct v2 backbuffer_size;
struct rect viewport;
ID3D11BlendState *blend_state;
ID3D11RasterizerState *rasterizer_state;
@ -433,7 +430,7 @@ struct renderer_startup_receipt renderer_startup(struct sys_window *window)
/* Create D3D11 device & context */
{
#if D3D11_DEBUG
u32 flags = D3D11_CREATE_DEVICE_DEBUG : 0;
u32 flags = D3D11_CREATE_DEVICE_DEBUG;
#else
u32 flags = 0;
#endif
@ -648,7 +645,6 @@ struct renderer_canvas *renderer_canvas_alloc(void)
MEMZERO_STRUCT(canvas);
canvas->cpu_cmd_store.arena = arena_alloc(GIGABYTE(8));
canvas->gpu_cmd_store.arena = arena_alloc(GIGABYTE(8));
canvas->view = xform_from_trs(TRS());
canvas->valid = true;
/* Initialize buffers */
@ -679,11 +675,6 @@ void renderer_canvas_release(struct renderer_canvas *canvas)
}
}
void renderer_canvas_set_view(struct renderer_canvas *canvas, struct xform view)
{
canvas->view = view;
}
u32 renderer_canvas_push_vertices(struct renderer_canvas *canvas, u8 **vertices_out, vidx **indices_out, u32 vertices_count, u32 indices_count)
{
struct renderer_cmd *cmd = canvas->cpu_cmd_store.cmd_last;
@ -835,40 +826,14 @@ void renderer_canvas_send_to_gpu(struct renderer_canvas *canvas)
}
/* ========================== *
* Present canvas
* Render
* ========================== */
INTERNAL void resize_backbuffer(struct v2 size)
{
__prof;
/* TODO: error handling */
/* Release all outstanding references to the swap chain's buffers. */
if (G.backbuffer_view) {
ID3D11RenderTargetView_Release(G.backbuffer_view);
}
IDXGISwapChain_ResizeBuffers(G.swapchain, 0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0);
/* Get buffer and create a render-target-view. */
ID3D11Texture2D *backbuffer_texture = NULL;
IDXGISwapChain_GetBuffer(G.swapchain, 0, &IID_ID3D11Texture2D, (LPVOID *)&backbuffer_texture);
ID3D11Device_CreateRenderTargetView(G.dev, (ID3D11Resource *)backbuffer_texture, NULL, &G.backbuffer_view);
ID3D11Texture2D_Release(backbuffer_texture);
}
INTERNAL void resize_viewport(struct rect viewport)
{
D3D11_VIEWPORT d3d11_viewport = {
.Width = viewport.width,
.Height = viewport.height,
.MinDepth = 0.0f,
.MaxDepth = 1.0f,
.TopLeftX = viewport.x,
.TopLeftY = viewport.y
};
ID3D11DeviceContext_RSSetViewports(G.devcon, 1, &d3d11_viewport);
G.backbuffer_size = size;
}
/* TODO: Lock canvas or at least global state? (in-case multi-threaded present).
@ -876,42 +841,39 @@ INTERNAL void resize_viewport(struct rect viewport)
* research if that is smart first).
*
* I'm thinking we may also just need to lock texture modification access while presenting */
void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_count, struct v2 screen_size, struct rect viewport, i32 vsync, struct sprite_scope *sprite_scope)
INTERNAL void dx11_render_to_target(ID3D11RenderTargetView *target, struct renderer_canvas *canvas, u32 clear_color, struct xform view, struct rect viewport, struct sprite_scope *sprite_scope)
{
__prof;
ID3D11DeviceContext_OMSetRenderTargets(G.devcon, 1, &target, NULL);
/* Resize back buffer */
if (!v2_eq(G.backbuffer_size, screen_size)) {
resize_backbuffer(screen_size);
G.backbuffer_size = screen_size;
}
if (!rect_eq(G.viewport, viewport)) {
resize_viewport(viewport);
G.viewport = viewport;
}
ID3D11DeviceContext_OMSetRenderTargets(G.devcon, 1, &G.backbuffer_view, NULL);
/* Clear back buffer */
f32 clear_color[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
ID3D11DeviceContext_ClearRenderTargetView(G.devcon, G.backbuffer_view, clear_color);
/* Set draw mode */
for (u32 i = 0; i < canvases_count; ++i) {
struct renderer_canvas *canvas = canvases[i];
D3D11_VIEWPORT d3d11_viewport = ZI;
d3d11_viewport.Width = viewport.width;
d3d11_viewport.Height = viewport.height;
d3d11_viewport.MinDepth = 0.0f;
d3d11_viewport.MaxDepth = 1.0f;
d3d11_viewport.TopLeftX = viewport.x;
d3d11_viewport.TopLeftY = viewport.y;
ID3D11DeviceContext_RSSetViewports(G.devcon, 1, &d3d11_viewport);
/* Fill and set constant buffer
* NOTE: We're only doing this once per canvas, rather than once per draw call since
* the only constant right now is VP. */
struct mat4x4 vp_matrix = calculate_vp(canvas->view, viewport.width, viewport.height);
* NOTE: We're only doing this once per render, rather than once per
* draw call since the only constant right now is VP. */
struct mat4x4 vp_matrix = calculate_vp(view, viewport.width, viewport.height);
send_constant_buffer_data(G.vs_constant_buffer, vp_matrix);
ID3D11DeviceContext_VSSetConstantBuffers(G.devcon, 0, 1, &G.vs_constant_buffer);
struct dx11_shader *last_shader = NULL;
struct renderer_handle last_texture_handle = ZI;
for (struct renderer_cmd *cmd = canvas->gpu_cmd_store.cmd_first; cmd; cmd = cmd->next) {
if (clear_color) {
f32 r = (f32)((clear_color >> 0) & 0xFF) / 255.0f;
f32 g = (f32)((clear_color >> 8) & 0xFF) / 255.0f;
f32 b = (f32)((clear_color >> 16) & 0xFF) / 255.0f;
f32 a = (f32)((clear_color >> 24) & 0xFF) / 255.0f;
f32 fill[4] = { r, g, b, a };
ID3D11DeviceContext_ClearRenderTargetView(G.devcon, target, fill);
}
struct renderer_cmd *cmd = canvas ? canvas->gpu_cmd_store.cmd_first : NULL;
for (; cmd; cmd = cmd->next) {
struct dx11_shader *shader = cmd->shader;
struct dx11_buffer *buffer = &canvas->buffers[shader->kind];
@ -938,6 +900,7 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou
/* Load texture if handle is set */
texture_loaded = true;
texture_handle = cmd->texture_handle;
/* FIXME: Texture SRV refcount needs to be increased here to prevent release mid-render */
} else {
/* Otherwise load sprite */
struct sprite_texture *sprite_texture = sprite_texture_from_tag_async(sprite_scope, cmd->sprite);
@ -946,13 +909,9 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou
}
if (texture_loaded) {
/* FIXME: what if texture_srv is 0? will this unset it correctly? */
/* Activate texture */
if (!handle_eq(texture_handle, last_texture_handle)) {
/* Bind texture */
ID3D11ShaderResourceView *texture_srv = handle_data(texture_handle);
ID3D11DeviceContext_PSSetShaderResources(G.devcon, 0, 1, &texture_srv);
last_texture_handle = texture_handle;
}
ID3D11DeviceContext_IASetPrimitiveTopology(G.devcon, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
@ -967,6 +926,10 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou
u32 index_offset = cmd->index_offset;
u32 index_count = cmd->index_count;
ID3D11DeviceContext_DrawIndexed(G.devcon, index_count, index_offset, vertex_offset);
/* Unbind */
ID3D11ShaderResourceView *null_srv[1] = { NULL };
ID3D11DeviceContext_PSSetShaderResources(G.devcon, 0, 1, null_srv);
}
} break;
@ -988,22 +951,78 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou
} break;
}
}
}
void renderer_render_to_texture(struct renderer_handle target_handle, struct renderer_canvas *canvas, u32 clear_color, struct xform view, struct rect viewport, struct sprite_scope *sprite_scope)
{
__prof;
ID3D11ShaderResourceView *target_srv = handle_data(target_handle);
if (!target_srv) {
/* Invalid handle */
ASSERT(false);
return;
}
ID3D11Resource *target_resource = NULL;
ID3D11View_GetResource(target_srv, &target_resource);
ID3D11RenderTargetView *target_view = NULL;
ID3D11Device_CreateRenderTargetView(G.dev, target_resource, NULL, &target_view);
dx11_render_to_target(target_view, canvas, clear_color, view, viewport, sprite_scope);
if (target_view) {
ID3D11RenderTargetView_Release(target_view);
}
if (target_resource) {
ID3D11Texture2D_Release((ID3D11Texture2D *)target_resource);
}
}
void renderer_render_to_backbuffer(struct v2 backbuffer_size, struct renderer_canvas *canvas, u32 clear_color, struct xform view, struct rect viewport, struct sprite_scope *sprite_scope)
{
__prof;
/* Resize back buffer */
if (!v2_eq(G.backbuffer_size, backbuffer_size)) {
resize_backbuffer(backbuffer_size);
}
ID3D11Texture2D *backbuffer_texture = NULL;
IDXGISwapChain_GetBuffer(G.swapchain, 0, &IID_ID3D11Texture2D, (LPVOID *)&backbuffer_texture);
ID3D11RenderTargetView *backbuffer_view = NULL;
ID3D11Device_CreateRenderTargetView(G.dev, (ID3D11Resource *)backbuffer_texture, NULL, &backbuffer_view);
dx11_render_to_target(backbuffer_view, canvas, clear_color, view, viewport, sprite_scope);
if (backbuffer_view) {
ID3D11RenderTargetView_Release(backbuffer_view);
}
if (backbuffer_texture) {
ID3D11Texture2D_Release(backbuffer_texture);
}
}
void renderer_present_backbuffer(i32 vsync)
{
__prof;
/* Present */
{
__profscope(IDXGISwapchain_Present);
IDXGISwapChain1_Present(G.swapchain, vsync, 0);
__profframe(0);
}
renderer_capture_image_for_profiler(viewport.width, viewport.height);
renderer_capture_image_for_profiler();
}
/* ========================== *
* Texture
* ========================== */
struct renderer_handle renderer_texture_alloc(struct image_rgba data)
struct renderer_handle renderer_texture_image_alloc(struct image_rgba data)
{
__prof;
@ -1046,6 +1065,44 @@ struct renderer_handle renderer_texture_alloc(struct image_rgba data)
return handle;
}
struct renderer_handle renderer_texture_target_alloc(struct v2 size)
{
__prof;
/* Create texture */
ID3D11Texture2D *texture = NULL;
D3D11_TEXTURE2D_DESC desc = {
.Width = size.x,
.Height = size.y,
.MipLevels = 1,
.ArraySize = 1,
.Format = DXGI_FORMAT_R8G8B8A8_UNORM,
.SampleDesc.Count = 1,
.Usage = D3D11_USAGE_DEFAULT,
.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET,
.CPUAccessFlags = 0
};
ID3D11Device_CreateTexture2D(G.dev, &desc, NULL, &texture);
/* Create srv */
ID3D11ShaderResourceView *texture_srv = NULL;
if (texture) {
D3D11_SHADER_RESOURCE_VIEW_DESC shader_resource_view_desc = {
.Format = desc.Format,
.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D,
.Texture2D.MipLevels = desc.MipLevels,
.Texture2D.MostDetailedMip = 0
};
ID3D11Device_CreateShaderResourceView(G.dev, (ID3D11Resource *)texture, &shader_resource_view_desc, &texture_srv);
ID3D11Texture2D_Release(texture);
}
ASSERT(texture_srv != NULL);
struct renderer_handle handle = handle_alloc(texture_srv);
return handle;
}
void renderer_texture_release(struct renderer_handle handle)
{
__prof;
@ -1077,7 +1134,7 @@ struct prof_cap {
struct v2 size;
};
INTERNAL void renderer_capture_image_for_profiler(f32 width, f32 height)
INTERNAL void renderer_capture_image_for_profiler(void)
{
__prof;
@ -1096,13 +1153,14 @@ INTERNAL void renderer_capture_image_for_profiler(f32 width, f32 height)
IDXGISwapChain_GetBuffer(G.swapchain, 0, &IID_ID3D11Texture2D, (LPVOID *)&backbuffer);
struct prof_cap *write_cap = &staging_caps[cap_index];
*write_cap = (struct prof_cap) { .size = V2(width, height) };
MEMZERO_STRUCT(write_cap);
{
D3D11_TEXTURE2D_DESC staging_desc;
ID3D11Texture2D_GetDesc(backbuffer, &staging_desc);
staging_desc.Usage = D3D11_USAGE_STAGING;
staging_desc.BindFlags = 0;
staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
write_cap->size = V2(staging_desc.Width, staging_desc.Height);
ID3D11Device_CreateTexture2D(G.dev, &staging_desc, NULL, &write_cap->texture);
}
@ -1153,10 +1211,8 @@ INTERNAL void renderer_capture_image_for_profiler(f32 width, f32 height)
}
#else
INTERNAL void renderer_capture_image_for_profiler(f32 width, f32 height)
INTERNAL void renderer_capture_image_for_profiler(void)
{
(UNUSED)width;
(UNUSED)height;
}
#endif

View File

@ -229,7 +229,7 @@ struct sprite_startup_receipt sprite_startup(struct renderer_startup_receipt *re
{
struct temp_arena scratch = scratch_begin_no_conflict();
struct image_rgba purple_black_image = generate_purple_black_image(scratch.arena, 64, 64);
G.nil_texture->renderer_handle = renderer_texture_alloc(purple_black_image);
G.nil_texture->renderer_handle = renderer_texture_image_alloc(purple_black_image);
scratch_end(scratch);
}
@ -365,7 +365,7 @@ INTERNAL void cache_node_load_texture(struct cache_node *n, struct sprite_tag ta
n->texture = arena_push(&n->arena, struct sprite_texture);
n->texture->width = decoded.image.width;
n->texture->height = decoded.image.height;
n->texture->renderer_handle = renderer_texture_alloc(decoded.image);
n->texture->renderer_handle = renderer_texture_image_alloc(decoded.image);
n->texture->valid = true;
n->texture->loaded = true;
/* TODO: Query renderer for more accurate texture size in VRAM */

View File

@ -441,6 +441,7 @@ struct string sys_get_clipboard_text(struct arena *arena);
/* ========================== *
* Util
* ========================== */
void sys_rand(struct buffer b);
u32 sys_num_logical_processors(void);
void sys_exit(void);

View File

@ -40,6 +40,11 @@ GLOBAL struct {
struct arena arena;
struct sys_window *window;
struct renderer_handle render_target;
struct v2 render_target_resolution;
struct renderer_canvas *backbuffer_canvas;
struct renderer_canvas *world_canvas;
struct renderer_canvas *viewport_bg_canvas;
struct renderer_canvas *viewport_canvas;
@ -142,6 +147,9 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr,
G.sys_events_mutex = sys_mutex_alloc();
G.sys_events_arena = arena_alloc(GIGABYTE(64));
world_alloc(&G.world);
G.render_target = renderer_handle_nil();
G.backbuffer_canvas = renderer_canvas_alloc();
G.world_canvas = renderer_canvas_alloc();
G.world_view = XFORM_TRS(.t = V2(0, 0), .r = 0, .s = V2(PIXELS_PER_UNIT, PIXELS_PER_UNIT));
G.viewport_bg_canvas = renderer_canvas_alloc();
@ -629,11 +637,15 @@ INTERNAL void user_update(void)
enum user_bind_kind bind = g_binds[button];
if (bind) {
b32 pressed = event->kind == SYS_EVENT_KIND_BUTTON_DOWN;
#if 0
b32 out_of_bounds = button >= SYS_BTN_M1 && button <= SYS_BTN_M5 &&
(G.viewport_cursor.x < 0 ||
G.viewport_cursor.y < 0 ||
G.viewport_cursor.x > G.viewport_size.x ||
G.viewport_cursor.y > G.viewport_size.y);
#else
b32 out_of_bounds = false;
#endif
G.bind_states[bind].is_held = pressed && !out_of_bounds;
if (pressed) {
if (!out_of_bounds) {
@ -857,16 +869,6 @@ INTERNAL void user_update(void)
mixer_set_listener(listener_pos, listener_dir);
}
/* ========================== *
* Draw test BG
* ========================== */
{
u32 color = RGBA_32_F(0.2f, 0.2f, 0.2f, 1.f);
struct quad quad = quad_from_rect(RECT(0, 0, G.viewport_size.x, G.viewport_size.y));
draw_quad(G.viewport_bg_canvas, quad, color);
}
/* ========================== *
* Draw test grid
* ========================== */
@ -875,7 +877,7 @@ INTERNAL void user_update(void)
f32 thickness = 2;
u32 color = RGBA_32(0x3f, 0x3f, 0x3f, 0xFF);
struct v2 offset = v2_sub(v2_neg(xform_mul_v2(G.world_view, V2(0, 0))), G.viewport_screen_offset);
struct v2 offset = v2_neg(xform_mul_v2(G.world_view, V2(0, 0)));
f32 spacing = xform_get_scale(G.world_view).x;
draw_grid(G.viewport_bg_canvas, RECT(0, 0, G.viewport_size.x, G.viewport_size.y), color, thickness, spacing, offset);
@ -903,7 +905,6 @@ INTERNAL void user_update(void)
}
}
/* Sort */
logf_info("Sorting %F sprites", FMT_UINT(sorted_count));
{
__profscope(sort_sprites);
merge_sort(sorted, sorted_count, sizeof(*sorted), entity_draw_order_cmp, NULL);
@ -984,8 +985,8 @@ INTERNAL void user_update(void)
if (sheet->loaded) {
struct sprite_sheet_frame frame = sprite_sheet_get_frame(sheet, ent->animation_frame);
struct quad quad = xform_mul_quad(sprite_xform, QUAD_UNIT_SQUARE_CENTERED);
struct draw_sprite_params params = DRAW_SPRITE_PARAMS(.sprite = sprite, .tint = ent->sprite_tint, .clip = frame.clip);
draw_quad_sprite(G.world_canvas, params, quad);
struct draw_texture_params params = DRAW_TEXTURE_PARAMS(.sprite = sprite, .tint = ent->sprite_tint, .clip = frame.clip);
draw_quad_texture(G.world_canvas, params, quad);
}
}
@ -1399,7 +1400,7 @@ INTERNAL void user_update(void)
struct v2 size = V2(t->width, t->height);
struct xform xf = XFORM_TRS(.t = crosshair_pos, .s = size);
struct quad quad = xform_mul_quad(xf, QUAD_UNIT_SQUARE_CENTERED);
draw_quad_sprite(G.viewport_canvas, DRAW_SPRITE_PARAMS(.sprite = crosshair_tag, .tint = tint), quad);
draw_quad_texture(G.viewport_canvas, DRAW_TEXTURE_PARAMS(.sprite = crosshair_tag, .tint = tint), quad);
struct rect cursor_clip = RECT_FROM_V2(G.viewport_screen_offset, G.viewport_size);
cursor_clip.pos = v2_add(cursor_clip.pos, v2_mul(size, 0.5f));
@ -1571,8 +1572,8 @@ INTERNAL void user_update(void)
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("player angular velocity: %F"), FMT_FLOAT_P((f64)player_angular_vel, 12)));
pos.y += spacing;
struct v2 plaeyr_pos = entity_get_xform(entity_find_first_match_one(store, ENTITY_PROP_PLAYER_CONTROLLED)).og;
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("player pos: (%F, %F)"), FMT_FLOAT_P((f64)plaeyr_pos.x, 12), FMT_FLOAT_P((f64)plaeyr_pos.y, 12)));
struct v2 player_pos = entity_get_xform(entity_find_first_match_one(store, ENTITY_PROP_PLAYER_CONTROLLED)).og;
draw_text(G.viewport_canvas, font, pos, string_format(temp.arena, STR("player pos: (%F, %F)"), FMT_FLOAT_P((f64)player_pos.x, 12), FMT_FLOAT_P((f64)player_pos.y, 12)));
pos.y += spacing;
#if COLLIDER_DEBUG
@ -1589,43 +1590,65 @@ INTERNAL void user_update(void)
pubilsh_game_cmds(&cmd_list);
/* ========================== *
* Present
* Render
* ========================== */
{
struct rect target_viewport = RECT_FROM_V2(V2(0, 0), G.viewport_size);
struct rect backbuffer_viewport = RECT_FROM_V2(V2(0, 0), G.screen_size);
/* Allocate target texture */
struct renderer_handle target;
{
struct v2 size = ZI;
size.x = target_viewport.width + target_viewport.x;
size.y = target_viewport.height + target_viewport.y;
if (renderer_handle_is_nil(G.render_target) || !v2_eq(size, G.render_target_resolution)) {
if (!renderer_handle_is_nil(G.render_target)) {
renderer_texture_release(G.render_target);
}
G.render_target = renderer_texture_target_alloc(size);
G.render_target_resolution = size;
}
target = G.render_target;
}
/* Draw target texture to backbuffer */
{
struct draw_texture_params params = DRAW_TEXTURE_PARAMS(.texture = target);
struct rect rect = RECT_FROM_V2(G.viewport_screen_offset, G.viewport_size);
struct quad quad = quad_from_rect(rect);
draw_quad_texture(G.backbuffer_canvas, params, quad);
}
/* Send canvases to GPU */
renderer_canvas_send_to_gpu(G.viewport_bg_canvas);
renderer_canvas_send_to_gpu(G.world_canvas);
renderer_canvas_send_to_gpu(G.viewport_canvas);
renderer_canvas_send_to_gpu(G.backbuffer_canvas);
/* Set canvas views before presenting */
renderer_canvas_set_view(G.viewport_bg_canvas, XFORM_IDENT);
renderer_canvas_set_view(G.world_canvas, G.world_view);
renderer_canvas_set_view(G.viewport_canvas, XFORM_IDENT);
/* Execute render cmds */
{
/* Clear textures */
renderer_render_to_texture(target, NULL, RGBA_32_F(0.2f, 0.2f, 0.2f, 1.f), XFORM_IDENT, target_viewport, sprite_frame_scope);
renderer_render_to_backbuffer(G.screen_size, NULL, COLOR_BLACK, XFORM_IDENT, target_viewport, sprite_frame_scope);
/* Render to target */
renderer_render_to_texture(target, G.viewport_bg_canvas, 0, XFORM_IDENT, target_viewport, sprite_frame_scope);
if (!tick_is_first_frame) {
/* Only draw world canvas past first tick */
renderer_render_to_texture(target, G.world_canvas, 0, G.world_view, target_viewport, sprite_frame_scope);
}
renderer_render_to_texture(target, G.viewport_canvas, 0, XFORM_IDENT, target_viewport, sprite_frame_scope);
/* Render to backbuffer */
renderer_render_to_backbuffer(G.screen_size, G.backbuffer_canvas, 0, XFORM_IDENT, backbuffer_viewport, sprite_frame_scope);
}
/* Present */
i32 vsync = VSYNC_ENABLED;
struct renderer_canvas **canvases = arena_dry_push(scratch.arena, struct renderer_canvas *);
u64 canvases_count = 0;
{
/* Viewport background canvas */
*arena_push(scratch.arena, struct renderer_canvas *) = G.viewport_bg_canvas;
++canvases_count;
/* World canvas */
if (!tick_is_first_frame) {
/* Only render world if not on first frame */
*arena_push(scratch.arena, struct renderer_canvas *) = G.world_canvas;
++canvases_count;
renderer_present_backbuffer(VSYNC_ENABLED);
}
/* Viewport canvas */
*arena_push(scratch.arena, struct renderer_canvas *) = G.viewport_canvas;
++canvases_count;
}
renderer_canvas_present(canvases, canvases_count, G.screen_size, RECT_FROM_V2(G.viewport_screen_offset, G.viewport_size), vsync, sprite_frame_scope);
/* ========================== *
* End frame cache scopes
* ========================== */