diff --git a/src/draw.c b/src/draw.c index c7472e26..5ac657f9 100644 --- a/src/draw.c +++ b/src/draw.c @@ -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; } diff --git a/src/draw.h b/src/draw.h index d3afe171..ec5988bb 100644 --- a/src/draw.h +++ b/src/draw.h @@ -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); diff --git a/src/font.c b/src/font.c index 96de37f3..88dd62da 100644 --- a/src/font.c +++ b/src/font.c @@ -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; diff --git a/src/renderer.h b/src/renderer.h index 028108f3..c2d6c0ae 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -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); diff --git a/src/renderer_d3d11.c b/src/renderer_d3d11.c index a9d86af8..f1a40f28 100644 --- a/src/renderer_d3d11.c +++ b/src/renderer_d3d11.c @@ -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,102 +841,78 @@ 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; + 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 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; + 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); } - if (!rect_eq(G.viewport, viewport)) { - resize_viewport(viewport); - G.viewport = viewport; - } + 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]; - ID3D11DeviceContext_OMSetRenderTargets(G.devcon, 1, &G.backbuffer_view, NULL); + /* Activate shader */ + if (shader != last_shader) { + ID3D11DeviceContext_VSSetShader(G.devcon, shader->vs, 0, 0); + ID3D11DeviceContext_PSSetShader(G.devcon, shader->ps, 0, 0); + ID3D11DeviceContext_IASetInputLayout(G.devcon, shader->input_layout); + last_shader = shader; + } - /* Clear back buffer */ - f32 clear_color[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; - ID3D11DeviceContext_ClearRenderTargetView(G.devcon, G.backbuffer_view, clear_color); + switch (shader->kind) { + default: + { + /* Unknown shader */ + ASSERT(false); + } break; - /* Set draw mode */ + case SHADER_TEXTURE: + { + b32 texture_loaded; + struct renderer_handle texture_handle; + if (!handle_is_nil(cmd->texture_handle)) { + /* 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); + texture_loaded = sprite_texture->loaded; + texture_handle = sprite_texture->renderer_handle; + } - for (u32 i = 0; i < canvases_count; ++i) { - struct renderer_canvas *canvas = canvases[i]; + if (texture_loaded) { + /* Bind texture */ + ID3D11ShaderResourceView *texture_srv = handle_data(texture_handle); + ID3D11DeviceContext_PSSetShaderResources(G.devcon, 0, 1, &texture_srv); - /* 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); - 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) { - struct dx11_shader *shader = cmd->shader; - struct dx11_buffer *buffer = &canvas->buffers[shader->kind]; - - /* Activate shader */ - if (shader != last_shader) { - ID3D11DeviceContext_VSSetShader(G.devcon, shader->vs, 0, 0); - ID3D11DeviceContext_PSSetShader(G.devcon, shader->ps, 0, 0); - ID3D11DeviceContext_IASetInputLayout(G.devcon, shader->input_layout); - last_shader = shader; - } - - switch (shader->kind) { - default: - { - /* Unknown shader */ - ASSERT(false); - } break; - - case SHADER_TEXTURE: - { - b32 texture_loaded; - struct renderer_handle texture_handle; - if (!handle_is_nil(cmd->texture_handle)) { - /* Load texture if handle is set */ - texture_loaded = true; - texture_handle = cmd->texture_handle; - } else { - /* Otherwise load sprite */ - struct sprite_texture *sprite_texture = sprite_texture_from_tag_async(sprite_scope, cmd->sprite); - texture_loaded = sprite_texture->loaded; - texture_handle = sprite_texture->renderer_handle; - } - - 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)) { - 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); - - /* Activate buffer */ - u32 zero = 0; - UINT vertex_stride = shader->vertex_size; - ID3D11DeviceContext_IASetVertexBuffers(G.devcon, 0, 1, &buffer->gpu_vertex_buffer, &vertex_stride, &zero); - ID3D11DeviceContext_IASetIndexBuffer(G.devcon, buffer->gpu_index_buffer, DXGI_FORMAT_R32_UINT, zero); - - /* Draw */ - u32 vertex_offset = cmd->vertex_offset; - u32 index_offset = cmd->index_offset; - u32 index_count = cmd->index_count; - ID3D11DeviceContext_DrawIndexed(G.devcon, index_count, index_offset, vertex_offset); - } - } break; - - case SHADER_GRID: - { ID3D11DeviceContext_IASetPrimitiveTopology(G.devcon, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); /* Activate buffer */ @@ -985,10 +926,87 @@ 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); - } break; - } + + /* Unbind */ + ID3D11ShaderResourceView *null_srv[1] = { NULL }; + ID3D11DeviceContext_PSSetShaderResources(G.devcon, 0, 1, null_srv); + } + } break; + + case SHADER_GRID: + { + ID3D11DeviceContext_IASetPrimitiveTopology(G.devcon, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + /* Activate buffer */ + u32 zero = 0; + UINT vertex_stride = shader->vertex_size; + ID3D11DeviceContext_IASetVertexBuffers(G.devcon, 0, 1, &buffer->gpu_vertex_buffer, &vertex_stride, &zero); + ID3D11DeviceContext_IASetIndexBuffer(G.devcon, buffer->gpu_index_buffer, DXGI_FORMAT_R32_UINT, zero); + + /* Draw */ + u32 vertex_offset = cmd->vertex_offset; + u32 index_offset = cmd->index_offset; + u32 index_count = cmd->index_count; + ID3D11DeviceContext_DrawIndexed(G.devcon, index_count, index_offset, vertex_offset); + } 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 */ { @@ -996,14 +1014,15 @@ void renderer_canvas_present(struct renderer_canvas **canvases, u32 canvases_cou 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 diff --git a/src/sprite.c b/src/sprite.c index 5a9f4836..94becf81 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -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 */ diff --git a/src/sys.h b/src/sys.h index c82ceec4..b4ee2fd5 100644 --- a/src/sys.h +++ b/src/sys.h @@ -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); diff --git a/src/user.c b/src/user.c index b4eee28c..411a71c3 100644 --- a/src/user.c +++ b/src/user.c @@ -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,42 +1590,64 @@ INTERNAL void user_update(void) pubilsh_game_cmds(&cmd_list); /* ========================== * - * Present + * Render * ========================== */ - /* 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); - - /* 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); - - /* 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; + 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); - /* 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; + /* 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; } - /* Viewport canvas */ - *arena_push(scratch.arena, struct renderer_canvas *) = G.viewport_canvas; - ++canvases_count; - } + /* 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); + } - renderer_canvas_present(canvases, canvases_count, G.screen_size, RECT_FROM_V2(G.viewport_screen_offset, G.viewport_size), vsync, sprite_frame_scope); + /* 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); + + /* 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 */ + renderer_present_backbuffer(VSYNC_ENABLED); + } /* ========================== * * End frame cache scopes