diff --git a/ashura/include/ashura/animation.h b/ashura/include/ashura/animation.h index 2542330e7..7fb6505ad 100644 --- a/ashura/include/ashura/animation.h +++ b/ashura/include/ashura/animation.h @@ -40,7 +40,7 @@ struct Animation nanoseconds duration = milliseconds{256}; u64 iterations = 1; AnimationCfg cfg = AnimationCfg::Default; - f64 speed = 1; // higher spead means faster time to completion than specified duration + f64 speed = 1; // higher spead means faster time to completion than specified duration nanoseconds elapsed_duration = nanoseconds{0}; u64 iterations_done = 0; @@ -89,7 +89,9 @@ struct Animation { cfg &= ~AnimationCfg::Loop; iterations_done = iterations; - t = ((cfg & AnimationCfg::Alternate) == AnimationCfg::Alternate) ? (((iterations % 2) == 0) ? 0.0 : 1.0) : 1.0; + t = ((cfg & AnimationCfg::Alternate) == AnimationCfg::Alternate) ? + (((iterations % 2) == 0) ? 0.0 : 1.0) : + 1.0; } void tick(nanoseconds interval) @@ -99,21 +101,25 @@ struct Animation return; } - nanoseconds const tick_duration = nanoseconds{(i64) ((f64) interval.count() * (f64) speed)}; + nanoseconds const tick_duration = nanoseconds{(i64) ((f64) interval.count() * (f64) speed)}; nanoseconds const total_elapsed_duration = elapsed_duration + tick_duration; - f64 const t_total = (((f64) total_elapsed_duration.count()) / (f64) duration.count()); - u64 const t_iterations = (u64) t_total; + f64 const t_total = (((f64) total_elapsed_duration.count()) / (f64) duration.count()); + u64 const t_iterations = (u64) t_total; if (((cfg & AnimationCfg::Loop) != AnimationCfg::Loop) && t_iterations >= iterations) { elapsed_duration = total_elapsed_duration; iterations_done = iterations; - t = ((cfg & AnimationCfg::Alternate) == AnimationCfg::Alternate) ? (((iterations % 2) == 0) ? 0.0 : 1.0) : 1.0; + t = ((cfg & AnimationCfg::Alternate) == AnimationCfg::Alternate) ? + (((iterations % 2) == 0) ? 0.0 : 1.0) : + 1.0; } else { f64 const t_unsigned = t_total - (f64) t_iterations; - f64 const t_signed = ((cfg & AnimationCfg::Alternate) == AnimationCfg::Alternate) ? ((t_iterations % 2) == 0 ? t_unsigned : (1.0 - t_unsigned)) : t_unsigned; + f64 const t_signed = ((cfg & AnimationCfg::Alternate) == AnimationCfg::Alternate) ? + ((t_iterations % 2) == 0 ? t_unsigned : (1.0 - t_unsigned)) : + t_unsigned; elapsed_duration = total_elapsed_duration; iterations_done = t_iterations; t = t_signed; diff --git a/ashura/include/ashura/app.h b/ashura/include/ashura/app.h index 4f589f652..5c1586ab2 100644 --- a/ashura/include/ashura/app.h +++ b/ashura/include/ashura/app.h @@ -15,14 +15,15 @@ struct App STX_MAKE_PINNED(App) template DerivedWidget> - explicit App(AppConfig icfg, DerivedWidget &&widget) : - cfg{icfg}, engine{icfg, std::move(widget)} - {} + explicit App(AppConfig icfg, DerivedWidget &&widget) : cfg{icfg}, engine{icfg, std::move(widget)} + { + } void tick(std::chrono::nanoseconds interval); ~App() - {} + { + } AppConfig cfg; Engine engine; diff --git a/ashura/include/ashura/canvas.h b/ashura/include/ashura/canvas.h index 523e462b7..c6dba99da 100644 --- a/ashura/include/ashura/canvas.h +++ b/ashura/include/ashura/canvas.h @@ -51,7 +51,8 @@ inline stx::Span Rect(Vec2 offset, Vec2 extent, Vec4 color, stx::Span< return polygon.copy(vertices); } -inline stx::Span arc(Vec2 offset, f32 radius, f32 begin, f32 end, u32 nsegments, Vec4 color, stx::Span polygon) +inline stx::Span arc(Vec2 offset, f32 radius, f32 begin, f32 end, u32 nsegments, + Vec4 color, stx::Span polygon) { begin = ASH_TO_RADIANS(begin); end = ASH_TO_RADIANS(end); @@ -71,7 +72,8 @@ inline stx::Span arc(Vec2 offset, f32 radius, f32 begin, f32 end, u32 return polygon; } -inline stx::Span circle(Vec2 offset, f32 radius, u32 nsegments, Vec4 color, stx::Span polygon) +inline stx::Span circle(Vec2 offset, f32 radius, u32 nsegments, Vec4 color, + stx::Span polygon) { if (nsegments == 0 || radius <= 0) { @@ -89,7 +91,8 @@ inline stx::Span circle(Vec2 offset, f32 radius, u32 nsegments, Vec4 c return polygon; } -inline stx::Span ellipse(Vec2 offset, Vec2 radii, u32 nsegments, Vec4 color, stx::Span polygon) +inline stx::Span ellipse(Vec2 offset, Vec2 radii, u32 nsegments, Vec4 color, + stx::Span polygon) { if (nsegments == 0 || radii.x <= 0 || radii.y <= 0) { @@ -108,7 +111,8 @@ inline stx::Span ellipse(Vec2 offset, Vec2 radii, u32 nsegments, Vec4 } // outputs 8 + nsegments * 4 vertices -inline stx::Span round_rect(Vec2 offset, Vec2 extent, Vec4 radii, u32 nsegments, Vec4 color, stx::Span polygon) +inline stx::Span round_rect(Vec2 offset, Vec2 extent, Vec4 radii, u32 nsegments, + Vec4 color, stx::Span polygon) { f32 max_radius = std::min(extent.x, extent.y); radii.x = std::min(radii.x, max_radius); @@ -127,7 +131,8 @@ inline stx::Span round_rect(Vec2 offset, Vec2 extent, Vec4 radii, u32 for (u32 segment = 0; segment < nsegments; segment++, i++) { - Vec2 p = (extent - radii.z) + radii.z * Vec2{std::cos(segment * step), std::sin(segment * step)}; + Vec2 p = + (extent - radii.z) + radii.z * Vec2{std::cos(segment * step), std::sin(segment * step)}; polygon[i] = Vertex2d{.position = offset + p, .uv = {}, .color = color}; } @@ -139,7 +144,8 @@ inline stx::Span round_rect(Vec2 offset, Vec2 extent, Vec4 radii, u32 for (u32 segment = 0; segment < nsegments; segment++, i++) { - Vec2 p = Vec2{radii.w, extent.y - radii.w} + radii.w * Vec2{std::cos(PI / 2 + segment * step), std::sin(PI / 2 + segment * step)}; + Vec2 p = Vec2{radii.w, extent.y - radii.w} + + radii.w * Vec2{std::cos(PI / 2 + segment * step), std::sin(PI / 2 + segment * step)}; polygon[i] = Vertex2d{.position = offset + p, .uv = {}, .color = color}; } @@ -151,7 +157,7 @@ inline stx::Span round_rect(Vec2 offset, Vec2 extent, Vec4 radii, u32 for (u32 segment = 0; segment < nsegments; segment++, i++) { - Vec2 p = radii.x + radii.x * Vec2{std::cos(PI + segment * step), std::sin(PI + segment * step)}; + Vec2 p = radii.x + radii.x * Vec2{std::cos(PI + segment * step), std::sin(PI + segment * step)}; polygon[i] = Vertex2d{.position = offset + p, .uv = {}, .color = color}; } @@ -163,7 +169,9 @@ inline stx::Span round_rect(Vec2 offset, Vec2 extent, Vec4 radii, u32 for (u32 segment = 0; segment < nsegments; segment++, i++) { - Vec2 p = Vec2{extent.x - radii.y, radii.y} + radii.y * Vec2{std::cos(PI * 3.0f / 2.0f + segment * step), std::sin(PI * 3.0f / 2.0f + segment * step)}; + Vec2 p = Vec2{extent.x - radii.y, radii.y} + + radii.y * Vec2{std::cos(PI * 3.0f / 2.0f + segment * step), + std::sin(PI * 3.0f / 2.0f + segment * step)}; polygon[i] = Vertex2d{.position = offset + p, .uv = {}, .color = color}; } @@ -172,7 +180,8 @@ inline stx::Span round_rect(Vec2 offset, Vec2 extent, Vec4 radii, u32 return polygon; } -inline stx::Span bevel_rect(Vec2 offset, Vec2 extent, Vec4 radii, Vec4 color, stx::Span polygon) +inline stx::Span bevel_rect(Vec2 offset, Vec2 extent, Vec4 radii, Vec4 color, + stx::Span polygon) { f32 max_radius = std::min(extent.x, extent.y); radii.x = std::min(radii.x, max_radius); @@ -182,19 +191,21 @@ inline stx::Span bevel_rect(Vec2 offset, Vec2 extent, Vec4 radii, Vec4 f32 max_radius_w = std::min(max_radius_z, max_radius - radii.z); radii.w = std::min(radii.w, max_radius_w); - Vertex2d const vertices[] = {{.position = offset + Vec2{radii.x, 0}, .uv = {}, .color = color}, - {.position = offset + Vec2{extent.x - radii.y, 0}, .uv = {}, .color = color}, - {.position = offset + Vec2{extent.x, radii.y}, .uv = {}, .color = color}, - {.position = offset + Vec2{extent.x, extent.y - radii.z}, .uv = {}, .color = color}, - {.position = offset + Vec2{extent.x - radii.z, extent.y}, .uv = {}, .color = color}, - {.position = offset + Vec2{radii.w, extent.y}, .uv = {}, .color = color}, - {.position = offset + Vec2{0, extent.y - radii.w}, .uv = {}, .color = color}, - {.position = offset + Vec2{0, radii.x}, .uv = {}, .color = color}}; + Vertex2d const vertices[] = { + {.position = offset + Vec2{radii.x, 0}, .uv = {}, .color = color}, + {.position = offset + Vec2{extent.x - radii.y, 0}, .uv = {}, .color = color}, + {.position = offset + Vec2{extent.x, radii.y}, .uv = {}, .color = color}, + {.position = offset + Vec2{extent.x, extent.y - radii.z}, .uv = {}, .color = color}, + {.position = offset + Vec2{extent.x - radii.z, extent.y}, .uv = {}, .color = color}, + {.position = offset + Vec2{radii.w, extent.y}, .uv = {}, .color = color}, + {.position = offset + Vec2{0, extent.y - radii.w}, .uv = {}, .color = color}, + {.position = offset + Vec2{0, radii.x}, .uv = {}, .color = color}}; return polygon.copy(vertices); } -inline stx::Span lerp_uvs(stx::Span path, Vec2 extent, TextureRect texture_region) +inline stx::Span lerp_uvs(stx::Span path, Vec2 extent, + TextureRect texture_region) { for (Vertex2d &v : path) { @@ -206,7 +217,8 @@ inline stx::Span lerp_uvs(stx::Span path, Vec2 extent, Textu return path; } -inline stx::Span lerp_color_gradient(stx::Span path, Vec2 extent, LinearColorGradient gradient) +inline stx::Span lerp_color_gradient(stx::Span path, Vec2 extent, + LinearColorGradient gradient) { if (gradient.is_uniform()) { @@ -295,7 +307,9 @@ inline void add_line_stroke(Vec2 p0, Vec2 p1, f32 thickness, Vec4 color, stx::Ve } // line joint is a bevel joint, it is the most efficient since it re-uses existing vertices and doesn't require generating new vertices -inline void triangulate_line(stx::Span in_points, f32 thickness, stx::Vec &out_vertices, stx::Vec &out_indices, bool should_close) +inline void triangulate_line(stx::Span in_points, f32 thickness, + stx::Vec &out_vertices, stx::Vec &out_indices, + bool should_close) { if (in_points.size() < 2 || thickness == 0) { @@ -315,7 +329,8 @@ inline void triangulate_line(stx::Span in_points, f32 thickness, add_line_stroke(p0, p1, thickness, color, out_vertices); // weave the line triangles - u32 indices[] = {Vertex_index, Vertex_index + 1, Vertex_index + 3, Vertex_index, Vertex_index + 2, Vertex_index + 3}; + u32 indices[] = {Vertex_index, Vertex_index + 1, Vertex_index + 3, + Vertex_index, Vertex_index + 2, Vertex_index + 3}; out_indices.extend(indices).unwrap(); @@ -345,7 +360,8 @@ inline void triangulate_line(stx::Span in_points, f32 thickness, add_line_stroke(p0, p1, thickness, color, out_vertices); // weave the line triangles - u32 indices[] = {Vertex_index, Vertex_index + 1, Vertex_index + 3, Vertex_index, Vertex_index + 2, Vertex_index + 3}; + u32 indices[] = {Vertex_index, Vertex_index + 1, Vertex_index + 3, + Vertex_index, Vertex_index + 2, Vertex_index + 3}; out_indices.extend(indices).unwrap(); @@ -353,13 +369,12 @@ inline void triangulate_line(stx::Span in_points, f32 thickness, u32 prev_line_Vertex_index = Vertex_index - 4; u32 first_line_Vertex_index = 0; - u32 indices[] = { - // weave the previous line's end to the beginning of this line - prev_line_Vertex_index + 2, prev_line_Vertex_index + 3, Vertex_index, - prev_line_Vertex_index + 2, prev_line_Vertex_index + 3, Vertex_index + 1, - // weave this line's end to the beginning of the first line - Vertex_index + 2, Vertex_index + 3, first_line_Vertex_index, - Vertex_index + 2, Vertex_index + 3, first_line_Vertex_index + 1}; + u32 indices[] = {// weave the previous line's end to the beginning of this line + prev_line_Vertex_index + 2, prev_line_Vertex_index + 3, Vertex_index, + prev_line_Vertex_index + 2, prev_line_Vertex_index + 3, Vertex_index + 1, + // weave this line's end to the beginning of the first line + Vertex_index + 2, Vertex_index + 3, first_line_Vertex_index, + Vertex_index + 2, Vertex_index + 3, first_line_Vertex_index + 1}; out_indices.extend(indices).unwrap(); } @@ -368,14 +383,19 @@ inline void triangulate_line(stx::Span in_points, f32 thickness, struct DrawCommand { - std::string_view pipeline; /// ID of pipeline to use for rendering - u32 nvertices = 0; /// number of vertices for this draw call. offset is automatically determined - u32 nindices = 0; /// number of indices for this draw call. offset is automatically determined - u32 first_instance = 0; /// first instance used for instanced rendering - u32 ninstances = 1; /// number of instances used for instanced rendering - Rect scissor; /// determines visible area of the rendering operation, in framebuffer coordinates (0, 0) -> viewport_extent - image textures[NIMAGES_PER_DRAWCALL] = {}; /// textures bounded to each descriptor set, 8-max - u8 push_constant[PUSH_CONSTANT_SIZE] = {}; /// push constant used for draw call. maximum size of PUSH_CONSTANT_SIZE bytes + std::string_view pipeline; /// ID of pipeline to use for rendering + u32 nvertices = + 0; /// number of vertices for this draw call. offset is automatically determined + u32 nindices = + 0; /// number of indices for this draw call. offset is automatically determined + u32 first_instance = 0; /// first instance used for instanced rendering + u32 ninstances = 1; /// number of instances used for instanced rendering + Rect + scissor; /// determines visible area of the rendering operation, in framebuffer coordinates (0, 0) -> viewport_extent + image textures[NIMAGES_PER_DRAWCALL] = + {}; /// textures bounded to each descriptor set, 8-max + u8 push_constant[PUSH_CONSTANT_SIZE] = + {}; /// push constant used for draw call. maximum size of PUSH_CONSTANT_SIZE bytes template DrawCommand with_push_constant(T const &constant) @@ -403,8 +423,10 @@ struct DrawList struct CanvasState { - Mat3 local_transform; // local object transform, applies to local coordinates of the objects - Mat3 global_transform; // global scene transform, applies to the global coordinate of the objects + Mat3 + local_transform; // local object transform, applies to local coordinates of the objects + Mat3 + global_transform; // global scene transform, applies to the global coordinate of the objects Rect scissor; }; @@ -426,13 +448,14 @@ struct Canvas CanvasState state; stx::Vec state_stack; DrawList draw_list; - stx::Vec scratch; // scratch/temporary buffer for storing generating vertices before storing in the draw list + stx::Vec + scratch; // scratch/temporary buffer for storing generating vertices before storing in the draw list bool viewport_contains(Rect area) const { // TODO(lamarrr): check for scissor - return Rect{.offset = {}, .extent = viewport_extent} - .overlaps(transform2d(state.global_transform * state.local_transform, area)); + return Rect{.offset = {}, .extent = viewport_extent}.overlaps( + transform2d(state.global_transform * state.local_transform, area)); } Canvas &restart(Vec2 viewport_extent) @@ -450,11 +473,12 @@ struct Canvas { Vec2 viewport_extent_clamped = epsilon_clamp(viewport_extent); - Mat3 t = state.local_transform; /// apply local coordinate transform - t = translate2d(position) * t; /// apply positioning - t = state.global_transform * t; /// apply global coordinate transform - t = scale2d(2 / viewport_extent_clamped) * t; /// normalize to 0 to 2 coordinate range - t = translate2d(-1, -1) * t; /// normalize from [0, 2] to vulkan viewport coordinate range [-1, 1] + Mat3 t = state.local_transform; /// apply local coordinate transform + t = translate2d(position) * t; /// apply positioning + t = state.global_transform * t; /// apply global coordinate transform + t = scale2d(2 / viewport_extent_clamped) * t; /// normalize to 0 to 2 coordinate range + t = translate2d(-1, -1) * + t; /// normalize from [0, 2] to vulkan viewport coordinate range [-1, 1] return t; } @@ -468,14 +492,19 @@ struct Canvas /// pop state (transform and scissor) stack and restore state Canvas &restore() { - state = state_stack.pop().unwrap_or(CanvasState{.local_transform = Mat3::identity(), .global_transform = Mat3::identity(), .scissor = Rect{.offset = {0, 0}, .extent = viewport_extent}}); + state = state_stack.pop().unwrap_or( + CanvasState{.local_transform = Mat3::identity(), + .global_transform = Mat3::identity(), + .scissor = Rect{.offset = {0, 0}, .extent = viewport_extent}}); return *this; } /// reset the rendering context to its default state (transform and scissor) Canvas &reset() { - state = CanvasState{.local_transform = Mat3::identity(), .global_transform = Mat3::identity(), .scissor = Rect{.offset = {0, 0}, .extent = viewport_extent}}; + state = CanvasState{.local_transform = Mat3::identity(), + .global_transform = Mat3::identity(), + .scissor = Rect{.offset = {0, 0}, .extent = viewport_extent}}; state_stack.clear(); return *this; } @@ -596,21 +625,23 @@ struct Canvas draw_list.indices.extend(indices).unwrap(); - draw_list.commands.push(DrawCommand{ - .pipeline = DEFAULT_SHAPE_PIPELINE, - .nvertices = AS(u32, std::size(vertices)), - .nindices = AS(u32, std::size(indices)), - .first_instance = 0, - .ninstances = 1, - .scissor = Rect{.offset = {0, 0}, .extent = viewport_extent}, - .textures = {texture}} - .with_push_constant(make_transform(Vec2{0, 0}).transpose())) + draw_list.commands + .push(DrawCommand{.pipeline = DEFAULT_SHAPE_PIPELINE, + .nvertices = AS(u32, std::size(vertices)), + .nindices = AS(u32, std::size(indices)), + .first_instance = 0, + .ninstances = 1, + .scissor = Rect{.offset = {0, 0}, .extent = viewport_extent}, + .textures = {texture}} + .with_push_constant(make_transform(Vec2{0, 0}).transpose())) .unwrap(); return *this; } - Canvas &draw_path(stx::Span points, Vec2 position, Vec2 uv_stretch, f32 thickness, bool should_close, image texture = WHITE_IMAGE, TextureRect texture_region = TextureRect{.uv0 = Vec2{0, 0}, .uv1 = Vec2{1, 1}}) + Canvas &draw_path(stx::Span points, Vec2 position, Vec2 uv_stretch, f32 thickness, + bool should_close, image texture = WHITE_IMAGE, + TextureRect texture_region = TextureRect{.uv0 = Vec2{0, 0}, .uv1 = Vec2{1, 1}}) { if (points.size() < 2 || thickness == 0) { @@ -629,15 +660,15 @@ struct Canvas u32 nvertices = AS(u32, curr_nvertices - prev_nvertices); u32 nindices = AS(u32, curr_nindices - prev_nindices); - draw_list.commands.push(DrawCommand{ - .pipeline = DEFAULT_SHAPE_PIPELINE, - .nvertices = nvertices, - .nindices = nindices, - .first_instance = 0, - .ninstances = 1, - .scissor = state.scissor, - .textures = {texture}} - .with_push_constant(make_transform(position).transpose())) + draw_list.commands + .push(DrawCommand{.pipeline = DEFAULT_SHAPE_PIPELINE, + .nvertices = nvertices, + .nindices = nindices, + .first_instance = 0, + .ninstances = 1, + .scissor = state.scissor, + .textures = {texture}} + .with_push_constant(make_transform(position).transpose())) .unwrap(); return *this; @@ -652,7 +683,9 @@ struct Canvas triangulate_convex_polygon(draw_list.indices, npoints); - stx::Span polygon = draw_list.vertices.unsafe_resize_uninitialized(draw_list.vertices.size() + npoints).unwrap(); + stx::Span polygon = + draw_list.vertices.unsafe_resize_uninitialized(draw_list.vertices.size() + npoints) + .unwrap(); usize curr_nvertices = draw_list.vertices.size(); usize curr_nindices = draw_list.indices.size(); @@ -660,22 +693,23 @@ struct Canvas u32 nvertices = AS(u32, curr_nvertices - prev_nvertices); u32 nindices = AS(u32, curr_nindices - prev_nindices); - draw_list.commands.push(DrawCommand{ - .pipeline = DEFAULT_SHAPE_PIPELINE, - .nvertices = nvertices, - .nindices = nindices, - .first_instance = 0, - .ninstances = 1, - .scissor = state.scissor, - .textures = {texture}} - .with_push_constant(make_transform(position).transpose())) + draw_list.commands + .push(DrawCommand{.pipeline = DEFAULT_SHAPE_PIPELINE, + .nvertices = nvertices, + .nindices = nindices, + .first_instance = 0, + .ninstances = 1, + .scissor = state.scissor, + .textures = {texture}} + .with_push_constant(make_transform(position).transpose())) .unwrap(); return polygon; } // texture coordinates are assumed to already be filled and area of viewport known - Canvas &draw_convex_polygon_filled(stx::Span polygon, Vec2 position, image texture) + Canvas &draw_convex_polygon_filled(stx::Span polygon, Vec2 position, + image texture) { if (polygon.size() < 3) { @@ -687,26 +721,26 @@ struct Canvas return *this; } - Canvas &draw_rect_filled(Rect area, Color color, LinearColorGradient gradient = {}, image texture = WHITE_IMAGE, TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) + Canvas &draw_rect_filled(Rect area, Color color, LinearColorGradient gradient = {}, + image texture = WHITE_IMAGE, + TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) { if (!viewport_contains(area)) { return *this; } - paths::lerp_color_gradient(paths::lerp_uvs(paths::Rect(Vec2{0, 0}, - area.extent, - color.to_normalized_vec(), - reserve_convex_polygon(4, area.offset, texture)), - area.extent, - texture_region), - area.extent, - gradient); + paths::lerp_color_gradient( + paths::lerp_uvs(paths::Rect(Vec2{0, 0}, area.extent, color.to_normalized_vec(), + reserve_convex_polygon(4, area.offset, texture)), + area.extent, texture_region), + area.extent, gradient); return *this; } - Canvas &draw_rect_stroke(Rect area, Color color, f32 thickness, image texture = WHITE_IMAGE, TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) + Canvas &draw_rect_stroke(Rect area, Color color, f32 thickness, image texture = WHITE_IMAGE, + TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) { if (!viewport_contains(area) || thickness == 0) { @@ -715,12 +749,15 @@ struct Canvas Vertex2d line[4]; - paths::Rect(Vec2::splat(thickness / 2), area.extent - thickness, color.to_normalized_vec(), line); + paths::Rect(Vec2::splat(thickness / 2), area.extent - thickness, color.to_normalized_vec(), + line); return draw_path(line, area.offset, area.extent, thickness, true, texture, texture_region); } - Canvas &draw_circle_filled(Vec2 center, f32 radius, u32 nsegments, Color color, LinearColorGradient gradient = {}, image texture = WHITE_IMAGE, TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) + Canvas &draw_circle_filled(Vec2 center, f32 radius, u32 nsegments, Color color, + LinearColorGradient gradient = {}, image texture = WHITE_IMAGE, + TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) { Vec2 position = center - radius; Rect area{.offset = position, .extent = Vec2::splat(2 * radius)}; @@ -730,14 +767,18 @@ struct Canvas return *this; } - paths::lerp_color_gradient(paths::lerp_uvs(paths::circle(Vec2{0, 0}, radius, nsegments, color.to_normalized_vec(), reserve_convex_polygon(nsegments, position, texture)), - area.extent, texture_region), - area.extent, gradient); + paths::lerp_color_gradient( + paths::lerp_uvs(paths::circle(Vec2{0, 0}, radius, nsegments, color.to_normalized_vec(), + reserve_convex_polygon(nsegments, position, texture)), + area.extent, texture_region), + area.extent, gradient); return *this; } - Canvas &draw_circle_stroke(Vec2 center, f32 radius, u32 nsegments, Color color, f32 thickness, image texture = WHITE_IMAGE, TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) + Canvas &draw_circle_stroke(Vec2 center, f32 radius, u32 nsegments, Color color, f32 thickness, + image texture = WHITE_IMAGE, + TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) { Vec2 position = center - radius - thickness / 2; Rect area{.offset = position, .extent = Vec2::splat(2 * radius + thickness)}; @@ -749,12 +790,15 @@ struct Canvas scratch.unsafe_resize_uninitialized(nsegments).unwrap(); - paths::circle(Vec2::splat(thickness / 2), radius, nsegments, color.to_normalized_vec(), scratch); + paths::circle(Vec2::splat(thickness / 2), radius, nsegments, color.to_normalized_vec(), + scratch); return draw_path(scratch, area.offset, area.extent, thickness, true, texture, texture_region); } - Canvas &draw_arc_stroke(Vec2 center, f32 radius, f32 begin, f32 end, u32 nsegments, Color color, f32 thickness, image texture = WHITE_IMAGE, TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) + Canvas &draw_arc_stroke(Vec2 center, f32 radius, f32 begin, f32 end, u32 nsegments, Color color, + f32 thickness, image texture = WHITE_IMAGE, + TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) { Vec2 position = center - radius - thickness / 2; Rect area{.offset = position, .extent = Vec2::splat(2 * radius + thickness)}; @@ -764,14 +808,18 @@ struct Canvas return *this; } - paths::lerp_uvs(paths::arc(Vec2::splat(thickness / 2), radius, begin, end, nsegments, color.to_normalized_vec(), + paths::lerp_uvs(paths::arc(Vec2::splat(thickness / 2), radius, begin, end, nsegments, + color.to_normalized_vec(), reserve_convex_polygon(nsegments, position, texture)), area.extent, texture_region); return *this; } - Canvas &draw_ellipse_filled(Vec2 center, Vec2 radii, u32 nsegments, Color color, LinearColorGradient gradient = {}, image texture = WHITE_IMAGE, TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) + Canvas &draw_ellipse_filled(Vec2 center, Vec2 radii, u32 nsegments, Color color, + LinearColorGradient gradient = {}, image texture = WHITE_IMAGE, + TextureRect texture_region = TextureRect{.uv0 = {0, 0}, + .uv1 = {1, 1}}) { Vec2 position = center - radii; Rect area{.offset = position, .extent = 2 * radii}; @@ -781,14 +829,19 @@ struct Canvas return *this; } - paths::lerp_color_gradient(paths::lerp_uvs(paths::ellipse(Vec2{0, 0}, radii, nsegments, color.to_normalized_vec(), reserve_convex_polygon(nsegments, area.offset, texture)), - area.extent, texture_region), - area.extent, gradient); + paths::lerp_color_gradient( + paths::lerp_uvs(paths::ellipse(Vec2{0, 0}, radii, nsegments, color.to_normalized_vec(), + reserve_convex_polygon(nsegments, area.offset, texture)), + area.extent, texture_region), + area.extent, gradient); return *this; } - Canvas &draw_ellipse_stroke(Vec2 center, Vec2 radii, u32 nsegments, Color color, f32 thickness, image texture = WHITE_IMAGE, TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) + Canvas &draw_ellipse_stroke(Vec2 center, Vec2 radii, u32 nsegments, Color color, f32 thickness, + image texture = WHITE_IMAGE, + TextureRect texture_region = TextureRect{.uv0 = {0, 0}, + .uv1 = {1, 1}}) { Vec2 position = center - radii; Rect area{.offset = position, .extent = 2 * radii}; @@ -800,26 +853,36 @@ struct Canvas scratch.unsafe_resize_uninitialized(nsegments).unwrap(); - paths::ellipse(Vec2::splat(thickness / 2), radii - thickness, nsegments, color.to_normalized_vec(), scratch); + paths::ellipse(Vec2::splat(thickness / 2), radii - thickness, nsegments, + color.to_normalized_vec(), scratch); return draw_path(scratch, area.offset, area.extent, thickness, true, texture, texture_region); } - Canvas &draw_round_rect_filled(Rect area, Vec4 radii, u32 nsegments, Color color, LinearColorGradient gradient = {}, image texture = WHITE_IMAGE, TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) + Canvas &draw_round_rect_filled(Rect area, Vec4 radii, u32 nsegments, Color color, + LinearColorGradient gradient = {}, image texture = WHITE_IMAGE, + TextureRect texture_region = TextureRect{.uv0 = {0, 0}, + .uv1 = {1, 1}}) { if (!viewport_contains(area)) { return *this; } - paths::lerp_color_gradient(paths::lerp_uvs(paths::round_rect(Vec2{0, 0}, area.extent, radii, nsegments, color.to_normalized_vec(), reserve_convex_polygon(nsegments * 4 + 8, area.offset, texture)), - area.extent, texture_region), - area.extent, gradient); + paths::lerp_color_gradient( + paths::lerp_uvs( + paths::round_rect(Vec2{0, 0}, area.extent, radii, nsegments, color.to_normalized_vec(), + reserve_convex_polygon(nsegments * 4 + 8, area.offset, texture)), + area.extent, texture_region), + area.extent, gradient); return *this; } - Canvas &draw_round_rect_stroke(Rect area, Vec4 radii, u32 nsegments, Color color, f32 thickness, image texture = WHITE_IMAGE, TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) + Canvas &draw_round_rect_stroke(Rect area, Vec4 radii, u32 nsegments, Color color, f32 thickness, + image texture = WHITE_IMAGE, + TextureRect texture_region = TextureRect{.uv0 = {0, 0}, + .uv1 = {1, 1}}) { if (!viewport_contains(area) || thickness == 0) { @@ -828,26 +891,35 @@ struct Canvas scratch.unsafe_resize_uninitialized(nsegments * 4 + 8).unwrap(); - paths::round_rect(Vec2::splat(thickness / 2), area.extent - thickness, radii, nsegments, color.to_normalized_vec(), scratch); + paths::round_rect(Vec2::splat(thickness / 2), area.extent - thickness, radii, nsegments, + color.to_normalized_vec(), scratch); return draw_path(scratch, area.offset, area.extent, thickness, true, texture, texture_region); } - Canvas &draw_bevel_rect_filled(Rect area, Vec4 radii, Color color, LinearColorGradient gradient = {}, image texture = WHITE_IMAGE, TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) + Canvas &draw_bevel_rect_filled(Rect area, Vec4 radii, Color color, + LinearColorGradient gradient = {}, image texture = WHITE_IMAGE, + TextureRect texture_region = TextureRect{.uv0 = {0, 0}, + .uv1 = {1, 1}}) { if (!viewport_contains(area)) { return *this; } - paths::lerp_color_gradient(paths::lerp_uvs(paths::bevel_rect(Vec2{0, 0}, area.extent, radii, color.to_normalized_vec(), reserve_convex_polygon(8, area.offset, texture)), - area.extent, texture_region), - area.extent, gradient); + paths::lerp_color_gradient( + paths::lerp_uvs(paths::bevel_rect(Vec2{0, 0}, area.extent, radii, color.to_normalized_vec(), + reserve_convex_polygon(8, area.offset, texture)), + area.extent, texture_region), + area.extent, gradient); return *this; } - Canvas &draw_bevel_rect_stroke(Rect area, Vec4 radii, Color color, f32 thickness, image texture = WHITE_IMAGE, TextureRect texture_region = TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}) + Canvas &draw_bevel_rect_stroke(Rect area, Vec4 radii, Color color, f32 thickness, + image texture = WHITE_IMAGE, + TextureRect texture_region = TextureRect{.uv0 = {0, 0}, + .uv1 = {1, 1}}) { if (!viewport_contains(area) || thickness == 0) { @@ -856,7 +928,8 @@ struct Canvas scratch.unsafe_resize_uninitialized(8).unwrap(); - paths::bevel_rect(Vec2::splat(thickness / 2), area.extent - thickness, radii, color.to_normalized_vec(), scratch); + paths::bevel_rect(Vec2::splat(thickness / 2), area.extent - thickness, radii, + color.to_normalized_vec(), scratch); return draw_path(scratch, area.offset, area.extent, thickness, true, texture, texture_region); } @@ -868,7 +941,8 @@ struct Canvas return *this; } - paths::lerp_uvs(paths::Rect(Vec2{0, 0}, area.extent, tint.to_normalized_vec(), reserve_convex_polygon(4, area.offset, img)), + paths::lerp_uvs(paths::Rect(Vec2{0, 0}, area.extent, tint.to_normalized_vec(), + reserve_convex_polygon(4, area.offset, img)), area.extent, texture_region); return *this; @@ -879,124 +953,163 @@ struct Canvas return draw_image(img, area, TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}, tint); } - Canvas &draw_rounded_image(image img, Rect area, Vec4 border_radii, u32 nsegments, TextureRect texture_region, Color tint = colors::WHITE) + Canvas &draw_rounded_image(image img, Rect area, Vec4 border_radii, u32 nsegments, + TextureRect texture_region, Color tint = colors::WHITE) { if (!viewport_contains(area)) { return *this; } - paths::lerp_uvs(paths::round_rect(Vec2{0, 0}, area.extent, border_radii, nsegments, tint.to_normalized_vec(), reserve_convex_polygon(nsegments * 4 + 8, area.offset, img)), + paths::lerp_uvs(paths::round_rect(Vec2{0, 0}, area.extent, border_radii, nsegments, + tint.to_normalized_vec(), + reserve_convex_polygon(nsegments * 4 + 8, area.offset, img)), area.extent, texture_region); return *this; } - Canvas &draw_rounded_image(image img, Rect area, Vec4 border_radii, u32 nsegments, Color tint = colors::WHITE) + Canvas &draw_rounded_image(image img, Rect area, Vec4 border_radii, u32 nsegments, + Color tint = colors::WHITE) { - return draw_rounded_image(img, area, border_radii, nsegments, TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}, tint); + return draw_rounded_image(img, area, border_radii, nsegments, + TextureRect{.uv0 = {0, 0}, .uv1 = {1, 1}}, tint); } - Canvas &draw_glyph(Vec2 block_position, Vec2 baseline, f32 text_scale_factor, Glyph const &glyph, GlyphShaping const &shaping, TextStyle const &style, gfx::image atlas) + Canvas &draw_glyph(Vec2 block_position, Vec2 baseline, f32 text_scale_factor, Glyph const &glyph, + GlyphShaping const &shaping, TextStyle const &style, gfx::image atlas) { save(); state.local_transform = state.local_transform * translate2d(baseline); Rect grect; - grect.offset = Vec2{glyph.metrics.bearing.x, -glyph.metrics.bearing.y} * style.font_height * text_scale_factor + shaping.offset; + grect.offset = Vec2{glyph.metrics.bearing.x, -glyph.metrics.bearing.y} * style.font_height * + text_scale_factor + + shaping.offset; grect.extent = glyph.metrics.extent * style.font_height * text_scale_factor; - if (!Rect{.offset = {}, .extent = viewport_extent}.overlaps(transform2d(state.global_transform * translate2d(block_position) * state.local_transform, grect))) + if (!Rect{.offset = {}, .extent = viewport_extent}.overlaps(transform2d( + state.global_transform * translate2d(block_position) * state.local_transform, grect))) { restore(); return *this; } - Vertex2d const vertices[] = {{.position = grect.top_left(), .uv = glyph.bin_region.top_left(), .color = style.foreground_color.to_normalized_vec()}, - {.position = grect.top_right(), .uv = glyph.bin_region.top_right(), .color = style.foreground_color.to_normalized_vec()}, - {.position = grect.bottom_right(), .uv = glyph.bin_region.bottom_right(), .color = style.foreground_color.to_normalized_vec()}, - {.position = grect.bottom_left(), .uv = glyph.bin_region.bottom_left(), .color = style.foreground_color.to_normalized_vec()}}; + Vertex2d const vertices[] = {{.position = grect.top_left(), + .uv = glyph.bin_region.top_left(), + .color = style.foreground_color.to_normalized_vec()}, + {.position = grect.top_right(), + .uv = glyph.bin_region.top_right(), + .color = style.foreground_color.to_normalized_vec()}, + {.position = grect.bottom_right(), + .uv = glyph.bin_region.bottom_right(), + .color = style.foreground_color.to_normalized_vec()}, + {.position = grect.bottom_left(), + .uv = glyph.bin_region.bottom_left(), + .color = style.foreground_color.to_normalized_vec()}}; draw_list.vertices.extend(vertices).unwrap(); triangulate_convex_polygon(draw_list.indices, 4); - draw_list.commands.push(DrawCommand{ - .pipeline = DEFAULT_GLYPH_PIPELINE, - .nvertices = 4, - .nindices = 6, - .first_instance = 0, - .ninstances = 1, - .scissor = state.scissor, - .textures = {atlas}} - .with_push_constant(make_transform(block_position).transpose())) + draw_list.commands + .push(DrawCommand{.pipeline = DEFAULT_GLYPH_PIPELINE, + .nvertices = 4, + .nindices = 6, + .first_instance = 0, + .ninstances = 1, + .scissor = state.scissor, + .textures = {atlas}} + .with_push_constant(make_transform(block_position).transpose())) .unwrap(); restore(); return *this; } - Canvas &draw_glyph_shadow(Vec2 block_position, Vec2 baseline, f32 text_scale_factor, Glyph const &glyph, GlyphShaping const &shaping, TextStyle const &style, gfx::image atlas) + Canvas &draw_glyph_shadow(Vec2 block_position, Vec2 baseline, f32 text_scale_factor, + Glyph const &glyph, GlyphShaping const &shaping, TextStyle const &style, + gfx::image atlas) { save(); state.local_transform = state.local_transform * translate2d(baseline); // TODO(lamarrr): add offset to shadow scale? and let offset be from midpoint?? Rect grect; - grect.offset = Vec2{glyph.metrics.bearing.x, -glyph.metrics.bearing.y} * style.font_height * text_scale_factor + shaping.offset; + grect.offset = Vec2{glyph.metrics.bearing.x, -glyph.metrics.bearing.y} * style.font_height * + text_scale_factor + + shaping.offset; grect.extent = glyph.metrics.extent * style.font_height * text_scale_factor; Rect srect = grect; srect.extent = srect.extent * style.shadow_scale; srect.offset = grect.offset + style.shadow_offset; - if (!Rect{.offset = {}, .extent = viewport_extent}.overlaps(transform2d(state.global_transform * translate2d(block_position) * state.local_transform, srect))) + if (!Rect{.offset = {}, .extent = viewport_extent}.overlaps(transform2d( + state.global_transform * translate2d(block_position) * state.local_transform, srect))) { restore(); return *this; } - Vertex2d const vertices[] = {{.position = srect.top_left(), .uv = glyph.bin_region.top_left(), .color = style.shadow_color.to_normalized_vec()}, - {.position = srect.top_right(), .uv = glyph.bin_region.top_right(), .color = style.shadow_color.to_normalized_vec()}, - {.position = srect.bottom_right(), .uv = glyph.bin_region.bottom_right(), .color = style.shadow_color.to_normalized_vec()}, - {.position = srect.bottom_left(), .uv = glyph.bin_region.bottom_left(), .color = style.shadow_color.to_normalized_vec()}}; + Vertex2d const vertices[] = {{.position = srect.top_left(), + .uv = glyph.bin_region.top_left(), + .color = style.shadow_color.to_normalized_vec()}, + {.position = srect.top_right(), + .uv = glyph.bin_region.top_right(), + .color = style.shadow_color.to_normalized_vec()}, + {.position = srect.bottom_right(), + .uv = glyph.bin_region.bottom_right(), + .color = style.shadow_color.to_normalized_vec()}, + {.position = srect.bottom_left(), + .uv = glyph.bin_region.bottom_left(), + .color = style.shadow_color.to_normalized_vec()}}; draw_list.vertices.extend(vertices).unwrap(); triangulate_convex_polygon(draw_list.indices, 4); - draw_list.commands.push(DrawCommand{ - .pipeline = DEFAULT_GLYPH_PIPELINE, - .nvertices = 4, - .nindices = 6, - .first_instance = 0, - .ninstances = 1, - .scissor = state.scissor, - .textures = {atlas}} - .with_push_constant(make_transform(block_position).transpose())) + draw_list.commands + .push(DrawCommand{.pipeline = DEFAULT_GLYPH_PIPELINE, + .nvertices = 4, + .nindices = 6, + .first_instance = 0, + .ninstances = 1, + .scissor = state.scissor, + .textures = {atlas}} + .with_push_constant(make_transform(block_position).transpose())) .unwrap(); restore(); return *this; } - Canvas &draw_text_segment_lines(Vec2 block_position, Vec2 baseline, f32 line_height, f32 segment_width, TextStyle const &style) + Canvas &draw_text_segment_lines(Vec2 block_position, Vec2 baseline, f32 line_height, + f32 segment_width, TextStyle const &style) { save(); translate(block_position); if (style.strikethrough_color.is_visible() && style.strikethrough_thickness > 0) { - Vertex2d const strikethrough_path[] = {{.position = baseline - Vec2{0, line_height / 2}, .uv = {}, .color = style.strikethrough_color.to_normalized_vec()}, - {.position = baseline - Vec2{-segment_width, line_height / 2}, .uv = {}, .color = style.strikethrough_color.to_normalized_vec()}}; + Vertex2d const strikethrough_path[] = { + {.position = baseline - Vec2{0, line_height / 2}, + .uv = {}, + .color = style.strikethrough_color.to_normalized_vec()}, + {.position = baseline - Vec2{-segment_width, line_height / 2}, + .uv = {}, + .color = style.strikethrough_color.to_normalized_vec()}}; draw_path(strikethrough_path, Vec2{0, 0}, Vec2{0, 0}, style.strikethrough_thickness, false); } if (style.underline_color.is_visible() && style.underline_thickness > 0) { - Vertex2d const underline_path[] = {{.position = baseline, .uv = {}, .color = style.underline_color.to_normalized_vec()}, - {.position = baseline + Vec2{segment_width, 0}, .uv = {}, .color = style.underline_color.to_normalized_vec()}}; + Vertex2d const underline_path[] = { + {.position = baseline, .uv = {}, .color = style.underline_color.to_normalized_vec()}, + {.position = baseline + Vec2{segment_width, 0}, + .uv = {}, + .color = style.underline_color.to_normalized_vec()}}; draw_path(underline_path, Vec2{0, 0}, Vec2{0, 0}, style.underline_thickness, false); } @@ -1006,7 +1119,8 @@ struct Canvas return *this; } - Canvas &draw_text_segment_background(Vec2 block_position, Vec2 line_top, Vec2 extent, TextStyle const &style) + Canvas &draw_text_segment_background(Vec2 block_position, Vec2 line_top, Vec2 extent, + TextStyle const &style) { save(); translate(block_position); @@ -1016,7 +1130,8 @@ struct Canvas } // TODO(lamarrr): text gradient, reset on each line or continue???? how does css do it? - Canvas &draw_text(TextBlock const &block, TextLayout const &layout, stx::Span font_bundle, Vec2 const position) + Canvas &draw_text(TextBlock const &block, TextLayout const &layout, + stx::Span font_bundle, Vec2 const position) { /// TEXT BACKGROUNDS /// { @@ -1058,12 +1173,16 @@ struct Canvas f32 x_cursor = x_alignment; - for (TextRunSegment const &segment : layout.run_segments.span().slice(line.run_segments_offset, line.nrun_segments)) + for (TextRunSegment const &segment : + layout.run_segments.span().slice(line.run_segments_offset, line.nrun_segments)) { - TextStyle const &segment_style = segment.style >= block.styles.size() ? block.default_style : block.styles[segment.style]; + TextStyle const &segment_style = segment.style >= block.styles.size() ? + block.default_style : + block.styles[segment.style]; if (segment_style.background_color.is_visible()) { - draw_text_segment_background(position, Vec2{x_cursor, line_top}, Vec2{segment.width, line.line_height}, segment_style); + draw_text_segment_background(position, Vec2{x_cursor, line_top}, + Vec2{segment.width, line.line_height}, segment_style); } x_cursor += segment.width; @@ -1111,12 +1230,15 @@ struct Canvas } f32 x_segment_cursor = x_alignment; - f32 const line_gap = std::max(line.line_height - (line.ascent + line.descent), 0.0f) / 2; - f32 const baseline = line_top + line.line_height - line_gap - line.descent; + f32 const line_gap = std::max(line.line_height - (line.ascent + line.descent), 0.0f) / 2; + f32 const baseline = line_top + line.line_height - line_gap - line.descent; - for (TextRunSegment const &segment : layout.run_segments.span().slice(line.run_segments_offset, line.nrun_segments)) + for (TextRunSegment const &segment : + layout.run_segments.span().slice(line.run_segments_offset, line.nrun_segments)) { - TextStyle const &segment_style = segment.style >= block.styles.size() ? block.default_style : block.styles[segment.style]; + TextStyle const &segment_style = segment.style >= block.styles.size() ? + block.default_style : + block.styles[segment.style]; if (segment_style.shadow_color.is_transparent() || segment_style.shadow_scale <= 0) { @@ -1126,9 +1248,12 @@ struct Canvas FontAtlas const &atlas = font_bundle[segment.font].atlas; f32 x_cursor = x_segment_cursor; - for (GlyphShaping const &shaping : layout.glyph_shapings.span().slice(segment.glyph_shapings_offset, segment.nglyph_shapings)) + for (GlyphShaping const &shaping : layout.glyph_shapings.span().slice( + segment.glyph_shapings_offset, segment.nglyph_shapings)) { - draw_glyph_shadow(position, Vec2{x_cursor, baseline}, layout.text_scale_factor, atlas.glyphs[shaping.glyph], shaping, segment_style, atlas.bins[atlas.glyphs[shaping.glyph].bin].texture); + draw_glyph_shadow(position, Vec2{x_cursor, baseline}, layout.text_scale_factor, + atlas.glyphs[shaping.glyph], shaping, segment_style, + atlas.bins[atlas.glyphs[shaping.glyph].bin].texture); x_cursor += shaping.advance + layout.text_scale_factor * segment_style.letter_spacing; } @@ -1177,18 +1302,24 @@ struct Canvas } f32 x_segment_cursor = x_alignment; - f32 const line_gap = std::max(line.line_height - (line.ascent + line.descent), 0.0f) / 2; - f32 const baseline = line_top + line.line_height - line_gap - line.descent; + f32 const line_gap = std::max(line.line_height - (line.ascent + line.descent), 0.0f) / 2; + f32 const baseline = line_top + line.line_height - line_gap - line.descent; - for (TextRunSegment const &segment : layout.run_segments.span().slice(line.run_segments_offset, line.nrun_segments)) + for (TextRunSegment const &segment : + layout.run_segments.span().slice(line.run_segments_offset, line.nrun_segments)) { - TextStyle const &segment_style = segment.style >= block.styles.size() ? block.default_style : block.styles[segment.style]; + TextStyle const &segment_style = segment.style >= block.styles.size() ? + block.default_style : + block.styles[segment.style]; FontAtlas const &atlas = font_bundle[segment.font].atlas; f32 x_cursor = x_segment_cursor; - for (GlyphShaping const &shaping : layout.glyph_shapings.span().slice(segment.glyph_shapings_offset, segment.nglyph_shapings)) + for (GlyphShaping const &shaping : layout.glyph_shapings.span().slice( + segment.glyph_shapings_offset, segment.nglyph_shapings)) { - draw_glyph(position, Vec2{x_cursor, baseline}, layout.text_scale_factor, atlas.glyphs[shaping.glyph], shaping, segment_style, atlas.bins[atlas.glyphs[shaping.glyph].bin].texture); + draw_glyph(position, Vec2{x_cursor, baseline}, layout.text_scale_factor, + atlas.glyphs[shaping.glyph], shaping, segment_style, + atlas.bins[atlas.glyphs[shaping.glyph].bin].texture); x_cursor += shaping.advance + layout.text_scale_factor * segment_style.letter_spacing; } @@ -1241,13 +1372,20 @@ struct Canvas f32 const line_gap = std::max(line.line_height - (line.ascent + line.descent), 0.0f) / 2; f32 const baseline = line_top + line.line_height - line_gap - line.descent; - for (TextRunSegment const &segment : layout.run_segments.span().slice(line.run_segments_offset, line.nrun_segments)) + for (TextRunSegment const &segment : + layout.run_segments.span().slice(line.run_segments_offset, line.nrun_segments)) { - TextStyle const &segment_style = segment.style >= block.styles.size() ? block.default_style : block.styles[segment.style]; - - if ((segment_style.underline_color.is_visible() && segment_style.underline_thickness > 0) || (segment_style.strikethrough_color.is_visible() && segment_style.strikethrough_thickness > 0)) [[unlikely]] + TextStyle const &segment_style = segment.style >= block.styles.size() ? + block.default_style : + block.styles[segment.style]; + + if ((segment_style.underline_color.is_visible() && + segment_style.underline_thickness > 0) || + (segment_style.strikethrough_color.is_visible() && + segment_style.strikethrough_thickness > 0)) [[unlikely]] { - draw_text_segment_lines(position, Vec2{x_cursor, baseline}, line.line_height, segment.width, segment_style); + draw_text_segment_lines(position, Vec2{x_cursor, baseline}, line.line_height, + segment.width, segment_style); } x_cursor += segment.width; diff --git a/ashura/include/ashura/context.h b/ashura/include/ashura/context.h index 2583b7cbc..6bea3b4c0 100644 --- a/ashura/include/ashura/context.h +++ b/ashura/include/ashura/context.h @@ -50,7 +50,8 @@ struct Context FrameStats frame_stats; f32 text_scale_factor = 1; Widget *root = nullptr; - stx::Vec key_events; // These are more of key state polling than key event state change notifications + stx::Vec + key_events; // These are more of key state polling than key event state change notifications // TODO(lamarrr): expose current window here??? @@ -65,7 +66,8 @@ struct Context } Context() - {} + { + } STX_MAKE_PINNED(Context) @@ -73,7 +75,8 @@ struct Context { for (Subsystem *subsystem : subsystems) { - ASH_LOG_INFO(Context, "Destroying subsystem: {} (type: {})", subsystem->get_name(), typeid(*subsystem).name()); + ASH_LOG_INFO(Context, "Destroying subsystem: {} (type: {})", subsystem->get_name(), + typeid(*subsystem).name()); delete subsystem; } } @@ -81,13 +84,15 @@ struct Context void register_subsystem(Subsystem *subsystem) { subsystems.push_inplace(subsystem).unwrap(); - ASH_LOG_INFO(Context, "Registered subsystem: {} (type: {})", subsystem->get_name(), typeid(*subsystem).name()); + ASH_LOG_INFO(Context, "Registered subsystem: {} (type: {})", subsystem->get_name(), + typeid(*subsystem).name()); } template stx::Option get_subsystem(std::string_view name) const { - stx::Span subsystem = subsystems.span().which([name](Subsystem *subsystem) { return subsystem->get_name() == name; }); + stx::Span subsystem = subsystems.span().which( + [name](Subsystem *subsystem) { return subsystem->get_name() == name; }); if (!subsystem.is_empty()) { return stx::Some(subsystem[0]->template as()); @@ -207,7 +212,10 @@ struct Context case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_MOUSE_BUTTON_UP: { - MouseClickEvent mouse_event{.mouse_id = MouseID{event.button.which}, .position = Vec2{AS(f32, event.button.x), AS(f32, event.button.y)}, .clicks = event.button.clicks}; + MouseClickEvent mouse_event{.mouse_id = MouseID{event.button.which}, + .position = + Vec2{AS(f32, event.button.x), AS(f32, event.button.y)}, + .clicks = event.button.clicks}; switch (event.button.button) { case SDL_BUTTON_LEFT: @@ -264,7 +272,10 @@ struct Context } for (auto &listener : win->event_listeners.mouse_motion) { - listener.handle(MouseMotionEvent{.mouse_id = MouseID{event.motion.which}, .position = Vec2{AS(f32, event.motion.x), AS(f32, event.motion.y)}, .translation = Vec2{AS(f32, event.motion.xrel), AS(f32, event.motion.yrel)}}); + listener.handle(MouseMotionEvent{ + .mouse_id = MouseID{event.motion.which}, + .position = Vec2{AS(f32, event.motion.x), AS(f32, event.motion.y)}, + .translation = Vec2{AS(f32, event.motion.xrel), AS(f32, event.motion.yrel)}}); } return true; } @@ -278,7 +289,10 @@ struct Context } for (auto &listener : win->event_listeners.mouse_wheel) { - listener.handle(MouseWheelEvent{.mouse_id = MouseID{event.wheel.which}, .position = Vec2{AS(f32, event.wheel.mouseX), AS(f32, event.wheel.mouseY)}, .translation = Vec2{event.wheel.x, event.wheel.y}}); + listener.handle(MouseWheelEvent{ + .mouse_id = MouseID{event.wheel.which}, + .position = Vec2{AS(f32, event.wheel.mouseX), AS(f32, event.wheel.mouseY)}, + .translation = Vec2{event.wheel.x, event.wheel.y}}); } return true; } @@ -293,7 +307,11 @@ struct Context } for (auto &listener : win->event_listeners.key) { - listener.handle(KeyEvent{.key = event.key.keysym.sym, .modifiers = AS(KeyModifiers, event.key.keysym.mod), .action = event.type == SDL_EVENT_KEY_DOWN ? KeyAction::Press : KeyAction::Release}); + listener.handle(KeyEvent{.key = event.key.keysym.sym, + .modifiers = AS(KeyModifiers, event.key.keysym.mod), + .action = event.type == SDL_EVENT_KEY_DOWN ? + KeyAction::Press : + KeyAction::Release}); } return true; } @@ -312,13 +330,17 @@ struct Context case SDL_EVENT_DROP_BEGIN: { - ASH_LOG_INFO(Vulkan, "drop begin: {} x={}, y={}", event.drop.file == nullptr ? " " : event.drop.file, event.drop.x, event.drop.y); + ASH_LOG_INFO(Vulkan, "drop begin: {} x={}, y={}", + event.drop.file == nullptr ? " " : event.drop.file, event.drop.x, + event.drop.y); return true; } case SDL_EVENT_DROP_COMPLETE: { - ASH_LOG_INFO(Vulkan, "drop complete: {} x={}, y={}", event.drop.file == nullptr ? " " : event.drop.file, event.drop.x, event.drop.y); + ASH_LOG_INFO(Vulkan, "drop complete: {} x={}, y={}", + event.drop.file == nullptr ? " " : event.drop.file, event.drop.x, + event.drop.y); return true; } @@ -326,19 +348,25 @@ struct Context { f32 x = 0, y = 0; SDL_GetMouseState(&x, &y); - ASH_LOG_INFO(Vulkan, "drop file: {} x={}, y={}, {},{}", event.drop.file == nullptr ? " " : event.drop.file, event.drop.x, event.drop.y, x, y); + ASH_LOG_INFO(Vulkan, "drop file: {} x={}, y={}, {},{}", + event.drop.file == nullptr ? " " : event.drop.file, event.drop.x, + event.drop.y, x, y); return true; } case SDL_EVENT_DROP_POSITION: { - ASH_LOG_INFO(Vulkan, "drop position: {} x={}, y={}", event.drop.file == nullptr ? " " : event.drop.file, event.drop.x, event.drop.y); + ASH_LOG_INFO(Vulkan, "drop position: {} x={}, y={}", + event.drop.file == nullptr ? " " : event.drop.file, event.drop.x, + event.drop.y); return true; } case SDL_EVENT_DROP_TEXT: { - ASH_LOG_INFO(Vulkan, "drop text: {} x={}, y={}", event.drop.file == nullptr ? " " : event.drop.file, event.drop.x, event.drop.y); + ASH_LOG_INFO(Vulkan, "drop text: {} x={}, y={}", + event.drop.file == nullptr ? " " : event.drop.file, event.drop.x, + event.drop.y); return true; } diff --git a/ashura/include/ashura/curve.h b/ashura/include/ashura/curve.h index 7def1877c..09ed689cf 100644 --- a/ashura/include/ashura/curve.h +++ b/ashura/include/ashura/curve.h @@ -10,7 +10,8 @@ namespace ash struct Curve { virtual ~Curve() - {} + { + } virtual f32 operator()(f32 t) = 0; }; @@ -18,7 +19,8 @@ struct Curve struct Linear final : public Curve { virtual ~Linear() override - {} + { + } virtual f32 operator()(f32 t) override { @@ -29,7 +31,8 @@ struct Linear final : public Curve struct EaseIn final : public Curve { virtual ~EaseIn() override - {} + { + } virtual f32 operator()(f32 t) override { @@ -40,7 +43,8 @@ struct EaseIn final : public Curve struct EaseOut final : public Curve { virtual ~EaseOut() override - {} + { + } virtual f32 operator()(f32 t) override { @@ -51,7 +55,8 @@ struct EaseOut final : public Curve struct EaseInOut final : public Curve { virtual ~EaseInOut() override - {} + { + } virtual f32 operator()(f32 t) override { @@ -60,10 +65,12 @@ struct EaseInOut final : public Curve }; struct Quadratic final : public Curve -{}; +{ +}; struct Cubic final : public Curve -{}; +{ +}; struct QuadraticBezier final : public Curve { @@ -72,7 +79,8 @@ struct QuadraticBezier final : public Curve f32 p2 = 0; virtual ~QuadraticBezier() override - {} + { + } virtual f32 operator()(f32 t) override { @@ -82,6 +90,7 @@ struct QuadraticBezier final : public Curve // TODO(lamarrr): Splines, Bezier Curves, Hermite Curves, Catmull-Rom curves, B-Spline struct Spline final : public Curve -{}; +{ +}; }; // namespace ash diff --git a/ashura/include/ashura/ecs.h b/ashura/include/ashura/ecs.h index 5cb87f78b..3f0bdc74d 100644 --- a/ashura/include/ashura/ecs.h +++ b/ashura/include/ashura/ecs.h @@ -91,11 +91,12 @@ struct Registry // check entity validity: (entity_id < nentities) && ((entity_free_mask[entity_id >> 6] >> (entity_id & 63)) & 1) // free entity: free components && entity_free_mask[] u64 **entity_component_map = nullptr; - u64 *entity_free_mask = nullptr; // we don't want to waste indices and have to update indirections - u64 *free_entity_indices = nullptr; - u64 entities_capacity = 0; - u64 nentities = 0; - u64 nfree_entities = 0; + u64 *entity_free_mask = + nullptr; // we don't want to waste indices and have to update indirections + u64 *free_entity_indices = nullptr; + u64 entities_capacity = 0; + u64 nentities = 0; + u64 nfree_entities = 0; }; // say for example we want a system that processes elements in batches diff --git a/ashura/include/ashura/engine.h b/ashura/include/ashura/engine.h index 4fd64c91d..738399950 100644 --- a/ashura/include/ashura/engine.h +++ b/ashura/include/ashura/engine.h @@ -28,7 +28,8 @@ struct Engine template DerivedWidget> Engine(AppConfig const &cfg, DerivedWidget &&root_widget) : Engine{cfg, new DerivedWidget{std::move(root_widget)}} - {} + { + } Engine(AppConfig const &cfg, Widget *root_widget); diff --git a/ashura/include/ashura/event.h b/ashura/include/ashura/event.h index 2d3ce0cf8..138c39843 100644 --- a/ashura/include/ashura/event.h +++ b/ashura/include/ashura/event.h @@ -390,7 +390,7 @@ struct WindowEventListeners stx::Vec> mouse_click; stx::Vec> mouse_motion; stx::Vec> mouse_wheel; - stx::Vec> key; + stx::Vec> key; }; struct GlobalEventListeners diff --git a/ashura/include/ashura/font.h b/ashura/include/ashura/font.h index 894b10738..79c275bb7 100644 --- a/ashura/include/ashura/font.h +++ b/ashura/include/ashura/font.h @@ -48,8 +48,8 @@ struct Font stx::Vec data; Font(stx::String ipostscript_name, stx::String ifamily_name, stx::String istyle_name, - hb_blob_t *ihb_blob, hb_face_t *ihb_face, hb_font_t *ihb_font, - u32 infaces, u32 iselected_face, stx::Vec idata) : + hb_blob_t *ihb_blob, hb_face_t *ihb_face, hb_font_t *ihb_font, u32 infaces, + u32 iselected_face, stx::Vec idata) : postscript_name{std::move(ipostscript_name)}, family_name{std::move(ifamily_name)}, style_name{std::move(istyle_name)}, @@ -59,7 +59,8 @@ struct Font nfaces{infaces}, selected_face{iselected_face}, data{std::move(idata)} - {} + { + } STX_MAKE_PINNED(Font) @@ -71,9 +72,11 @@ struct Font } }; -inline stx::Result, FontLoadError> load_font_from_memory(stx::Vec data, u32 selected_face) +inline stx::Result, FontLoadError> load_font_from_memory(stx::Vec data, + u32 selected_face) { - hb_blob_t *hb_blob = hb_blob_create(data.span().as_char().data(), AS(uint, data.size()), HB_MEMORY_MODE_READONLY, nullptr, nullptr); + hb_blob_t *hb_blob = hb_blob_create(data.span().as_char().data(), AS(uint, data.size()), + HB_MEMORY_MODE_READONLY, nullptr, nullptr); ASH_CHECK(hb_blob != nullptr); @@ -110,19 +113,29 @@ inline stx::Result, FontLoadError> load_font_from_memory(stx::Ve } char const *p_postscript_name = FT_Get_Postscript_Name(ft_face); - stx::String postscript_name = p_postscript_name == nullptr ? "" : stx::string::make(stx::os_allocator, p_postscript_name).unwrap(); - stx::String family_name = ft_face->family_name == nullptr ? "" : stx::string::make(stx::os_allocator, ft_face->family_name).unwrap(); - stx::String style_name = ft_face->style_name == nullptr ? "" : stx::string::make(stx::os_allocator, ft_face->style_name).unwrap(); + stx::String postscript_name = + p_postscript_name == nullptr ? + "" : + stx::string::make(stx::os_allocator, p_postscript_name).unwrap(); + stx::String family_name = ft_face->family_name == nullptr ? + "" : + stx::string::make(stx::os_allocator, ft_face->family_name).unwrap(); + stx::String style_name = ft_face->style_name == nullptr ? + "" : + stx::string::make(stx::os_allocator, ft_face->style_name).unwrap(); ASH_CHECK(FT_Done_Face(ft_face) == 0); ASH_CHECK(FT_Done_FreeType(ft_lib) == 0); - return stx::Ok(stx::rc::make_inplace(stx::os_allocator, std::move(postscript_name), std::move(family_name), std::move(style_name), - hb_blob, hb_face, hb_font, nfaces, selected_face, std::move(data)) + return stx::Ok(stx::rc::make_inplace(stx::os_allocator, std::move(postscript_name), + std::move(family_name), std::move(style_name), hb_blob, + hb_face, hb_font, nfaces, selected_face, + std::move(data)) .unwrap()); } -inline stx::Result, FontLoadError> load_font_from_file(stx::CStringView path, u32 selected_face) +inline stx::Result, FontLoadError> load_font_from_file(stx::CStringView path, + u32 selected_face) { if (!std::filesystem::exists(path.c_str())) { @@ -174,12 +187,13 @@ struct GlyphMetrics /// NOTE: using stubs enables us to perform fast constant lookups of glyph indices by ensuring the array is filled and sorted by glyph index from 0 -> nglyphs_found_in_font-1 struct Glyph { - bool is_valid = false; // if the glyph was found in the font and loaded successfully - bool is_needed = false; // if the texture is a texture that is needed. i.e. if the unicode ranges are empty then this is always true, otherwise it is set to true if the config unicode ranges contains it, note that special glyphs like replacement unicode codepoint glyph (0xFFFD) will always be true - GlyphMetrics metrics; // normalized font metrics - u32 bin = 0; // atlas bin this glyph belongs to - URect bin_area; // area in the atlas this glyph's cache data is placed - TextureRect bin_region; // normalized texture coordinates of this glyph in the atlas bin + bool is_valid = false; // if the glyph was found in the font and loaded successfully + bool is_needed = + false; // if the texture is a texture that is needed. i.e. if the unicode ranges are empty then this is always true, otherwise it is set to true if the config unicode ranges contains it, note that special glyphs like replacement unicode codepoint glyph (0xFFFD) will always be true + GlyphMetrics metrics; // normalized font metrics + u32 bin = 0; // atlas bin this glyph belongs to + URect bin_area; // area in the atlas this glyph's cache data is placed + TextureRect bin_region; // normalized texture coordinates of this glyph in the atlas bin }; /// stores codepoint glyphs for a font at a specific font height @@ -194,14 +208,15 @@ struct Glyph /// struct FontAtlas { - stx::Vec glyphs; - u32 replacement_glyph = 0; // glyph for the replacement glyph 0xFFFD if found, otherwise glyph index 0 - u32 space_glyph = 0; - u32 ellipsis_glyph = 0; // glypg for the ellipsis character '…' - u32 font_height = 0; // font height at which the this atlas was rendered - f32 ascent = 0; // normalized maximum ascent of the font's glyphs - f32 descent = 0; // normalized maximum descent of the font's glyphs - f32 advance = 0; // normalized maximum advance of the font's glyphs + stx::Vec glyphs; + u32 replacement_glyph = + 0; // glyph for the replacement glyph 0xFFFD if found, otherwise glyph index 0 + u32 space_glyph = 0; + u32 ellipsis_glyph = 0; // glypg for the ellipsis character '…' + u32 font_height = 0; // font height at which the this atlas was rendered + f32 ascent = 0; // normalized maximum ascent of the font's glyphs + f32 descent = 0; // normalized maximum descent of the font's glyphs + f32 advance = 0; // normalized maximum advance of the font's glyphs stx::Vec bins; }; @@ -214,16 +229,20 @@ struct BundledFont struct FontSpec { - stx::String name; // name to use in font matching - stx::String path; // local file system path of the typeface resource - bool use_caching = true; // whether to try to load or save font atlas from the cache directory. the font is identified in the cache directory by its postscript name, which is different from its font matching name - u32 face = 0; // font face to use - u32 font_height = 40; // the height at which the SDF texture is cached at - Extent max_atlas_bin_extent = DEFAULT_MAX_ATLAS_BIN_EXTENT; // maximum extent of each atlas bin - stx::Span ranges = {}; // if set only the specified unicode ranges will be loaded, otherwise glyphs in the font will be loaded. Note that this means during font ligature glyph substitution where scripts might change, if the replacement glyph is not in the unicode range, it won't result in a valid glyph. + stx::String name; // name to use in font matching + stx::String path; // local file system path of the typeface resource + bool use_caching = + true; // whether to try to load or save font atlas from the cache directory. the font is identified in the cache directory by its postscript name, which is different from its font matching name + u32 face = 0; // font face to use + u32 font_height = 40; // the height at which the SDF texture is cached at + Extent max_atlas_bin_extent = + DEFAULT_MAX_ATLAS_BIN_EXTENT; // maximum extent of each atlas bin + stx::Span ranges = + {}; // if set only the specified unicode ranges will be loaded, otherwise glyphs in the font will be loaded. Note that this means during font ligature glyph substitution where scripts might change, if the replacement glyph is not in the unicode range, it won't result in a valid glyph. }; -inline std::pair> render_font_atlas(Font const &font, FontSpec const &spec) +inline std::pair> render_font_atlas(Font const &font, + FontSpec const &spec) { // NOTE: all *64 or << 6, /64 or >> 6 are to convert to and from 26.6 pixel format used in Freetype and Harfbuzz metrics @@ -240,21 +259,25 @@ inline std::pair> render_font_atlas(Font const ASH_CHECK(FT_Init_FreeType(&ft_lib) == 0); FT_Face ft_face; - ASH_CHECK(FT_New_Memory_Face(ft_lib, font.data.data(), (FT_Long) font.data.size(), (FT_Long) font.selected_face, &ft_face) == 0); + ASH_CHECK(FT_New_Memory_Face(ft_lib, font.data.data(), (FT_Long) font.data.size(), + (FT_Long) font.selected_face, &ft_face) == 0); ASH_CHECK(FT_Set_Char_Size(ft_face, 0, (FT_F26Dot6) (spec.font_height << 6), 72, 72) == 0); stx::Vec glyphs; u32 const nglyphs = (u32) ft_face->num_glyphs; - u32 const replacement_glyph = FT_Get_Char_Index(ft_face, HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT); // glyph index 0 is selected if the glyph for the replacement codepoint is not found - u32 const ellipsis_glyph = FT_Get_Char_Index(ft_face, 0x2026); - u32 const space_glyph = FT_Get_Char_Index(ft_face, ' '); - f32 const ascent = (ft_face->size->metrics.ascender / 64.0f) / spec.font_height; - f32 const descent = (ft_face->size->metrics.descender / -64.0f) / spec.font_height; - f32 const advance = (ft_face->size->metrics.max_advance / 64.0f) / spec.font_height; - - ASH_LOG_INFO(FontRenderer, "Fetching {} Glyph Metrics For Font: {}", nglyphs, font.postscript_name.c_str()); + u32 const replacement_glyph = FT_Get_Char_Index( + ft_face, + HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT); // glyph index 0 is selected if the glyph for the replacement codepoint is not found + u32 const ellipsis_glyph = FT_Get_Char_Index(ft_face, 0x2026); + u32 const space_glyph = FT_Get_Char_Index(ft_face, ' '); + f32 const ascent = (ft_face->size->metrics.ascender / 64.0f) / spec.font_height; + f32 const descent = (ft_face->size->metrics.descender / -64.0f) / spec.font_height; + f32 const advance = (ft_face->size->metrics.max_advance / 64.0f) / spec.font_height; + + ASH_LOG_INFO(FontRenderer, "Fetching {} Glyph Metrics For Font: {}", nglyphs, + font.postscript_name.c_str()); for (u32 glyph_index = 0; glyph_index < nglyphs; glyph_index++) { @@ -366,15 +389,15 @@ inline std::pair> render_font_atlas(Font const while (!unpacked_rects.is_empty()) { - rect_packer::Context pack_context = rect_packer::init(spec.max_atlas_bin_extent.width, - spec.max_atlas_bin_extent.height, - nodes.data(), - spec.max_atlas_bin_extent.width, - true); + rect_packer::Context pack_context = + rect_packer::init(spec.max_atlas_bin_extent.width, spec.max_atlas_bin_extent.height, + nodes.data(), spec.max_atlas_bin_extent.width, true); // tries to pack all the glyph rects into the provided extent - was_all_packed = rect_packer::pack_rects(pack_context, unpacked_rects.data(), AS(i32, unpacked_rects.size())); - auto [just_packed, unpacked] = unpacked_rects.partition([](rect_packer::rect const &r) { return r.was_packed; }); - unpacked_rects = unpacked; + was_all_packed = rect_packer::pack_rects(pack_context, unpacked_rects.data(), + AS(i32, unpacked_rects.size())); + auto [just_packed, unpacked] = + unpacked_rects.partition([](rect_packer::rect const &r) { return r.was_packed; }); + unpacked_rects = unpacked; // NOTE: vulkan doesn't allow zero-extent images Extent bin_extent{1, 1}; @@ -397,7 +420,9 @@ inline std::pair> render_font_atlas(Font const glyph.bin_region.uv1 = glyph.bin_area.max().to_vec() / bin_extent.to_vec(); } - bins.push(FontAtlasBin{.texture = gfx::WHITE_IMAGE, .extent = bin_extent, .used_area = used_area}).unwrap(); + bins.push(FontAtlasBin{ + .texture = gfx::WHITE_IMAGE, .extent = bin_extent, .used_area = used_area}) + .unwrap(); bin++; total_used_area += used_area; @@ -412,8 +437,10 @@ inline std::pair> render_font_atlas(Font const usize const total_wasted_area = total_area - total_used_area; ASH_LOG_INFO(FontRenderer, - "Finished Bin Packing Glyphs For Font: {} Into {} Bins With {} Efficiency (Wasted Area = {}, Used Area = {}, Total Area = {}) ", - font.postscript_name.c_str(), bins.size(), packing_efficiency, total_wasted_area, total_used_area, total_area); + "Finished Bin Packing Glyphs For Font: {} Into {} Bins With {} Efficiency (Wasted " + "Area = {}, Used Area = {}, Total Area = {}) ", + font.postscript_name.c_str(), bins.size(), packing_efficiency, total_wasted_area, + total_used_area, total_area); stx::Vec bin_buffers; @@ -434,7 +461,8 @@ inline std::pair> render_font_atlas(Font const FT_Error ft_error = FT_Load_Glyph(ft_face, glyph_index, FT_LOAD_DEFAULT); if (ft_error != 0) [[unlikely]] { - ASH_LOG_ERR(FontRenderer, "Failed To Load Glyph At Index: {} For Font: {} (FT_Error = {})", glyph_index, font.postscript_name.c_str(), ft_error); + ASH_LOG_ERR(FontRenderer, "Failed To Load Glyph At Index: {} For Font: {} (FT_Error = {})", + glyph_index, font.postscript_name.c_str(), ft_error); continue; } @@ -442,7 +470,8 @@ inline std::pair> render_font_atlas(Font const ft_error = FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL); if (ft_error != 0) [[unlikely]] { - ASH_LOG_ERR(FontRenderer, "Failed To Render Glyph At Index: {} for font: {}", glyph_index, font.postscript_name.c_str(), ft_error); + ASH_LOG_ERR(FontRenderer, "Failed To Render Glyph At Index: {} for font: {}", glyph_index, + font.postscript_name.c_str(), ft_error); continue; } @@ -451,28 +480,29 @@ inline std::pair> render_font_atlas(Font const bin_buffers[glyph.bin] .view() .subview(glyph.bin_area) - .copy(ImageView{.span = stx::Span{slot->bitmap.buffer, slot->bitmap.rows * slot->bitmap.pitch}, - .extent = Extent{slot->bitmap.width, slot->bitmap.rows}, - .pitch = (usize) slot->bitmap.pitch, - .format = ImageFormat::R8}); + .copy(ImageView{ + .span = stx::Span{slot->bitmap.buffer, slot->bitmap.rows * slot->bitmap.pitch}, + .extent = Extent{slot->bitmap.width, slot->bitmap.rows}, + .pitch = (usize) slot->bitmap.pitch, + .format = ImageFormat::R8}); } } - ASH_LOG_INFO(FontRenderer, "Finished Caching SDF Atlas Bins For Font: {}", font.postscript_name.c_str()); + ASH_LOG_INFO(FontRenderer, "Finished Caching SDF Atlas Bins For Font: {}", + font.postscript_name.c_str()); ASH_CHECK(FT_Done_Face(ft_face) == 0); ASH_CHECK(FT_Done_FreeType(ft_lib) == 0); - return std::make_pair(FontAtlas{ - .glyphs = std::move(glyphs), - .replacement_glyph = replacement_glyph, - .space_glyph = space_glyph, - .ellipsis_glyph = ellipsis_glyph, - .font_height = spec.font_height, - .ascent = ascent, - .descent = descent, - .advance = advance, - .bins = std::move(bins)}, + return std::make_pair(FontAtlas{.glyphs = std::move(glyphs), + .replacement_glyph = replacement_glyph, + .space_glyph = space_glyph, + .ellipsis_glyph = ellipsis_glyph, + .font_height = spec.font_height, + .ascent = ascent, + .descent = descent, + .advance = advance, + .bins = std::move(bins)}, std::move(bin_buffers)); } @@ -494,7 +524,8 @@ inline usize match_font(std::string_view font, stx::Span font return i; } -inline usize match_font(std::string_view font, stx::Span fallback_fonts, stx::Span font_bundle) +inline usize match_font(std::string_view font, stx::Span fallback_fonts, + stx::Span font_bundle) { usize pos = match_font(font, font_bundle); if (pos < font_bundle.size()) diff --git a/ashura/include/ashura/gfx.h b/ashura/include/ashura/gfx.h index 704ce626e..1224b19d2 100644 --- a/ashura/include/ashura/gfx.h +++ b/ashura/include/ashura/gfx.h @@ -19,7 +19,7 @@ namespace gfx constexpr u32 REMAINING_MIP_LEVELS = ~0U; constexpr u32 REMAINING_ARRAY_LAYERS = ~0U; constexpr u64 WHOLE_SIZE = ~0ULL; -// TODO(lamarrr): we have to check these against usages and device info +// TODO(lamarrr): we have to check these against usage and device info constexpr u8 MAX_DESCRIPTOR_SET_BINDINGS = 16; constexpr u8 MAX_COLOR_ATTACHMENTS = 8; constexpr u8 MAX_VERTEX_ATTRIBUTES = 16; @@ -557,7 +557,7 @@ enum class PipelineStages : u32 STX_DEFINE_ENUM_BIT_OPS(PipelineStages) -enum class BufferUsages : u8 +enum class BufferUsage : u8 { None = 0x00000000, TransferSrc = 0x00000001, @@ -570,9 +570,9 @@ enum class BufferUsages : u8 VertexBuffer = 0x00000080 }; -STX_DEFINE_ENUM_BIT_OPS(BufferUsages) +STX_DEFINE_ENUM_BIT_OPS(BufferUsage) -enum class ImageUsages : u8 +enum class ImageUsage : u8 { None = 0x00000000, TransferSrc = 0x00000001, @@ -584,35 +584,75 @@ enum class ImageUsages : u8 InputAttachment = 0x00000080 }; -STX_DEFINE_ENUM_BIT_OPS(ImageUsages) +STX_DEFINE_ENUM_BIT_OPS(ImageUsage) -enum class BufferBindings : u16 +// USED TO DETERMINE HOW TO INSERT BARRIERS BEFORE AND AFTER MUTATING SCOPES +enum class BufferUsageScope : u32 { - None = 0x0000, - ComputeShaderUniform = 0x0001, - ComputeShaderStorage = 0x0002, - ComputeShaderUniformTexel = 0x0004, - ComputeShaderStorageTexel = 0x0008, - FragmentShaderUniform = 0x0010, - FragmentShaderUniformTexel = 0x0020, - VertexShaderUniform = 0x0040, - VertexShaderUniformTexel = 0x0080, - GraphicsIndexBuffer = 0x0100, - GraphicsVertexBuffer = 0x0200, + None = 0x00000000, + TransferSrc = 0x00000001, + TransferDst = 0x00000002, + ComputeShaderUniform = 0x00000004, + ComputeShaderStorage = 0x00000008, + ComputeShaderUniformTexel = 0x00000010, + ComputeShaderStorageTexel = 0x00000020, + IndexBuffer = 0x00000040, + VertexBuffer = 0x00000080, + VertexShaderUniform = 0x00000100, + FragmentShaderUniform = 0x00000200, + All = 0xFFFFFFFF }; -STX_DEFINE_ENUM_BIT_OPS(BufferBindings) +STX_DEFINE_ENUM_BIT_OPS(BufferUsageScope) -enum class ImageBindings : u8 +// BASE LAYOUT IS ALWAYS SHADER-READ-ONLY??? +// +// +// +// used for: -> base layout, layout transitions +// TransferSrc is implicitly supported? +// +// read from color attachment in compute shader +// write from color attachment in compute shader +// read but with transitions in compute shader +// +// tramsition from base layout to new layout storage|sampled +// transition back to base layout +// +// +// base layout is determined by relative frequency of operation +// +// +// +enum class ImageUsageScope : u32 { - None = 0x00, - ComputeSampled = 0x01, - ComputeStorage = 0x02, - GraphicsSampled = 0x01, - GraphicsInputAttachment = 0x04 + None = 0x00000000, + TransferSrc = 0x00000001, + TransferDst = 0x00000002, + InputAttachment = 0x00000004, + ReadColorAttachment = 0x00000008, + WriteColorAttachment = 0x00000010, + ReadDepthStencilAttachment = 0x00000020, + WriteDepthStencilAttachment = 0x00000040, + ComputeShaderSampled = 0x00000080, + ComputeShaderStorage = 0x00000100, + VertexShaderSampled = 0x00000200, + FragmentShaderSampled = 0x00000400, + Present = 0x00000800, + All = 0xFFFFFFFF }; -STX_DEFINE_ENUM_BIT_OPS(ImageBindings) +STX_DEFINE_ENUM_BIT_OPS(ImageUsageScope) + +constexpr PipelineStages stages_from_usage(BufferUsage usage) +{ +} + +constexpr PipelineStages stages_from_usage(ImageUsage usage) +{ +} + +// compute pipelines enum class InputRate : u8 { @@ -780,7 +820,8 @@ struct BufferDesc { u64 size = 0; MemoryProperties properties = MemoryProperties::None; - BufferUsages usages = BufferUsages::None; + BufferUsage usage = BufferUsage::None; + BufferUsageScope scope = BufferUsageScope::None; }; struct BufferViewDesc @@ -793,13 +834,14 @@ struct BufferViewDesc struct ImageDesc { - ImageType type = ImageType::Type1D; - Format format = Format::Undefined; - ImageUsages usages = ImageUsages::None; - ImageAspects aspects = ImageAspects::None; - Extent3D extent = {}; - u32 mips = 0; - u32 array_layers = 0; + ImageType type = ImageType::Type1D; + Format format = Format::Undefined; + ImageUsage usage = ImageUsage::None; + ImageUsageScope scope = ImageUsageScope::None; + ImageAspects aspects = ImageAspects::None; + Extent3D extent = {}; + u32 mips = 0; + u32 array_layers = 0; }; struct ImageViewDesc @@ -1233,194 +1275,6 @@ struct QueueImageMemoryBarrier Access dst_access = Access::None; }; -struct BufferAccess -{ - PipelineStages stages = PipelineStages::None; - MemoryAccess access = MemoryAccess::None; -}; - -struct ImageAccess -{ - ImageLayout layout = ImageLayout::Undefined; - PipelineStages stages = PipelineStages::None; - MemoryAccess access = MemoryAccess::None; -}; - -struct BufferSyncScope -{ - MemoryAccess access = MemoryAccess::None; - BufferBindings bindings = BufferBindings::None; - - constexpr void reset() - { - access = MemoryAccess::None; - bindings = BufferBindings::None; - } - - constexpr bool sync(MemoryAccess new_access, PipelineStages new_stages, - QueueBufferMemoryBarrier &barrier) - { - if (access == MemoryAccess::None) - { - access |= new_access; - return false; - } - - // RAR needs no barrier - if ((new_access == MemoryAccess::Read || new_access == MemoryAccess::None) && - (access == MemoryAccess::Read || access == MemoryAccess::None)) - { - access |= new_access; - return false; - } - - bool const is_non_mutating = new_access == MemoryAccess::Read; - - // WAR, RAW, WAW - barrier.src_access = (Access) (is_non_mutating ? MemoryAccess::Write : access); - barrier.src_stages = PipelineStages::BottomOfPipe; - barrier.dst_access = (Access) new_access; - barrier.dst_stages = new_stages; - barrier.offset = 0; - barrier.size = WHOLE_SIZE; - access |= new_access; - return true; - } -}; - -struct ImageSyncScope -{ - MemoryAccess access = MemoryAccess::None; - ImageLayout layout = ImageLayout::Undefined; - ImageBindings bindings = ImageBindings::None; - - constexpr void reset() - { - access = MemoryAccess::None; - bindings = ImageBindings::None; - } - - constexpr bool sync(MemoryAccess new_access, PipelineStages new_stages, ImageLayout new_layout, - QueueImageMemoryBarrier &barrier) - { - if (layout == new_layout && access == MemoryAccess::None) - { - access |= new_access; - return false; - } - - if (layout == new_layout && - (new_access == MemoryAccess::Read || new_access == MemoryAccess::None) && - (access == MemoryAccess::Read || access == MemoryAccess::None)) - { - access |= new_access; - return false; - } - - // RAW, WAR, WAW - bool const is_non_mutating = new_access == MemoryAccess::Read && layout == new_layout; - barrier.first_mip_level = 0; - barrier.num_mip_levels = REMAINING_MIP_LEVELS; - barrier.first_array_layer = 0; - barrier.num_array_layers = REMAINING_ARRAY_LAYERS; - barrier.old_layout = layout; - barrier.new_layout = new_layout; - barrier.src_access = (Access) (is_non_mutating ? MemoryAccess::Write : access); - barrier.src_stages = PipelineStages::BottomOfPipe; - barrier.dst_access = (Access) new_access; - barrier.dst_stages = new_stages; - access |= is_non_mutating ? MemoryAccess::Read : new_access; - layout = new_layout; - return true; - } - - BufferAccess to_pipeline_access(BufferBindings bindings, PipelineStages binding_stages) - { - Access access = Access::None; - PipelineStages stages = PipelineStages::None; - - if ((bindings & (BufferBindings::Uniform | BufferBindings::Storage | - BufferBindings::UniformTexel | BufferBindings::StorageTexel)) != - BufferBindings::None) - { - stages |= binding_stages; - } - - if ((bindings & (BufferBindings::IndexBuffer | BufferBindings::VertexBuffer)) != - BufferBindings::None) - { - stages |= PipelineStages::VertexInput; - } - - if ((bindings & BufferBindings::IndexBuffer) != BufferBindings::None) - { - access |= Access::IndexRead; - } - - if ((bindings & BufferBindings::VertexBuffer) != BufferBindings::None) - { - access |= Access::VertexAttributeRead; - } - - if ((bindings & (BufferBindings::Uniform | BufferBindings::UniformTexel)) != - BufferBindings::None) - { - access |= Access::ShaderRead; - } - - if ((bindings & (BufferBindings::Storage | BufferBindings::StorageTexel)) != - BufferBindings::None) - { - access |= Access::ShaderWrite; - } - - return BufferAccess{.stages = stages, .access = access}; - } - - ImageAccess to_pipeline_access(ImageBindings bindings, PipelineStages binding_stages) - { - Access access = Access::None; - ImageLayout layout = ImageLayout::Undefined; - PipelineStages stages = PipelineStages::None; - - if ((bindings & ImageBindings::InputAttachment) != ImageBindings::None) - { - access |= Access::InputAttachmentRead; - } - - if ((bindings & ImageBindings::Sampled) != ImageBindings::None) - { - access |= Access::ShaderRead; - } - - if ((bindings & ImageBindings::Storage) != ImageBindings::None) - { - access |= Access::ShaderWrite; - } - - if ((bindings & ImageBindings::Storage) != ImageBindings::None) - { - layout = ImageLayout::General; - } - else if ((bindings & (ImageBindings::Sampled | ImageBindings::InputAttachment)) != - ImageBindings::None) - { - layout = ImageLayout::ShaderReadOnlyOptimal; - } - - if (bindings == ImageBindings::InputAttachment) - { - stages = PipelineStages::FragmentShader; - } - else if (bindings != ImageBindings::None) - { - stages = binding_stages; - } - - return ImageAccess{.stages = stages, .access = access, .layout = layout}; - } -}; - struct StencilFaceState { u32 compare_mask = 0; @@ -1441,9 +1295,8 @@ struct RenderState struct BufferResource { - BufferDesc desc; - BufferSyncScope sync_scope; - Buffer handle = nullptr; + BufferDesc desc; + Buffer handle = nullptr; }; struct BufferViewResource @@ -1454,9 +1307,8 @@ struct BufferViewResource struct ImageResource { - ImageDesc desc; - ImageSyncScope sync_scope; - Image handle = nullptr; + ImageDesc desc; + Image handle = nullptr; }; struct ImageViewResource diff --git a/ashura/include/ashura/image.h b/ashura/include/ashura/image.h index 22ef0e97e..40e848063 100644 --- a/ashura/include/ashura/image.h +++ b/ashura/include/ashura/image.h @@ -87,7 +87,8 @@ struct ImageView operator ImageView() const { - return ImageView{.span = span.as_const(), .extent = extent, .pitch = pitch, .format = format}; + return ImageView{ + .span = span.as_const(), .extent = extent, .pitch = pitch, .format = format}; } ImageView subview(URect slice) const @@ -99,14 +100,23 @@ struct ImageView usize const pixel_bytes = pixel_byte_size(format); usize const byte_offset = slice.offset.y * pitch + slice.offset.x * pixel_bytes; - usize const byte_span = slice.extent.height > 0 ? (slice.extent.width * pixel_bytes + (slice.extent.height - 1U) * pitch) : 0; + usize const byte_span = + slice.extent.height > 0 ? + (slice.extent.width * pixel_bytes + (slice.extent.height - 1U) * pitch) : + 0; - return ImageView{.span = span.slice(byte_offset, byte_span), .extent = slice.extent, .pitch = pitch, .format = format}; + return ImageView{.span = span.slice(byte_offset, byte_span), + .extent = slice.extent, + .pitch = pitch, + .format = format}; } ImageView subview(Offset slice) const { - return subview(URect{.offset = slice, .extent = ash::Extent{.width = extent.width - std::min(extent.width, slice.x), .height = extent.height - std::min(extent.height, slice.y)}}); + return subview( + URect{.offset = slice, + .extent = ash::Extent{.width = extent.width - std::min(extent.width, slice.x), + .height = extent.height - std::min(extent.height, slice.y)}}); } ImageView as_const() const @@ -185,7 +195,8 @@ struct ImageBuffer operator ImageView() const { - return ImageView{.span = span(), .extent = extent, .pitch = pitch(), .format = format}; + return ImageView{ + .span = span(), .extent = extent, .pitch = pitch(), .format = format}; } operator ImageView() diff --git a/ashura/include/ashura/image_decoder.h b/ashura/include/ashura/image_decoder.h index b04de65bd..043c9ac27 100644 --- a/ashura/include/ashura/image_decoder.h +++ b/ashura/include/ashura/image_decoder.h @@ -38,26 +38,34 @@ inline stx::Result decode_webp(stx::Span return stx::Err(ImageLoadError::InvalidData); } - stx::Memory memory = stx::mem::allocate(stx::os_allocator, AS(usize, features.width * features.height * features.has_alpha ? 4 : 3)).unwrap(); + stx::Memory memory = + stx::mem::allocate(stx::os_allocator, + AS(usize, features.width * features.height * features.has_alpha ? 4 : 3)) + .unwrap(); u8 *pixels = AS(u8 *, memory.handle); if (features.has_alpha) { - if (WebPDecodeRGBAInto(data.data(), data.size(), pixels, features.width * features.height * 4, features.width * 4) == nullptr) + if (WebPDecodeRGBAInto(data.data(), data.size(), pixels, features.width * features.height * 4, + features.width * 4) == nullptr) { return stx::Err(ImageLoadError::InvalidData); } } else { - if (WebPDecodeRGBInto(data.data(), data.size(), pixels, features.width * features.height * 3, features.width * 3) == nullptr) + if (WebPDecodeRGBInto(data.data(), data.size(), pixels, features.width * features.height * 3, + features.width * 3) == nullptr) { return stx::Err(ImageLoadError::InvalidData); } } - return stx::Ok(ImageBuffer{.memory = std::move(memory), .extent = Extent{AS(u32, features.width), AS(u32, features.height)}, .format = features.has_alpha ? ImageFormat::Rgba8888 : ImageFormat::Rgb888}); + return stx::Ok( + ImageBuffer{.memory = std::move(memory), + .extent = Extent{AS(u32, features.width), AS(u32, features.height)}, + .format = features.has_alpha ? ImageFormat::Rgba8888 : ImageFormat::Rgb888}); } inline void png_stream_reader(png_structp png_ptr, unsigned char *out, usize nbytes_to_read) @@ -91,7 +99,8 @@ inline stx::Result decode_png(stx::Span d int color_type; int bit_depth; - u32 status = png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, nullptr, nullptr, nullptr); + u32 status = png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, nullptr, + nullptr, nullptr); if (status != 1) { @@ -117,7 +126,8 @@ inline stx::Result decode_png(stx::Span d return stx::Err(ImageLoadError::UnsupportedChannels); } - stx::Memory pixels_mem = stx::mem::allocate(stx::os_allocator, width * height * ncomponents).unwrap(); + stx::Memory pixels_mem = + stx::mem::allocate(stx::os_allocator, width * height * ncomponents).unwrap(); u8 *pixels = AS(u8 *, pixels_mem.handle); @@ -129,7 +139,8 @@ inline stx::Result decode_png(stx::Span d png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); - return stx::Ok(ImageBuffer{.memory = std::move(pixels_mem), .extent = Extent{width, height}, .format = fmt}); + return stx::Ok( + ImageBuffer{.memory = std::move(pixels_mem), .extent = Extent{width, height}, .format = fmt}); } inline stx::Result decode_jpg(stx::Span bytes) @@ -172,7 +183,8 @@ inline stx::Result decode_jpg(stx::Span b return stx::Err(ImageLoadError::UnsupportedChannels); } - stx::Memory pixels_mem = stx::mem::allocate(stx::os_allocator, height * width * ncomponents).unwrap(); + stx::Memory pixels_mem = + stx::mem::allocate(stx::os_allocator, height * width * ncomponents).unwrap(); u8 *pixels = AS(u8 *, pixels_mem.handle); @@ -185,7 +197,8 @@ inline stx::Result decode_jpg(stx::Span b jpeg_finish_decompress(&info); jpeg_destroy_decompress(&info); - return stx::Ok(ImageBuffer{.memory = std::move(pixels_mem), .extent = Extent{width, height}, .format = fmt}); + return stx::Ok( + ImageBuffer{.memory = std::move(pixels_mem), .extent = Extent{width, height}, .format = fmt}); } inline stx::Result decode_image(stx::Span bytes) diff --git a/ashura/include/ashura/logger.h b/ashura/include/ashura/logger.h index 8df94a0f8..e5b71e5c8 100644 --- a/ashura/include/ashura/logger.h +++ b/ashura/include/ashura/logger.h @@ -12,14 +12,16 @@ #define ASH_GET_LOGGER(identifier) get_logger_##identifier() // TODO(lamarrr): use log path specified in config? -#define ASH_DEFINE_LOGGER(identifier) \ - ::spdlog::logger *get_logger_##identifier() \ - { \ - static ::spdlog::sink_ptr __sinks[] = {::std::make_shared<::spdlog::sinks::basic_file_sink_mt>("log.txt"), \ - ::std::make_shared<::spdlog::sinks::stdout_color_sink_mt>()}; \ - \ - static ::spdlog::logger __logger{#identifier, __sinks, __sinks + sizeof(__sinks) / sizeof(::spdlog::sink_ptr)}; \ - return &__logger; \ +#define ASH_DEFINE_LOGGER(identifier) \ + ::spdlog::logger *get_logger_##identifier() \ + { \ + static ::spdlog::sink_ptr __sinks[] = { \ + ::std::make_shared<::spdlog::sinks::basic_file_sink_mt>("log.txt"), \ + ::std::make_shared<::spdlog::sinks::stdout_color_sink_mt>()}; \ + \ + static ::spdlog::logger __logger{#identifier, __sinks, \ + __sinks + sizeof(__sinks) / sizeof(::spdlog::sink_ptr)}; \ + return &__logger; \ } #define ASH_LOG_TRACE(logger, ...) ASH_GET_LOGGER(logger)->trace(__VA_ARGS__) diff --git a/ashura/include/ashura/pass.h b/ashura/include/ashura/pass.h index 1a77e691b..559a3e015 100644 --- a/ashura/include/ashura/pass.h +++ b/ashura/include/ashura/pass.h @@ -13,7 +13,8 @@ namespace lgfx { struct Graph -{}; +{ +}; struct PassContext { }; @@ -25,7 +26,7 @@ struct CommandBuffer; // TODO(lamarrr): what if we need to destroy an index buffer because of multibuffering? // new pass doesn't begin until the previous pass' buffer is done with // -// +// // // @@ -74,8 +75,8 @@ struct ScreenPass struct Resources { - Image color_images[16]; // screen has implicit pass to present the screen_color_image - Image depth_stencil_images[16]; + Image color_images[16]; // screen has implicit pass to present the screen_color_image + Image depth_stencil_images[16]; RenderPass render_passes[16]; Framebuffer framebuffers[16]; } resources; diff --git a/ashura/include/ashura/pipeline.h b/ashura/include/ashura/pipeline.h index 496d407c6..43b9b79d6 100644 --- a/ashura/include/ashura/pipeline.h +++ b/ashura/include/ashura/pipeline.h @@ -11,7 +11,7 @@ using pipeline = u32; }; // namespace gfx constexpr std::string_view DEFAULT_SHAPE_PIPELINE = "BuiltinPipeline:Shape2D"; -constexpr std::string_view DEFAULT_GLYPH_PIPELINE = "BuiltinPipeline:Glyph2D"; +constexpr std::string_view DEFAULT_GLYPH_PIPELINE = "BuiltinPipeline:Glyph2D"; struct CanvasPipelineSpec { diff --git a/ashura/include/ashura/primitives.h b/ashura/include/ashura/primitives.h index 0a10fa883..1d42956da 100644 --- a/ashura/include/ashura/primitives.h +++ b/ashura/include/ashura/primitives.h @@ -301,7 +301,8 @@ struct Quad constexpr bool contains(Vec2 point) const { - return tri{.p0 = p0, .p1 = p1, .p2 = p2}.contains(point) || tri{.p0 = p0, .p1 = p2, .p2 = p3}.contains(point); + return tri{.p0 = p0, .p1 = p1, .p2 = p2}.contains(point) || + tri{.p0 = p0, .p1 = p2, .p2 = p3}.contains(point); } }; @@ -358,7 +359,8 @@ struct Rect constexpr bool contains(Vec2 point) const { - return offset.x <= point.x && offset.y <= point.y && (offset.x + extent.x) >= point.x && (offset.y + extent.y) >= point.y; + return offset.x <= point.x && offset.y <= point.y && (offset.x + extent.x) >= point.x && + (offset.y + extent.y) >= point.y; } constexpr bool is_visible() const @@ -388,11 +390,7 @@ struct Rect constexpr Quad to_quad() const { - return Quad{ - .p0 = top_left(), - .p1 = top_right(), - .p2 = bottom_right(), - .p3 = bottom_left()}; + return Quad{.p0 = top_left(), .p1 = top_right(), .p2 = bottom_right(), .p3 = bottom_left()}; } constexpr Rect with_offset(Vec2 new_offset) const @@ -645,14 +643,12 @@ struct Mat2 static constexpr Mat2 identity() { - return Mat2{.rows = {{.x = 1, .y = 0}, - {.x = 0, .y = 1}}}; + return Mat2{.rows = {{.x = 1, .y = 0}, {.x = 0, .y = 1}}}; } constexpr Mat2 transpose() const { - return Mat2{.rows = {{rows[0].x, rows[1].x}, - {rows[0].y, rows[1].y}}}; + return Mat2{.rows = {{rows[0].x, rows[1].x}, {rows[0].y, rows[1].y}}}; } constexpr Vec2 &operator[](usize i) @@ -668,14 +664,12 @@ struct Mat2 constexpr Mat2 operator*(Mat2 a, f32 b) { - return Mat2{.rows = {a[0] * b, - a[1] * b}}; + return Mat2{.rows = {a[0] * b, a[1] * b}}; } constexpr Mat2 operator*(f32 a, Mat2 b) { - return Mat2{.rows = {a * b[0], - a * b[1]}}; + return Mat2{.rows = {a * b[0], a * b[1]}}; } constexpr f32 determinant(Mat2 a) @@ -685,8 +679,7 @@ constexpr f32 determinant(Mat2 a) constexpr Mat2 adjoint(Mat2 a) { - return Mat2{.rows = {{a[1].y, -a[0].y}, - {-a[1].x, a[0].x}}}; + return Mat2{.rows = {{a[1].y, -a[0].y}, {-a[1].x, a[0].x}}}; } constexpr Mat2 inverse(Mat2 a) @@ -701,9 +694,8 @@ struct Mat3 static constexpr Mat3 identity() { - return Mat3{.rows = {{.x = 1, .y = 0, .z = 0}, - {.x = 0, .y = 1, .z = 0}, - {.x = 0, .y = 0, .z = 1}}}; + return Mat3{ + .rows = {{.x = 1, .y = 0, .z = 0}, {.x = 0, .y = 1, .z = 0}, {.x = 0, .y = 0, .z = 1}}}; } constexpr Mat3 transpose() const @@ -726,16 +718,12 @@ struct Mat3 constexpr Mat3 operator*(Mat3 a, f32 b) { - return Mat3{.rows = {a[0] * b, - a[1] * b, - a[2] * b}}; + return Mat3{.rows = {a[0] * b, a[1] * b, a[2] * b}}; } constexpr Mat3 operator*(f32 a, Mat3 b) { - return Mat3{.rows = {a * b[0], - a * b[1], - a * b[2]}}; + return Mat3{.rows = {a * b[0], a * b[1], a * b[2]}}; } constexpr Vec3 operator*(Mat3 const &a, Vec3 const &b) @@ -745,37 +733,27 @@ constexpr Vec3 operator*(Mat3 const &a, Vec3 const &b) constexpr Mat3 operator*(Mat3 const &a, Mat3 const &b) { - return Mat3{.rows = {{dot(a[0], {b[0].x, b[1].x, b[2].x}), - dot(a[0], {b[0].y, b[1].y, b[2].y}), + return Mat3{.rows = {{dot(a[0], {b[0].x, b[1].x, b[2].x}), dot(a[0], {b[0].y, b[1].y, b[2].y}), dot(a[0], {b[0].z, b[1].z, b[2].z})}, - {dot(a[1], {b[0].x, b[1].x, b[2].x}), - dot(a[1], {b[0].y, b[1].y, b[2].y}), + {dot(a[1], {b[0].x, b[1].x, b[2].x}), dot(a[1], {b[0].y, b[1].y, b[2].y}), dot(a[1], {b[0].z, b[1].z, b[2].z})}, - {dot(a[2], {b[0].x, b[1].x, b[2].x}), - dot(a[2], {b[0].y, b[1].y, b[2].y}), + {dot(a[2], {b[0].x, b[1].x, b[2].x}), dot(a[2], {b[0].y, b[1].y, b[2].y}), dot(a[2], {b[0].z, b[1].z, b[2].z})}}}; } constexpr f32 determinant(Mat3 const &a) { - return a[0].x * a[1].y * a[2].z - - a[0].x * a[1].z * a[2].y - - a[0].y * a[1].x * a[2].z + - a[0].y * a[1].z * a[2].x + - a[0].z * a[1].x * a[2].y - - a[0].z * a[1].y * a[2].x; + return a[0].x * a[1].y * a[2].z - a[0].x * a[1].z * a[2].y - a[0].y * a[1].x * a[2].z + + a[0].y * a[1].z * a[2].x + a[0].z * a[1].x * a[2].y - a[0].z * a[1].y * a[2].x; } constexpr Mat3 adjoint(Mat3 const &a) { - return Mat3{.rows = {{a[1].y * a[2].z - a[1].z * a[2].y, - a[0].z * a[2].y - a[0].y * a[2].z, + return Mat3{.rows = {{a[1].y * a[2].z - a[1].z * a[2].y, a[0].z * a[2].y - a[0].y * a[2].z, a[0].y * a[1].z - a[0].z * a[1].y}, - {a[1].z * a[2].x - a[1].x * a[2].z, - a[0].x * a[2].z - a[0].z * a[2].x, + {a[1].z * a[2].x - a[1].x * a[2].z, a[0].x * a[2].z - a[0].z * a[2].x, a[0].z * a[1].x - a[0].x * a[1].z}, - {a[1].x * a[2].y - a[1].y * a[2].x, - a[0].y * a[2].x - a[0].x * a[2].y, + {a[1].x * a[2].y - a[1].y * a[2].x, a[0].y * a[2].x - a[0].x * a[2].y, a[0].x * a[1].y - a[0].y * a[1].x}}}; } @@ -818,18 +796,12 @@ struct Mat4 constexpr Mat4 operator*(Mat4 a, f32 b) { - return Mat4{.rows = {a[0] * b, - a[1] * b, - a[2] * b, - a[3] * b}}; + return Mat4{.rows = {a[0] * b, a[1] * b, a[2] * b, a[3] * b}}; } constexpr Mat4 operator*(f32 a, Mat4 b) { - return Mat4{.rows = {a * b[0], - a * b[1], - a * b[2], - a * b[3]}}; + return Mat4{.rows = {a * b[0], a * b[1], a * b[2], a * b[3]}}; } constexpr bool operator==(Mat4 const &a, Mat4 const &b) @@ -844,22 +816,20 @@ constexpr bool operator!=(Mat4 const &a, Mat4 const &b) constexpr Mat4 operator*(Mat4 const &a, Mat4 const &b) { - return Mat4{.rows = {{dot(a[0], {b[0].x, b[1].x, b[2].x, b[3].x}), - dot(a[0], {b[0].y, b[1].y, b[2].y, b[3].y}), - dot(a[0], {b[0].z, b[1].z, b[2].z, b[3].z}), - dot(a[0], {b[0].w, b[1].w, b[2].w, b[3].w})}, - {dot(a[1], {b[0].x, b[1].x, b[2].x, b[3].x}), - dot(a[1], {b[0].y, b[1].y, b[2].y, b[3].y}), - dot(a[1], {b[0].z, b[1].z, b[2].z, b[3].z}), - dot(a[1], {b[0].w, b[1].w, b[2].w, b[3].w})}, - {dot(a[2], {b[0].x, b[1].x, b[2].x, b[3].x}), - dot(a[2], {b[0].y, b[1].y, b[2].y, b[3].y}), - dot(a[2], {b[0].z, b[1].z, b[2].z, b[3].z}), - dot(a[2], {b[0].w, b[1].w, b[2].w, b[3].w})}, - {dot(a[3], {b[0].x, b[1].x, b[2].x, b[3].x}), - dot(a[3], {b[0].y, b[1].y, b[2].y, b[3].y}), - dot(a[3], {b[0].z, b[1].z, b[2].z, b[3].z}), - dot(a[3], {b[0].w, b[1].w, b[2].w, b[3].w})}}}; + return Mat4{ + .rows = { + {dot(a[0], {b[0].x, b[1].x, b[2].x, b[3].x}), dot(a[0], {b[0].y, b[1].y, b[2].y, b[3].y}), + dot(a[0], {b[0].z, b[1].z, b[2].z, b[3].z}), + dot(a[0], {b[0].w, b[1].w, b[2].w, b[3].w})}, + {dot(a[1], {b[0].x, b[1].x, b[2].x, b[3].x}), dot(a[1], {b[0].y, b[1].y, b[2].y, b[3].y}), + dot(a[1], {b[0].z, b[1].z, b[2].z, b[3].z}), + dot(a[1], {b[0].w, b[1].w, b[2].w, b[3].w})}, + {dot(a[2], {b[0].x, b[1].x, b[2].x, b[3].x}), dot(a[2], {b[0].y, b[1].y, b[2].y, b[3].y}), + dot(a[2], {b[0].z, b[1].z, b[2].z, b[3].z}), + dot(a[2], {b[0].w, b[1].w, b[2].w, b[3].w})}, + {dot(a[3], {b[0].x, b[1].x, b[2].x, b[3].x}), dot(a[3], {b[0].y, b[1].y, b[2].y, b[3].y}), + dot(a[3], {b[0].z, b[1].z, b[2].z, b[3].z}), + dot(a[3], {b[0].w, b[1].w, b[2].w, b[3].w})}}}; } constexpr Vec4 operator*(Mat4 const &a, Vec4 const &b) @@ -869,30 +839,14 @@ constexpr Vec4 operator*(Mat4 const &a, Vec4 const &b) constexpr f32 determinant(Mat4 const &a) { - return a[0].x * (a[1].y * a[2].z * a[3].w + - a[1].z * a[2].w * a[3].y + - a[1].w * a[2].y * a[3].z - - a[1].w * a[2].z * a[3].y - - a[1].z * a[2].y * a[3].w - - a[1].y * a[2].w * a[3].z) - - a[1].x * (a[0].y * a[2].z * a[3].w + - a[0].z * a[2].w * a[3].y + - a[0].w * a[2].y * a[3].z - - a[0].w * a[2].z * a[3].y - - a[0].z * a[2].y * a[3].w - - a[0].y * a[2].w * a[3].z) + - a[2].x * (a[0].y * a[1].z * a[3].w + - a[0].z * a[1].w * a[3].y + - a[0].w * a[1].y * a[3].z - - a[0].w * a[1].z * a[3].y - - a[0].z * a[1].y * a[3].w - - a[0].y * a[1].w * a[3].z) - - a[3].x * (a[0].y * a[1].z * a[2].w + - a[0].z * a[1].w * a[2].y + - a[0].w * a[1].y * a[2].z - - a[0].w * a[1].z * a[2].y - - a[0].z * a[1].y * a[2].w - - a[0].y * a[1].w * a[2].z); + return a[0].x * (a[1].y * a[2].z * a[3].w + a[1].z * a[2].w * a[3].y + a[1].w * a[2].y * a[3].z - + a[1].w * a[2].z * a[3].y - a[1].z * a[2].y * a[3].w - a[1].y * a[2].w * a[3].z) - + a[1].x * (a[0].y * a[2].z * a[3].w + a[0].z * a[2].w * a[3].y + a[0].w * a[2].y * a[3].z - + a[0].w * a[2].z * a[3].y - a[0].z * a[2].y * a[3].w - a[0].y * a[2].w * a[3].z) + + a[2].x * (a[0].y * a[1].z * a[3].w + a[0].z * a[1].w * a[3].y + a[0].w * a[1].y * a[3].z - + a[0].w * a[1].z * a[3].y - a[0].z * a[1].y * a[3].w - a[0].y * a[1].w * a[3].z) - + a[3].x * (a[0].y * a[1].z * a[2].w + a[0].z * a[1].w * a[2].y + a[0].w * a[1].y * a[2].z - + a[0].w * a[1].z * a[2].y - a[0].z * a[1].y * a[2].w - a[0].y * a[1].w * a[2].z); } // constexpr mat4 adjoint(mat4 const &a) @@ -952,18 +906,15 @@ constexpr Vec2 transform2d(Mat3 const &a, Vec2 const &b) constexpr Quad transform2d(Mat3 const &a, Rect const &b) { - return Quad{ - .p0 = transform2d(a, b.top_left()), - .p1 = transform2d(a, b.top_right()), - .p2 = transform2d(a, b.bottom_right()), - .p3 = transform2d(a, b.bottom_left())}; + return Quad{.p0 = transform2d(a, b.top_left()), + .p1 = transform2d(a, b.top_right()), + .p2 = transform2d(a, b.bottom_right()), + .p3 = transform2d(a, b.bottom_left())}; } constexpr Mat3 translate2d(Vec2 t) { - return Mat3{.rows = {{1, 0, t.x}, - {0, 1, t.y}, - {0, 0, 1}}}; + return Mat3{.rows = {{1, 0, t.x}, {0, 1, t.y}, {0, 0, 1}}}; } constexpr Mat3 translate2d(f32 tx, f32 ty) @@ -973,17 +924,12 @@ constexpr Mat3 translate2d(f32 tx, f32 ty) constexpr Mat4 translate3d(Vec3 t) { - return Mat4{.rows = {{1, 0, 0, t.x}, - {0, 1, 0, t.y}, - {0, 0, 1, t.z}, - {0, 0, 0, 1}}}; + return Mat4{.rows = {{1, 0, 0, t.x}, {0, 1, 0, t.y}, {0, 0, 1, t.z}, {0, 0, 0, 1}}}; } constexpr Mat3 scale2d(Vec2 s) { - return Mat3{.rows = {{s.x, 0, 0}, - {0, s.y, 0}, - {0, 0, 1}}}; + return Mat3{.rows = {{s.x, 0, 0}, {0, s.y, 0}, {0, 0, 1}}}; } constexpr Mat3 scale2d(f32 sx, f32 sy) @@ -993,10 +939,7 @@ constexpr Mat3 scale2d(f32 sx, f32 sy) constexpr Mat4 scale3d(Vec3 s) { - return Mat4{.rows = {{s.x, 0, 0, 0}, - {0, s.y, 0, 0}, - {0, 0, s.z, 0}, - {0, 0, 0, 1}}}; + return Mat4{.rows = {{s.x, 0, 0, 0}, {0, s.y, 0, 0}, {0, 0, s.z, 0}, {0, 0, 0, 1}}}; } inline Mat3 rotate2d(f32 degree_radians) @@ -1032,40 +975,27 @@ inline Mat4 rotate3d_z(f32 degree_radians) constexpr Mat3 shear2d_x(f32 x_shear) { - return Mat3{.rows = {{1, 0, 0}, - {x_shear, 1, 0}, - {0, 0, 1}}}; + return Mat3{.rows = {{1, 0, 0}, {x_shear, 1, 0}, {0, 0, 1}}}; } constexpr Mat3 shear2d_y(f32 y_shear) { - return Mat3{.rows = {{1, y_shear, 0}, - {0, 1, 0}, - {0, 0, 1}}}; + return Mat3{.rows = {{1, y_shear, 0}, {0, 1, 0}, {0, 0, 1}}}; } constexpr Mat4 shear3d_x(f32 y_shear, f32 z_shear) { - return Mat4{.rows = {{1, y_shear, z_shear, 0}, - {0, 1, 0, 0}, - {0, 0, 1, 0}, - {0, 0, 0, 1}}}; + return Mat4{.rows = {{1, y_shear, z_shear, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}}; } constexpr Mat4 shear3d_y(f32 x_shear, f32 z_shear) { - return Mat4{.rows = {{1, 0, 0, 0}, - {x_shear, 1, z_shear, 0}, - {0, 0, 1, 0}, - {0, 0, 0, 1}}}; + return Mat4{.rows = {{1, 0, 0, 0}, {x_shear, 1, z_shear, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}}; } constexpr Mat4 shear3d_z(f32 x_shear, f32 y_shear) { - return Mat4{.rows = {{1, 0, 0, 0}, - {0, 1, 0, 0}, - {x_shear, y_shear, 1, 0}, - {0, 0, 0, 1}}}; + return Mat4{.rows = {{1, 0, 0, 0}, {0, 1, 0, 0}, {x_shear, y_shear, 1, 0}, {0, 0, 0, 1}}}; } struct Quaternion @@ -1215,7 +1145,8 @@ struct Extent3D constexpr Extent3D at_mip_level(u32 mip_level) const { - return Extent3D{.width = width >> mip_level, .height = height >> mip_level, .depth = depth >> mip_level}; + return Extent3D{ + .width = width >> mip_level, .height = height >> mip_level, .depth = depth >> mip_level}; } }; @@ -1303,12 +1234,15 @@ struct IRect /// Simple Layout Constraint Model struct Constraint { - f32 bias = 0; /// adding or subtracting from the source size, i.e. value should be source size - 20px - f32 scale = 0; /// scales the source size, i.e. value should be 0.5 of source size + f32 bias = + 0; /// adding or subtracting from the source size, i.e. value should be source size - 20px + f32 scale = 0; /// scales the source size, i.e. value should be 0.5 of source size f32 min = stx::F32_MIN; /// clamps the source size, i.e. value should be at least 20px f32 max = stx::F32_MAX; /// clamps the source size, i.e. value should be at most 100px - f32 minr = 0; /// clamps the source size relatively. i.e. value should be at least 0.5 of source size - f32 maxr = 1; /// clamps the source size relatively. i.e. value should be at most 0.5 of source size + f32 minr = + 0; /// clamps the source size relatively. i.e. value should be at least 0.5 of source size + f32 maxr = + 1; /// clamps the source size relatively. i.e. value should be at most 0.5 of source size static constexpr Constraint relative(f32 scale) { @@ -1322,22 +1256,26 @@ struct Constraint constexpr Constraint with_min(f32 v) const { - return Constraint{.bias = bias, .scale = scale, .min = v, .max = max, .minr = minr, .maxr = maxr}; + return Constraint{ + .bias = bias, .scale = scale, .min = v, .max = max, .minr = minr, .maxr = maxr}; } constexpr Constraint with_max(f32 v) const { - return Constraint{.bias = bias, .scale = scale, .min = min, .max = v, .minr = minr, .maxr = maxr}; + return Constraint{ + .bias = bias, .scale = scale, .min = min, .max = v, .minr = minr, .maxr = maxr}; } constexpr Constraint with_minr(f32 v) const { - return Constraint{.bias = bias, .scale = scale, .min = min, .max = max, .minr = v, .maxr = maxr}; + return Constraint{ + .bias = bias, .scale = scale, .min = min, .max = max, .minr = v, .maxr = maxr}; } constexpr Constraint with_maxr(f32 v) const { - return Constraint{.bias = bias, .scale = scale, .min = min, .max = max, .minr = minr, .maxr = v}; + return Constraint{ + .bias = bias, .scale = scale, .min = min, .max = max, .minr = minr, .maxr = v}; } constexpr f32 resolve(f32 value) const diff --git a/ashura/include/ashura/rhi.h b/ashura/include/ashura/rhi.h index 9bf5f8a54..497020cd5 100644 --- a/ashura/include/ashura/rhi.h +++ b/ashura/include/ashura/rhi.h @@ -139,13 +139,13 @@ struct Driver stx::Span bindings, stx::Span push_constants_data) = 0; virtual void cmd_draw(gfx::CommandBuffer command_buffer, gfx::GraphicsPipeline pipeline, - gfx::RenderState const &state, - stx::Span vertex_buffers, gfx::Buffer index_buffer, - u32 first_index, u32 num_indices, u32 vertex_offset, u32 first_instance, - u32 num_instances, stx::Span bindings, - stx::Span push_constants_data) = 0; + gfx::RenderState const &state, stx::Span vertex_buffers, + gfx::Buffer index_buffer, u32 first_index, u32 num_indices, + u32 vertex_offset, u32 first_instance, u32 num_instances, + stx::Span bindings, + stx::Span push_constants_data) = 0; virtual void cmd_draw_indirect(gfx::CommandBuffer command_buffer, gfx::GraphicsPipeline pipeline, - gfx::RenderState const &state, + gfx::RenderState const &state, stx::Span vertex_buffers, gfx::Buffer index_buffer, gfx::Buffer buffer, u64 offset, u32 draw_count, u32 stride, diff --git a/ashura/include/ashura/sdf.h b/ashura/include/ashura/sdf.h index 5a0b913cf..76197930a 100644 --- a/ashura/include/ashura/sdf.h +++ b/ashura/include/ashura/sdf.h @@ -14,7 +14,9 @@ namespace ash /// output_height = height + sdf_spread * 2 /// /// -inline void generate_sdf_from_mono(u8 const *const src, u32 const src_pitch, u32 const width, u32 const height, u32 const sdf_spread, u8 *const output, u32 const output_pitch) +inline void generate_sdf_from_mono(u8 const *const src, u32 const src_pitch, u32 const width, + u32 const height, u32 const sdf_spread, u8 *const output, + u32 const output_pitch) { for (i64 i = 0; i < height + sdf_spread * 2; i++) { @@ -33,15 +35,19 @@ inline void generate_sdf_from_mono(u8 const *const src, u32 const src_pitch, u32 // the squared distance to the nearest neigbor that has a different position along the shape i64 square_distance = sdf_spread * sdf_spread; - for (i64 ifield = std::max(isrc - sdf_spread, (i64) 0); ifield < std::min(isrc + sdf_spread + 1, (i64) height); ifield++) + for (i64 ifield = std::max(isrc - sdf_spread, (i64) 0); + ifield < std::min(isrc + sdf_spread + 1, (i64) height); ifield++) { - for (i64 jfield = std::max(jsrc - sdf_spread, (i64) 0); jfield < std::min(jsrc + sdf_spread + 1, (i64) width); jfield++) + for (i64 jfield = std::max(jsrc - sdf_spread, (i64) 0); + jfield < std::min(jsrc + sdf_spread + 1, (i64) width); jfield++) { - u8 const neighbor_is_inside = (src[ifield * src_pitch + (jfield / 8)] >> (7 - (jfield % 8))) & 1U; + u8 const neighbor_is_inside = + (src[ifield * src_pitch + (jfield / 8)] >> (7 - (jfield % 8))) & 1U; if (neighbor_is_inside != is_inside) [[likely]] { - i64 const neighbor_square_distance = (ifield - isrc) * (ifield - isrc) + (jfield - jsrc) * (jfield - jsrc); - square_distance = std::min(square_distance, neighbor_square_distance); + i64 const neighbor_square_distance = + (ifield - isrc) * (ifield - isrc) + (jfield - jsrc) * (jfield - jsrc); + square_distance = std::min(square_distance, neighbor_square_distance); } } } diff --git a/ashura/include/ashura/sdl_utils.h b/ashura/include/ashura/sdl_utils.h index 99f12e654..ca57c935c 100644 --- a/ashura/include/ashura/sdl_utils.h +++ b/ashura/include/ashura/sdl_utils.h @@ -3,9 +3,10 @@ #include "SDL.h" #include "stx/panic.h" -#define ASH_SDL_CHECK(expr, ...) \ - do \ - { \ - if (!(expr)) \ - ::stx::panic(#expr " failed. " #__VA_ARGS__ " SDL Error:", ::std::string_view(::SDL_GetError())); \ +#define ASH_SDL_CHECK(expr, ...) \ + do \ + { \ + if (!(expr)) \ + ::stx::panic(#expr " failed. " #__VA_ARGS__ " SDL Error:", \ + ::std::string_view(::SDL_GetError())); \ } while (false) diff --git a/ashura/include/ashura/stb_image_resize.h b/ashura/include/ashura/stb_image_resize.h index c8a256741..831aa5663 100644 --- a/ashura/include/ashura/stb_image_resize.h +++ b/ashura/include/ashura/stb_image_resize.h @@ -182,30 +182,30 @@ */ #ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H -#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H +# define STBIR_INCLUDE_STB_IMAGE_RESIZE_H -#ifdef _MSC_VER +# ifdef _MSC_VER typedef unsigned char stbir_uint8; typedef unsigned short stbir_uint16; typedef unsigned int stbir_uint32; -#else -#include +# else +# include typedef uint8_t stbir_uint8; typedef uint16_t stbir_uint16; typedef uint32_t stbir_uint32; -#endif - -#ifndef STBIRDEF -#ifdef STB_IMAGE_RESIZE_STATIC -#define STBIRDEF static -#else -#ifdef __cplusplus -#define STBIRDEF extern "C" -#else -#define STBIRDEF extern -#endif -#endif -#endif +# endif + +# ifndef STBIRDEF +# ifdef STB_IMAGE_RESIZE_STATIC +# define STBIRDEF static +# else +# ifdef __cplusplus +# define STBIRDEF extern "C" +# else +# define STBIRDEF extern +# endif +# endif +# endif ////////////////////////////////////////////////////////////////////////////// // @@ -225,14 +225,14 @@ typedef uint32_t stbir_uint32; // you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE // and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API. -STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); - -STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); +STBIRDEF int stbir_resize_uint8(const unsigned char *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, unsigned char *output_pixels, + int output_w, int output_h, int output_stride_in_bytes, + int num_channels); +STBIRDEF int stbir_resize_float(const float *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, float *output_pixels, int output_w, + int output_h, int output_stride_in_bytes, int num_channels); // The following functions interpret image data as gamma-corrected sRGB. // Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, @@ -240,32 +240,34 @@ STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , i // of 0 will probably do the right thing if you're not sure what // the flags mean. -#define STBIR_ALPHA_CHANNEL_NONE -1 +# define STBIR_ALPHA_CHANNEL_NONE -1 // Set this flag if your texture has premultiplied alpha. Otherwise, stbir will // use alpha-weighted resampling (effectively premultiplying, resampling, // then unpremultiplying). -#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) +# define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) // The specified alpha channel should be handled as gamma-corrected value even // when doing sRGB operations. -#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) +# define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) -STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, unsigned char *output_pixels, + int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags); - typedef enum { - STBIR_EDGE_CLAMP = 1, - STBIR_EDGE_REFLECT = 2, - STBIR_EDGE_WRAP = 3, - STBIR_EDGE_ZERO = 4, + STBIR_EDGE_CLAMP = 1, + STBIR_EDGE_REFLECT = 2, + STBIR_EDGE_WRAP = 3, + STBIR_EDGE_ZERO = 4, } stbir_edge; // This function adds the ability to specify how requests to sample off the edge of the image are handled. -STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels, int input_w, + int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, + int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode); @@ -286,43 +288,47 @@ STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels typedef enum { - STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses - STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios - STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering - STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque - STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline - STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses + STBIR_FILTER_BOX = + 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios + STBIR_FILTER_TRIANGLE = + 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBICBSPLINE = + 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque + STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 } stbir_filter; typedef enum { - STBIR_COLORSPACE_LINEAR, - STBIR_COLORSPACE_SRGB, + STBIR_COLORSPACE_LINEAR, + STBIR_COLORSPACE_SRGB, - STBIR_MAX_COLORSPACES, + STBIR_MAX_COLORSPACES, } stbir_colorspace; // The following functions are all identical except for the type of the image data -STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); +STBIRDEF int stbir_resize_uint8_generic(const unsigned char *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, unsigned char *output_pixels, + int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, + stbir_colorspace space, void *alloc_context); -STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, stbir_uint16 *output_pixels, + int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - -STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - + stbir_edge edge_wrap_mode, stbir_filter filter, + stbir_colorspace space, void *alloc_context); +STBIRDEF int stbir_resize_float_generic(const float *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, float *output_pixels, + int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, + stbir_colorspace space, void *alloc_context); ////////////////////////////////////////////////////////////////////////////// // @@ -339,321 +345,320 @@ STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int typedef enum { - STBIR_TYPE_UINT8 , - STBIR_TYPE_UINT16, - STBIR_TYPE_UINT32, - STBIR_TYPE_FLOAT , + STBIR_TYPE_UINT8, + STBIR_TYPE_UINT16, + STBIR_TYPE_UINT32, + STBIR_TYPE_FLOAT, - STBIR_MAX_TYPES + STBIR_MAX_TYPES } stbir_datatype; -STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context); - -STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float x_scale, float y_scale, +STBIRDEF int stbir_resize(const void *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, void *output_pixels, int output_w, + int output_h, int output_stride_in_bytes, stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context); + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, void *output_pixels, int output_w, + int output_h, int output_stride_in_bytes, + stbir_datatype datatype, int num_channels, int alpha_channel, + int flags, stbir_edge edge_mode_horizontal, + stbir_edge edge_mode_vertical, stbir_filter filter_horizontal, + stbir_filter filter_vertical, stbir_colorspace space, + void *alloc_context, float x_scale, float y_scale, float x_offset, float y_offset); -STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float s0, float t0, float s1, float t1); +STBIRDEF int stbir_resize_region(const void *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, void *output_pixels, int output_w, + int output_h, int output_stride_in_bytes, stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, float s0, float t0, + float s1, float t1); // (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use. // // //// end header file ///////////////////////////////////////////////////// -#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H - - - - +#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H #ifdef STB_IMAGE_RESIZE_IMPLEMENTATION -#ifndef STBIR_ASSERT -#include -#define STBIR_ASSERT(x) assert(x) -#endif +# ifndef STBIR_ASSERT +# include +# define STBIR_ASSERT(x) assert(x) +# endif // For memset -#include +# include -#include +# include -#ifndef STBIR_MALLOC -#include +# ifndef STBIR_MALLOC +# include // use comma operator to evaluate c, to avoid "unused parameter" warnings -#define STBIR_MALLOC(size,c) ((void)(c), malloc(size)) -#define STBIR_FREE(ptr,c) ((void)(c), free(ptr)) -#endif - -#ifndef _MSC_VER -#ifdef __cplusplus -#define stbir__inline inline -#else -#define stbir__inline -#endif -#else -#define stbir__inline __forceinline -#endif - +# define STBIR_MALLOC(size, c) ((void) (c), malloc(size)) +# define STBIR_FREE(ptr, c) ((void) (c), free(ptr)) +# endif + +# ifndef _MSC_VER +# ifdef __cplusplus +# define stbir__inline inline +# else +# define stbir__inline +# endif +# else +# define stbir__inline __forceinline +# endif // should produce compiler error if size is wrong typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; -#ifdef _MSC_VER -#define STBIR__NOTUSED(v) (void)(v) -#else -#define STBIR__NOTUSED(v) (void)sizeof(v) -#endif +# ifdef _MSC_VER +# define STBIR__NOTUSED(v) (void) (v) +# else +# define STBIR__NOTUSED(v) (void) sizeof(v) +# endif -#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) +# define STBIR__ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) -#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE -#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM -#endif +# ifndef STBIR_DEFAULT_FILTER_UPSAMPLE +# define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM +# endif -#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE -#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL -#endif +# ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE +# define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL +# endif -#ifndef STBIR_PROGRESS_REPORT -#define STBIR_PROGRESS_REPORT(float_0_to_1) -#endif +# ifndef STBIR_PROGRESS_REPORT +# define STBIR_PROGRESS_REPORT(float_0_to_1) +# endif -#ifndef STBIR_MAX_CHANNELS -#define STBIR_MAX_CHANNELS 64 -#endif +# ifndef STBIR_MAX_CHANNELS +# define STBIR_MAX_CHANNELS 64 +# endif -#if STBIR_MAX_CHANNELS > 65536 -#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." +# if STBIR_MAX_CHANNELS > 65536 +# error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." // because we store the indices in 16-bit variables -#endif +# endif // This value is added to alpha just before premultiplication to avoid // zeroing out color values. It is equivalent to 2^-80. If you don't want // that behavior (it may interfere if you have floating point images with // very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to // disable it. -#ifndef STBIR_ALPHA_EPSILON -#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) -#endif - - +# ifndef STBIR_ALPHA_EPSILON +# define STBIR_ALPHA_EPSILON ((float) 1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) +# endif -#ifdef _MSC_VER -#define STBIR__UNUSED_PARAM(v) (void)(v) -#else -#define STBIR__UNUSED_PARAM(v) (void)sizeof(v) -#endif +# ifdef _MSC_VER +# define STBIR__UNUSED_PARAM(v) (void) (v) +# else +# define STBIR__UNUSED_PARAM(v) (void) sizeof(v) +# endif // must match stbir_datatype static unsigned char stbir__type_size[] = { - 1, // STBIR_TYPE_UINT8 - 2, // STBIR_TYPE_UINT16 - 4, // STBIR_TYPE_UINT32 - 4, // STBIR_TYPE_FLOAT + 1, // STBIR_TYPE_UINT8 + 2, // STBIR_TYPE_UINT16 + 4, // STBIR_TYPE_UINT32 + 4, // STBIR_TYPE_FLOAT }; // Kernel function centered at 0 -typedef float (stbir__kernel_fn)(float x, float scale); -typedef float (stbir__support_fn)(float scale); +typedef float(stbir__kernel_fn)(float x, float scale); +typedef float(stbir__support_fn)(float scale); typedef struct { - stbir__kernel_fn* kernel; - stbir__support_fn* support; + stbir__kernel_fn *kernel; + stbir__support_fn *support; } stbir__filter_info; // When upsampling, the contributors are which source pixels contribute. // When downsampling, the contributors are which destination pixels are contributed to. typedef struct { - int n0; // First contributing pixel - int n1; // Last contributing pixel + int n0; // First contributing pixel + int n1; // Last contributing pixel } stbir__contributors; typedef struct { - const void* input_data; - int input_w; - int input_h; - int input_stride_bytes; - - void* output_data; - int output_w; - int output_h; - int output_stride_bytes; - - float s0, t0, s1, t1; - - float horizontal_shift; // Units: output pixels - float vertical_shift; // Units: output pixels - float horizontal_scale; - float vertical_scale; - - int channels; - int alpha_channel; - stbir_uint32 flags; - stbir_datatype type; - stbir_filter horizontal_filter; - stbir_filter vertical_filter; - stbir_edge edge_horizontal; - stbir_edge edge_vertical; - stbir_colorspace colorspace; - - stbir__contributors* horizontal_contributors; - float* horizontal_coefficients; - - stbir__contributors* vertical_contributors; - float* vertical_coefficients; - - int decode_buffer_pixels; - float* decode_buffer; - - float* horizontal_buffer; - - // cache these because ceil/floor are inexplicably showing up in profile - int horizontal_coefficient_width; - int vertical_coefficient_width; - int horizontal_filter_pixel_width; - int vertical_filter_pixel_width; - int horizontal_filter_pixel_margin; - int vertical_filter_pixel_margin; - int horizontal_num_contributors; - int vertical_num_contributors; - - int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) - int ring_buffer_num_entries; // Total number of entries in the ring buffer. - int ring_buffer_first_scanline; - int ring_buffer_last_scanline; - int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer - float* ring_buffer; - - float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. - - int horizontal_contributors_size; - int horizontal_coefficients_size; - int vertical_contributors_size; - int vertical_coefficients_size; - int decode_buffer_size; - int horizontal_buffer_size; - int ring_buffer_size; - int encode_buffer_size; + const void *input_data; + int input_w; + int input_h; + int input_stride_bytes; + + void *output_data; + int output_w; + int output_h; + int output_stride_bytes; + + float s0, t0, s1, t1; + + float horizontal_shift; // Units: output pixels + float vertical_shift; // Units: output pixels + float horizontal_scale; + float vertical_scale; + + int channels; + int alpha_channel; + stbir_uint32 flags; + stbir_datatype type; + stbir_filter horizontal_filter; + stbir_filter vertical_filter; + stbir_edge edge_horizontal; + stbir_edge edge_vertical; + stbir_colorspace colorspace; + + stbir__contributors *horizontal_contributors; + float *horizontal_coefficients; + + stbir__contributors *vertical_contributors; + float *vertical_coefficients; + + int decode_buffer_pixels; + float *decode_buffer; + + float *horizontal_buffer; + + // cache these because ceil/floor are inexplicably showing up in profile + int horizontal_coefficient_width; + int vertical_coefficient_width; + int horizontal_filter_pixel_width; + int vertical_filter_pixel_width; + int horizontal_filter_pixel_margin; + int vertical_filter_pixel_margin; + int horizontal_num_contributors; + int vertical_num_contributors; + + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) + int ring_buffer_num_entries; // Total number of entries in the ring buffer. + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer + float *ring_buffer; + + float * + encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. + + int horizontal_contributors_size; + int horizontal_coefficients_size; + int vertical_contributors_size; + int vertical_coefficients_size; + int decode_buffer_size; + int horizontal_buffer_size; + int ring_buffer_size; + int encode_buffer_size; } stbir__info; - -static const float stbir__max_uint8_as_float = 255.0f; -static const float stbir__max_uint16_as_float = 65535.0f; +static const float stbir__max_uint8_as_float = 255.0f; +static const float stbir__max_uint16_as_float = 65535.0f; static const double stbir__max_uint32_as_float = 4294967295.0; - static stbir__inline int stbir__min(int a, int b) { - return a < b ? a : b; + return a < b ? a : b; } static stbir__inline float stbir__saturate(float x) { - if (x < 0) - return 0; + if (x < 0) + return 0; - if (x > 1) - return 1; + if (x > 1) + return 1; - return x; + return x; } -#ifdef STBIR_SATURATE_INT +# ifdef STBIR_SATURATE_INT static stbir__inline stbir_uint8 stbir__saturate8(int x) { - if ((unsigned int) x <= 255) - return x; + if ((unsigned int) x <= 255) + return x; - if (x < 0) - return 0; + if (x < 0) + return 0; - return 255; + return 255; } static stbir__inline stbir_uint16 stbir__saturate16(int x) { - if ((unsigned int) x <= 65535) - return x; + if ((unsigned int) x <= 65535) + return x; - if (x < 0) - return 0; + if (x < 0) + return 0; - return 65535; + return 65535; } -#endif +# endif static float stbir__srgb_uchar_to_linear_float[256] = { - 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, - 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, - 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, - 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, - 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, - 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, - 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, - 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, - 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, - 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, - 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, - 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, - 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, - 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, - 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, - 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, - 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, - 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, - 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, - 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, - 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, - 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, - 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, - 0.982251f, 0.991102f, 1.0f -}; + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, + 0.002428f, 0.002732f, 0.003035f, 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, + 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, 0.008023f, 0.008568f, + 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, + 0.014444f, 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, + 0.021219f, 0.022174f, 0.023153f, 0.024158f, 0.025187f, 0.026241f, 0.027321f, 0.028426f, + 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, 0.038204f, + 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, + 0.051269f, 0.052861f, 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, + 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, 0.074214f, 0.076185f, 0.078187f, + 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, + 0.116971f, 0.119538f, 0.122139f, 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, + 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, 0.155926f, 0.158961f, + 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, + 0.187821f, 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, + 0.215861f, 0.219526f, 0.223228f, 0.226966f, 0.230740f, 0.234551f, 0.238398f, 0.242281f, + 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, 0.274677f, + 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, + 0.313989f, 0.318547f, 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, + 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, 0.376262f, 0.381326f, 0.386430f, + 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, + 0.479320f, 0.485150f, 0.491021f, 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, + 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, 0.564712f, 0.571125f, + 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, + 0.630757f, 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, + 0.686685f, 0.693872f, 0.701102f, 0.708376f, 0.715694f, 0.723055f, 0.730461f, 0.737911f, + 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, 0.799103f, + 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, + 0.871367f, 0.879622f, 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, + 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, 0.982251f, 0.991102f, 1.0f}; static float stbir__srgb_to_linear(float f) { - if (f <= 0.04045f) - return f / 12.92f; - else - return (float)pow((f + 0.055f) / 1.055f, 2.4f); + if (f <= 0.04045f) + return f / 12.92f; + else + return (float) pow((f + 0.055f) / 1.055f, 2.4f); } static float stbir__linear_to_srgb(float f) { - if (f <= 0.0031308f) - return f * 12.92f; - else - return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; + if (f <= 0.0031308f) + return f * 12.92f; + else + return 1.055f * (float) pow(f, 1 / 2.4f) - 0.055f; } -#ifndef STBIR_NON_IEEE_FLOAT +# ifndef STBIR_NON_IEEE_FLOAT // From https://gist.github.com/rygorous/2203834 typedef union { - stbir_uint32 u; - float f; + stbir_uint32 u; + float f; } stbir__FP32; static const stbir_uint32 fp32_to_srgb8_tab4[104] = { @@ -674,55 +679,54 @@ static const stbir_uint32 fp32_to_srgb8_tab4[104] = { static stbir_uint8 stbir__linear_to_srgb_uchar(float in) { - static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps - static const stbir__FP32 minval = { (127-13) << 23 }; - stbir_uint32 tab,bias,scale,t; - stbir__FP32 f; + static const stbir__FP32 almostone = {0x3f7fffff}; // 1-eps + static const stbir__FP32 minval = {(127 - 13) << 23}; + stbir_uint32 tab, bias, scale, t; + stbir__FP32 f; - // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. - // The tests are carefully written so that NaNs map to 0, same as in the reference - // implementation. - if (!(in > minval.f)) // written this way to catch NaNs - in = minval.f; - if (in > almostone.f) - in = almostone.f; + // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. + // The tests are carefully written so that NaNs map to 0, same as in the reference + // implementation. + if (!(in > minval.f)) // written this way to catch NaNs + in = minval.f; + if (in > almostone.f) + in = almostone.f; - // Do the table lookup and unpack bias, scale - f.f = in; - tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; - bias = (tab >> 16) << 9; - scale = tab & 0xffff; + // Do the table lookup and unpack bias, scale + f.f = in; + tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; + bias = (tab >> 16) << 9; + scale = tab & 0xffff; - // Grab next-highest mantissa bits and perform linear interpolation - t = (f.u >> 12) & 0xff; - return (unsigned char) ((bias + scale*t) >> 16); + // Grab next-highest mantissa bits and perform linear interpolation + t = (f.u >> 12) & 0xff; + return (unsigned char) ((bias + scale * t) >> 16); } -#else +# else // sRGB transition values, scaled by 1<<28 -static int stbir__srgb_offset_to_linear_scaled[256] = -{ - 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, - 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, - 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, - 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, - 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, - 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, - 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, - 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, - 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, - 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, - 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, - 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, - 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, - 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, - 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, - 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, - 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, - 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, - 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, - 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, - 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, +static int stbir__srgb_offset_to_linear_scaled[256] = { + 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, + 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, + 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, + 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, + 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, + 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, + 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, + 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, + 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, + 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, + 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, + 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, + 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, + 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, + 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, + 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, + 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, + 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, + 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, + 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, + 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544, 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832, 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528, @@ -738,1858 +742,2062 @@ static int stbir__srgb_offset_to_linear_scaled[256] = static stbir_uint8 stbir__linear_to_srgb_uchar(float f) { - int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp - int v = 0; - int i; - - // Refine the guess with a short binary search. - i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - - return (stbir_uint8) v; -} -#endif + int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp + int v = 0; + int i; + + // Refine the guess with a short binary search. + i = v + 128; + if (x >= stbir__srgb_offset_to_linear_scaled[i]) + v = i; + i = v + 64; + if (x >= stbir__srgb_offset_to_linear_scaled[i]) + v = i; + i = v + 32; + if (x >= stbir__srgb_offset_to_linear_scaled[i]) + v = i; + i = v + 16; + if (x >= stbir__srgb_offset_to_linear_scaled[i]) + v = i; + i = v + 8; + if (x >= stbir__srgb_offset_to_linear_scaled[i]) + v = i; + i = v + 4; + if (x >= stbir__srgb_offset_to_linear_scaled[i]) + v = i; + i = v + 2; + if (x >= stbir__srgb_offset_to_linear_scaled[i]) + v = i; + i = v + 1; + if (x >= stbir__srgb_offset_to_linear_scaled[i]) + v = i; + + return (stbir_uint8) v; +} +# endif static float stbir__filter_trapezoid(float x, float scale) { - float halfscale = scale / 2; - float t = 0.5f + halfscale; - STBIR_ASSERT(scale <= 1); + float halfscale = scale / 2; + float t = 0.5f + halfscale; + STBIR_ASSERT(scale <= 1); - x = (float)fabs(x); + x = (float) fabs(x); - if (x >= t) - return 0; + if (x >= t) + return 0; + else + { + float r = 0.5f - halfscale; + if (x <= r) + return 1; else - { - float r = 0.5f - halfscale; - if (x <= r) - return 1; - else - return (t - x) / scale; - } + return (t - x) / scale; + } } static float stbir__support_trapezoid(float scale) { - STBIR_ASSERT(scale <= 1); - return 0.5f + scale / 2; + STBIR_ASSERT(scale <= 1); + return 0.5f + scale / 2; } static float stbir__filter_triangle(float x, float s) { - STBIR__UNUSED_PARAM(s); + STBIR__UNUSED_PARAM(s); - x = (float)fabs(x); + x = (float) fabs(x); - if (x <= 1.0f) - return 1 - x; - else - return 0; + if (x <= 1.0f) + return 1 - x; + else + return 0; } static float stbir__filter_cubic(float x, float s) { - STBIR__UNUSED_PARAM(s); + STBIR__UNUSED_PARAM(s); - x = (float)fabs(x); + x = (float) fabs(x); - if (x < 1.0f) - return (4 + x*x*(3*x - 6))/6; - else if (x < 2.0f) - return (8 + x*(-12 + x*(6 - x)))/6; + if (x < 1.0f) + return (4 + x * x * (3 * x - 6)) / 6; + else if (x < 2.0f) + return (8 + x * (-12 + x * (6 - x))) / 6; - return (0.0f); + return (0.0f); } static float stbir__filter_catmullrom(float x, float s) { - STBIR__UNUSED_PARAM(s); + STBIR__UNUSED_PARAM(s); - x = (float)fabs(x); + x = (float) fabs(x); - if (x < 1.0f) - return 1 - x*x*(2.5f - 1.5f*x); - else if (x < 2.0f) - return 2 - x*(4 + x*(0.5f*x - 2.5f)); + if (x < 1.0f) + return 1 - x * x * (2.5f - 1.5f * x); + else if (x < 2.0f) + return 2 - x * (4 + x * (0.5f * x - 2.5f)); - return (0.0f); + return (0.0f); } static float stbir__filter_mitchell(float x, float s) { - STBIR__UNUSED_PARAM(s); + STBIR__UNUSED_PARAM(s); - x = (float)fabs(x); + x = (float) fabs(x); - if (x < 1.0f) - return (16 + x*x*(21 * x - 36))/18; - else if (x < 2.0f) - return (32 + x*(-60 + x*(36 - 7*x)))/18; + if (x < 1.0f) + return (16 + x * x * (21 * x - 36)) / 18; + else if (x < 2.0f) + return (32 + x * (-60 + x * (36 - 7 * x))) / 18; - return (0.0f); + return (0.0f); } static float stbir__support_zero(float s) { - STBIR__UNUSED_PARAM(s); - return 0; + STBIR__UNUSED_PARAM(s); + return 0; } static float stbir__support_one(float s) { - STBIR__UNUSED_PARAM(s); - return 1; + STBIR__UNUSED_PARAM(s); + return 1; } static float stbir__support_two(float s) { - STBIR__UNUSED_PARAM(s); - return 2; + STBIR__UNUSED_PARAM(s); + return 2; } static stbir__filter_info stbir__filter_info_table[] = { - { NULL, stbir__support_zero }, - { stbir__filter_trapezoid, stbir__support_trapezoid }, - { stbir__filter_triangle, stbir__support_one }, - { stbir__filter_cubic, stbir__support_two }, - { stbir__filter_catmullrom, stbir__support_two }, - { stbir__filter_mitchell, stbir__support_two }, + {NULL, stbir__support_zero}, + {stbir__filter_trapezoid, stbir__support_trapezoid}, + {stbir__filter_triangle, stbir__support_one}, + {stbir__filter_cubic, stbir__support_two}, + {stbir__filter_catmullrom, stbir__support_two}, + {stbir__filter_mitchell, stbir__support_two}, }; stbir__inline static int stbir__use_upsampling(float ratio) { - return ratio > 1; + return ratio > 1; } -stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) +stbir__inline static int stbir__use_width_upsampling(stbir__info *stbir_info) { - return stbir__use_upsampling(stbir_info->horizontal_scale); + return stbir__use_upsampling(stbir_info->horizontal_scale); } -stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) +stbir__inline static int stbir__use_height_upsampling(stbir__info *stbir_info) { - return stbir__use_upsampling(stbir_info->vertical_scale); + return stbir__use_upsampling(stbir_info->vertical_scale); } // This is the maximum number of input samples that can affect an output sample // with the given filter static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) { - STBIR_ASSERT(filter != 0); - STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(filter != 0); + STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - if (stbir__use_upsampling(scale)) - return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); - else - return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); + if (stbir__use_upsampling(scale)) + return (int) ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); + else + return (int) ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); } // This is how much to expand buffers to account for filters seeking outside // the image boundaries. static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) { - return stbir__get_filter_pixel_width(filter, scale) / 2; + return stbir__get_filter_pixel_width(filter, scale) / 2; } static int stbir__get_coefficient_width(stbir_filter filter, float scale) { - if (stbir__use_upsampling(scale)) - return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); - else - return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); + if (stbir__use_upsampling(scale)) + return (int) ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); + else + return (int) ceil(stbir__filter_info_table[filter].support(scale) * 2); } -static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) +static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, + int output_size) { - if (stbir__use_upsampling(scale)) - return output_size; - else - return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); + if (stbir__use_upsampling(scale)) + return output_size; + else + return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); } -static int stbir__get_total_horizontal_coefficients(stbir__info* info) +static int stbir__get_total_horizontal_coefficients(stbir__info *info) { - return info->horizontal_num_contributors - * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); + return info->horizontal_num_contributors * + stbir__get_coefficient_width(info->horizontal_filter, info->horizontal_scale); } -static int stbir__get_total_vertical_coefficients(stbir__info* info) +static int stbir__get_total_vertical_coefficients(stbir__info *info) { - return info->vertical_num_contributors - * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); + return info->vertical_num_contributors * + stbir__get_coefficient_width(info->vertical_filter, info->vertical_scale); } -static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) +static stbir__contributors *stbir__get_contributor(stbir__contributors *contributors, int n) { - return &contributors[n]; + return &contributors[n]; } // For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, // if you change it here change it there too. -static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) +static float *stbir__get_coefficient(float *coefficients, stbir_filter filter, float scale, int n, + int c) { - int width = stbir__get_coefficient_width(filter, scale); - return &coefficients[width*n + c]; + int width = stbir__get_coefficient_width(filter, scale); + return &coefficients[width * n + c]; } static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) { - switch (edge) - { + switch (edge) + { case STBIR_EDGE_ZERO: - return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later + return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later case STBIR_EDGE_CLAMP: - if (n < 0) - return 0; + if (n < 0) + return 0; - if (n >= max) - return max - 1; + if (n >= max) + return max - 1; - return n; // NOTREACHED + return n; // NOTREACHED case STBIR_EDGE_REFLECT: { - if (n < 0) - { - if (n < max) - return -n; - else - return max - 1; - } - - if (n >= max) - { - int max2 = max * 2; - if (n >= max2) - return 0; - else - return max2 - n - 1; - } + if (n < 0) + { + if (n < max) + return -n; + else + return max - 1; + } + + if (n >= max) + { + int max2 = max * 2; + if (n >= max2) + return 0; + else + return max2 - n - 1; + } - return n; // NOTREACHED + return n; // NOTREACHED } case STBIR_EDGE_WRAP: - if (n >= 0) - return (n % max); - else - { - int m = (-n) % max; + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; - if (m != 0) - m = max - m; + if (m != 0) + m = max - m; - return (m); - } - // NOTREACHED + return (m); + } + // NOTREACHED default: - STBIR_ASSERT(!"Unimplemented edge type"); - return 0; - } + STBIR_ASSERT(!"Unimplemented edge type"); + return 0; + } } stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) { - // avoid per-pixel switch - if (n >= 0 && n < max) - return n; - return stbir__edge_wrap_slow(edge, n, max); + // avoid per-pixel switch + if (n >= 0 && n < max) + return n; + return stbir__edge_wrap_slow(edge, n, max); } // What input pixels contribute to this output pixel? -static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) +static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, + float scale_ratio, float out_shift, + int *in_first_pixel, int *in_last_pixel, + float *in_center_of_out) { - float out_pixel_center = (float)n + 0.5f; - float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; - float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; + float out_pixel_center = (float) n + 0.5f; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; - float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; - float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; - *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; - *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); - *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); + *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; + *in_first_pixel = (int) (floor(in_pixel_influence_lowerbound + 0.5)); + *in_last_pixel = (int) (floor(in_pixel_influence_upperbound - 0.5)); } // What output pixels does this input pixel contribute to? -static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) +static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, + float scale_ratio, float out_shift, + int *out_first_pixel, int *out_last_pixel, + float *out_center_of_in) { - float in_pixel_center = (float)n + 0.5f; - float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; - float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; + float in_pixel_center = (float) n + 0.5f; + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; - float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; - float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; - *out_center_of_in = in_pixel_center * scale_ratio - out_shift; - *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); - *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); + *out_center_of_in = in_pixel_center * scale_ratio - out_shift; + *out_first_pixel = (int) (floor(out_pixel_influence_lowerbound + 0.5)); + *out_last_pixel = (int) (floor(out_pixel_influence_upperbound - 0.5)); } -static void stbir__calculate_coefficients_upsample(stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) +static void stbir__calculate_coefficients_upsample(stbir_filter filter, float scale, + int in_first_pixel, int in_last_pixel, + float in_center_of_out, + stbir__contributors *contributor, + float *coefficient_group) { - int i; - float total_filter = 0; - float filter_scale; - - STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + int i; + float total_filter = 0; + float filter_scale; - contributor->n0 = in_first_pixel; - contributor->n1 = in_last_pixel; + STBIR_ASSERT( + in_last_pixel - in_first_pixel <= + (int) ceil( + stbir__filter_info_table[filter].support(1 / scale) * + 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. - STBIR_ASSERT(contributor->n1 >= contributor->n0); + contributor->n0 = in_first_pixel; + contributor->n1 = in_last_pixel; - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - { - float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; - coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); + STBIR_ASSERT(contributor->n1 >= contributor->n0); - // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) - if (i == 0 && !coefficient_group[i]) - { - contributor->n0 = ++in_first_pixel; - i--; - continue; - } + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + { + float in_pixel_center = (float) (i + in_first_pixel) + 0.5f; + coefficient_group[i] = + stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); - total_filter += coefficient_group[i]; + // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) + if (i == 0 && !coefficient_group[i]) + { + contributor->n0 = ++in_first_pixel; + i--; + continue; } - // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. - // It would be true in exact math but is at best approximately true in floating-point math, - // and it would not make sense to try and put actual bounds on this here because it depends - // on the image aspect ratio which can get pretty extreme. - //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); + total_filter += coefficient_group[i]; + } - STBIR_ASSERT(total_filter > 0.9); - STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. + // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. + // It would be true in exact math but is at best approximately true in floating-point math, + // and it would not make sense to try and put actual bounds on this here because it depends + // on the image aspect ratio which can get pretty extreme. + //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); - // Make sure the sum of all coefficients is 1. - filter_scale = 1 / total_filter; + STBIR_ASSERT(total_filter > 0.9); + STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - coefficient_group[i] *= filter_scale; + // Make sure the sum of all coefficients is 1. + filter_scale = 1 / total_filter; - for (i = in_last_pixel - in_first_pixel; i >= 0; i--) - { - if (coefficient_group[i]) - break; + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + coefficient_group[i] *= filter_scale; - // This line has no weight. We can skip it. - contributor->n1 = contributor->n0 + i - 1; - } + for (i = in_last_pixel - in_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; + + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } } -static void stbir__calculate_coefficients_downsample(stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) +static void stbir__calculate_coefficients_downsample(stbir_filter filter, float scale_ratio, + int out_first_pixel, int out_last_pixel, + float out_center_of_in, + stbir__contributors *contributor, + float *coefficient_group) { - int i; + int i; - STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. + STBIR_ASSERT( + out_last_pixel - out_first_pixel <= + (int) ceil( + stbir__filter_info_table[filter].support(scale_ratio) * + 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. - contributor->n0 = out_first_pixel; - contributor->n1 = out_last_pixel; + contributor->n0 = out_first_pixel; + contributor->n1 = out_last_pixel; - STBIR_ASSERT(contributor->n1 >= contributor->n0); + STBIR_ASSERT(contributor->n1 >= contributor->n0); - for (i = 0; i <= out_last_pixel - out_first_pixel; i++) - { - float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; - float x = out_pixel_center - out_center_of_in; - coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; - } + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) + { + float out_pixel_center = (float) (i + out_first_pixel) + 0.5f; + float x = out_pixel_center - out_center_of_in; + coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; + } - // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. - // It would be true in exact math but is at best approximately true in floating-point math, - // and it would not make sense to try and put actual bounds on this here because it depends - // on the image aspect ratio which can get pretty extreme. - //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); + // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. + // It would be true in exact math but is at best approximately true in floating-point math, + // and it would not make sense to try and put actual bounds on this here because it depends + // on the image aspect ratio which can get pretty extreme. + //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); - for (i = out_last_pixel - out_first_pixel; i >= 0; i--) - { - if (coefficient_group[i]) - break; + for (i = out_last_pixel - out_first_pixel; i >= 0; i--) + { + if (coefficient_group[i]) + break; - // This line has no weight. We can skip it. - contributor->n1 = contributor->n0 + i - 1; - } + // This line has no weight. We can skip it. + contributor->n1 = contributor->n0 + i - 1; + } } -static void stbir__normalize_downsample_coefficients(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, int input_size, int output_size) +static void stbir__normalize_downsample_coefficients(stbir__contributors *contributors, + float *coefficients, stbir_filter filter, + float scale_ratio, int input_size, + int output_size) { - int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); - int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); - int i, j; - int skip; + int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); + int i, j; + int skip; - for (i = 0; i < output_size; i++) - { - float scale; - float total = 0; + for (i = 0; i < output_size; i++) + { + float scale; + float total = 0; - for (j = 0; j < num_contributors; j++) - { - if (i >= contributors[j].n0 && i <= contributors[j].n1) - { - float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); - total += coefficient; - } - else if (i < contributors[j].n0) - break; - } - - STBIR_ASSERT(total > 0.9f); - STBIR_ASSERT(total < 1.1f); + for (j = 0; j < num_contributors; j++) + { + if (i >= contributors[j].n0 && i <= contributors[j].n1) + { + float coefficient = + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); + total += coefficient; + } + else if (i < contributors[j].n0) + break; + } - scale = 1 / total; + STBIR_ASSERT(total > 0.9f); + STBIR_ASSERT(total < 1.1f); - for (j = 0; j < num_contributors; j++) - { - if (i >= contributors[j].n0 && i <= contributors[j].n1) - *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; - else if (i < contributors[j].n0) - break; - } - } + scale = 1 / total; - // Optimize: Skip zero coefficients and contributions outside of image bounds. - // Do this after normalizing because normalization depends on the n0/n1 values. for (j = 0; j < num_contributors; j++) { - int range, max, width; + if (i >= contributors[j].n0 && i <= contributors[j].n1) + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= + scale; + else if (i < contributors[j].n0) + break; + } + } - skip = 0; - while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) - skip++; + // Optimize: Skip zero coefficients and contributions outside of image bounds. + // Do this after normalizing because normalization depends on the n0/n1 values. + for (j = 0; j < num_contributors; j++) + { + int range, max, width; - contributors[j].n0 += skip; + skip = 0; + while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) + skip++; - while (contributors[j].n0 < 0) - { - contributors[j].n0++; - skip++; - } + contributors[j].n0 += skip; - range = contributors[j].n1 - contributors[j].n0 + 1; - max = stbir__min(num_coefficients, range); + while (contributors[j].n0 < 0) + { + contributors[j].n0++; + skip++; + } - width = stbir__get_coefficient_width(filter, scale_ratio); - for (i = 0; i < max; i++) - { - if (i + skip >= width) - break; + range = contributors[j].n1 - contributors[j].n0 + 1; + max = stbir__min(num_coefficients, range); - *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); - } + width = stbir__get_coefficient_width(filter, scale_ratio); + for (i = 0; i < max; i++) + { + if (i + skip >= width) + break; - continue; + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = + *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); } - // Using min to avoid writing into invalid pixels. - for (i = 0; i < num_contributors; i++) - contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); + continue; + } + + // Using min to avoid writing into invalid pixels. + for (i = 0; i < num_contributors; i++) + contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); } // Each scan line uses the same kernel values so we should calculate the kernel // values once and then we can use them for every scan line. -static void stbir__calculate_filters(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) +static void stbir__calculate_filters(stbir__contributors *contributors, float *coefficients, + stbir_filter filter, float scale_ratio, float shift, + int input_size, int output_size) { - int n; - int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); + int n; + int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); - if (stbir__use_upsampling(scale_ratio)) - { - float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; + if (stbir__use_upsampling(scale_ratio)) + { + float out_pixels_radius = + stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; - // Looping through out pixels - for (n = 0; n < total_contributors; n++) - { - float in_center_of_out; // Center of the current out pixel in the in pixel space - int in_first_pixel, in_last_pixel; + // Looping through out pixels + for (n = 0; n < total_contributors; n++) + { + float in_center_of_out; // Center of the current out pixel in the in pixel space + int in_first_pixel, in_last_pixel; - stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); + stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, + &in_first_pixel, &in_last_pixel, &in_center_of_out); - stbir__calculate_coefficients_upsample(filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); - } + stbir__calculate_coefficients_upsample( + filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, + stbir__get_contributor(contributors, n), + stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); } - else + } + else + { + float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; + + // Looping through in pixels + for (n = 0; n < total_contributors; n++) { - float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; + float out_center_of_in; // Center of the current out pixel in the in pixel space + int out_first_pixel, out_last_pixel; + int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); + + stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, + &out_first_pixel, &out_last_pixel, + &out_center_of_in); + + stbir__calculate_coefficients_downsample( + filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, + stbir__get_contributor(contributors, n), + stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); + } - // Looping through in pixels - for (n = 0; n < total_contributors; n++) - { - float out_center_of_in; // Center of the current out pixel in the in pixel space - int out_first_pixel, out_last_pixel; - int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); + stbir__normalize_downsample_coefficients(contributors, coefficients, filter, scale_ratio, + input_size, output_size); + } +} + +static float *stbir__get_decode_buffer(stbir__info *stbir_info) +{ + // The 0 index of the decode buffer starts after the margin. This makes + // it okay to use negative indexes on the decode buffer. + return &stbir_info + ->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; +} + +# define STBIR__DECODE(type, colorspace) \ + ((int) (type) * (STBIR_MAX_COLORSPACES) + (int) (colorspace)) + +static void stbir__decode_scanline(stbir__info *stbir_info, int n) +{ + int c; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int input_w = stbir_info->input_w; + size_t input_stride_bytes = stbir_info->input_stride_bytes; + float *decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir_edge edge_horizontal = stbir_info->edge_horizontal; + stbir_edge edge_vertical = stbir_info->edge_vertical; + size_t in_buffer_row_offset = + stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; + const void *input_data = (char *) stbir_info->input_data + in_buffer_row_offset; + int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; + int decode = STBIR__DECODE(type, colorspace); + + int x = -stbir_info->horizontal_filter_pixel_margin; + + // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, + // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO + if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) + { + for (; x < max_x; x++) + for (c = 0; c < channels; c++) + decode_buffer[x * channels + c] = 0; + return; + } + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = + ((float) ((const unsigned char *) input_data)[input_pixel_index + c]) / + stbir__max_uint8_as_float; + } + break; - stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[( + (const unsigned char *) input_data)[input_pixel_index + c]]; + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = + ((float) ((const unsigned char *) input_data)[input_pixel_index + alpha_channel]) / + stbir__max_uint8_as_float; + } + break; - stbir__calculate_coefficients_downsample(filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); - } + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = + ((float) ((const unsigned short *) input_data)[input_pixel_index + c]) / + stbir__max_uint16_as_float; + } + break; - stbir__normalize_downsample_coefficients(contributors, coefficients, filter, scale_ratio, input_size, output_size); - } -} + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear( + ((float) ((const unsigned short *) input_data)[input_pixel_index + c]) / + stbir__max_uint16_as_float); + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = + ((float) ((const unsigned short *) input_data)[input_pixel_index + alpha_channel]) / + stbir__max_uint16_as_float; + } + break; -static float* stbir__get_decode_buffer(stbir__info* stbir_info) -{ - // The 0 index of the decode buffer starts after the margin. This makes - // it okay to use negative indexes on the decode buffer. - return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; -} + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = + (float) (((double) ((const unsigned int *) input_data)[input_pixel_index + c]) / + stbir__max_uint32_as_float); + } + break; -#define STBIR__DECODE(type, colorspace) ((int)(type) * (STBIR_MAX_COLORSPACES) + (int)(colorspace)) + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear( + (float) (((double) ((const unsigned int *) input_data)[input_pixel_index + c]) / + stbir__max_uint32_as_float)); + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = + (float) (((double) (( + const unsigned int *) input_data)[input_pixel_index + alpha_channel]) / + stbir__max_uint32_as_float); + } + break; -static void stbir__decode_scanline(stbir__info* stbir_info, int n) -{ - int c; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int input_w = stbir_info->input_w; - size_t input_stride_bytes = stbir_info->input_stride_bytes; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir_edge edge_horizontal = stbir_info->edge_horizontal; - stbir_edge edge_vertical = stbir_info->edge_vertical; - size_t in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; - const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; - int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; - int decode = STBIR__DECODE(type, colorspace); + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = + ((const float *) input_data)[input_pixel_index + c]; + } + break; + + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (; x < max_x; x++) + { + int decode_pixel_index = x * channels; + int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; + for (c = 0; c < channels; c++) + decode_buffer[decode_pixel_index + c] = + stbir__srgb_to_linear(((const float *) input_data)[input_pixel_index + c]); + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + decode_buffer[decode_pixel_index + alpha_channel] = + ((const float *) input_data)[input_pixel_index + alpha_channel]; + } - int x = -stbir_info->horizontal_filter_pixel_margin; + break; - // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, - // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO - if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) + default: + STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); + break; + } + + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) { - for (; x < max_x; x++) - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - return; + int decode_pixel_index = x * channels; + + // If the alpha value is 0 it will clobber the color values. Make sure it's not. + float alpha = decode_buffer[decode_pixel_index + alpha_channel]; +# ifndef STBIR_NO_ALPHA_EPSILON + if (stbir_info->type != STBIR_TYPE_FLOAT) + { + alpha += STBIR_ALPHA_EPSILON; + decode_buffer[decode_pixel_index + alpha_channel] = alpha; + } +# endif + for (c = 0; c < channels; c++) + { + if (c == alpha_channel) + continue; + + decode_buffer[decode_pixel_index + c] *= alpha; + } } + } - switch (decode) + if (edge_horizontal == STBIR_EDGE_ZERO) + { + for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / stbir__max_uint8_as_float; - } - break; + for (c = 0; c < channels; c++) + decode_buffer[x * channels + c] = 0; + } + for (x = input_w; x < max_x; x++) + { + for (c = 0; c < channels; c++) + decode_buffer[x * channels + c] = 0; + } + } +} - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; +static float *stbir__get_ring_buffer_entry(float *ring_buffer, int index, int ring_buffer_length) +{ + return &ring_buffer[index * ring_buffer_length]; +} - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint8_as_float; - } - break; +static float *stbir__add_empty_ring_buffer_entry(stbir__info *stbir_info, int n) +{ + int ring_buffer_index; + float *ring_buffer; - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float; - } - break; + stbir_info->ring_buffer_last_scanline = n; - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float); + if (stbir_info->ring_buffer_begin_index < 0) + { + ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; + stbir_info->ring_buffer_first_scanline = n; + } + else + { + ring_buffer_index = + (stbir_info->ring_buffer_begin_index + + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline)) % + stbir_info->ring_buffer_num_entries; + STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); + } - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint16_as_float; - } - break; + ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, + stbir_info->ring_buffer_length_bytes / sizeof(float)); + memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float); - } - break; + return ring_buffer; +} - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float)); +static void stbir__resample_horizontal_upsample(stbir__info *stbir_info, float *output_buffer) +{ + int x, k; + int output_w = stbir_info->output_w; + int channels = stbir_info->channels; + float *decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors *horizontal_contributors = stbir_info->horizontal_contributors; + float *horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint32_as_float); - } - break; + for (x = 0; x < output_w; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) + int out_pixel_index = x * channels; + int coefficient_group = coefficient_width * x; + int coefficient_counter = 0; + + STBIR_ASSERT(n1 >= n0); + STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); + + switch (channels) + { + case 1: + for (k = n0; k <= n1; k++) { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; + int in_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; } break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) + case 2: + for (k = n0; k <= n1; k++) { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; + int in_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; } - break; - - default: - STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); - break; - } - - if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) - { - for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) + case 3: + for (k = n0; k <= n1; k++) { - int decode_pixel_index = x * channels; - - // If the alpha value is 0 it will clobber the color values. Make sure it's not. - float alpha = decode_buffer[decode_pixel_index + alpha_channel]; -#ifndef STBIR_NO_ALPHA_EPSILON - if (stbir_info->type != STBIR_TYPE_FLOAT) { - alpha += STBIR_ALPHA_EPSILON; - decode_buffer[decode_pixel_index + alpha_channel] = alpha; - } -#endif - for (c = 0; c < channels; c++) - { - if (c == alpha_channel) - continue; - - decode_buffer[decode_pixel_index + c] *= alpha; - } + int in_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; } - } - - if (edge_horizontal == STBIR_EDGE_ZERO) - { - for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) + break; + case 4: + for (k = n0; k <= n1; k++) { - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; + int in_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + STBIR_ASSERT(coefficient != 0); + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; } - for (x = input_w; x < max_x; x++) + break; + default: + for (k = n0; k <= n1; k++) { - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; + int in_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; + int c; + STBIR_ASSERT(coefficient != 0); + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; } + break; } + } } -static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) +static void stbir__resample_horizontal_downsample(stbir__info *stbir_info, float *output_buffer) { - return &ring_buffer[index * ring_buffer_length]; -} + int x, k; + int input_w = stbir_info->input_w; + int channels = stbir_info->channels; + float *decode_buffer = stbir__get_decode_buffer(stbir_info); + stbir__contributors *horizontal_contributors = stbir_info->horizontal_contributors; + float *horizontal_coefficients = stbir_info->horizontal_coefficients; + int coefficient_width = stbir_info->horizontal_coefficient_width; + int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; + int max_x = input_w + filter_pixel_margin * 2; -static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) -{ - int ring_buffer_index; - float* ring_buffer; + STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info)); - stbir_info->ring_buffer_last_scanline = n; + switch (channels) + { + case 1: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; - if (stbir_info->ring_buffer_begin_index < 0) - { - ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; - stbir_info->ring_buffer_first_scanline = n; - } - else - { - ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; - STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); - } + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 1; + int max_n = n1; + int coefficient_group = coefficient_width * x; - ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); - memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 1; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + } + } + break; - return ring_buffer; -} + case 2: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 2; + int max_n = n1; + int coefficient_group = coefficient_width * x; -static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, float* output_buffer) -{ - int x, k; - int output_w = stbir_info->output_w; - int channels = stbir_info->channels; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; - float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir_info->horizontal_coefficient_width; + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 2; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + } + } + break; - for (x = 0; x < output_w; x++) - { + case 3: + for (x = 0; x < max_x; x++) + { int n0 = horizontal_contributors[x].n0; int n1 = horizontal_contributors[x].n1; - int out_pixel_index = x * channels; + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 3; + int max_n = n1; int coefficient_group = coefficient_width * x; - int coefficient_counter = 0; - - STBIR_ASSERT(n1 >= n0); - STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); - - switch (channels) { - case 1: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 1; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - } - break; - case 2: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 2; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - } - break; - case 3: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 3; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - } - break; - case 4: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 4; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; - } - break; - default: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - int c; - STBIR_ASSERT(coefficient != 0); - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - break; - } - } -} -static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float* output_buffer) -{ - int x, k; - int input_w = stbir_info->input_w; - int channels = stbir_info->channels; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; - float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir_info->horizontal_coefficient_width; - int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; - int max_x = input_w + filter_pixel_margin * 2; + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 3; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + } + } + break; - STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info)); + case 4: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; - switch (channels) { - case 1: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 1; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 1; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - } - } - break; + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * 4; + int max_n = n1; + int coefficient_group = coefficient_width * x; - case 2: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 2; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 2; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - } - } - break; + for (k = n0; k <= max_n; k++) + { + int out_pixel_index = k * 4; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; + output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; + output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; + output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; + } + } + break; - case 3: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 3; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 3; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - } - } - break; + default: + for (x = 0; x < max_x; x++) + { + int n0 = horizontal_contributors[x].n0; + int n1 = horizontal_contributors[x].n1; - case 4: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 4; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 4; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; - } - } - break; + int in_x = x - filter_pixel_margin; + int in_pixel_index = in_x * channels; + int max_n = n1; + int coefficient_group = coefficient_width * x; - default: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * channels; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int c; - int out_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - } - break; - } + for (k = n0; k <= max_n; k++) + { + int c; + int out_pixel_index = k * channels; + float coefficient = horizontal_coefficients[coefficient_group + k - n0]; + for (c = 0; c < channels; c++) + output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; + } + } + break; + } } -static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) +static void stbir__decode_and_resample_upsample(stbir__info *stbir_info, int n) { - // Decode the nth scanline from the source image into the decode buffer. - stbir__decode_scanline(stbir_info, n); + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); - // Now resample it into the ring buffer. - if (stbir__use_width_upsampling(stbir_info)) - stbir__resample_horizontal_upsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); - else - stbir__resample_horizontal_downsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); + // Now resample it into the ring buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, + stbir__add_empty_ring_buffer_entry(stbir_info, n)); + else + stbir__resample_horizontal_downsample(stbir_info, + stbir__add_empty_ring_buffer_entry(stbir_info, n)); - // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. } -static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) +static void stbir__decode_and_resample_downsample(stbir__info *stbir_info, int n) { - // Decode the nth scanline from the source image into the decode buffer. - stbir__decode_scanline(stbir_info, n); + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline(stbir_info, n); - memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); + memset(stbir_info->horizontal_buffer, 0, + stbir_info->output_w * stbir_info->channels * sizeof(float)); - // Now resample it into the horizontal buffer. - if (stbir__use_width_upsampling(stbir_info)) - stbir__resample_horizontal_upsample(stbir_info, stbir_info->horizontal_buffer); - else - stbir__resample_horizontal_downsample(stbir_info, stbir_info->horizontal_buffer); + // Now resample it into the horizontal buffer. + if (stbir__use_width_upsampling(stbir_info)) + stbir__resample_horizontal_upsample(stbir_info, stbir_info->horizontal_buffer); + else + stbir__resample_horizontal_downsample(stbir_info, stbir_info->horizontal_buffer); - // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. + // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. } // Get the specified scan line from the ring buffer. -static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_num_entries, int ring_buffer_length) +static float *stbir__get_ring_buffer_scanline(int get_scanline, float *ring_buffer, int begin_index, + int first_scanline, int ring_buffer_num_entries, + int ring_buffer_length) { - int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_num_entries; - return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); + int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_num_entries; + return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); } - -static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode) +static void stbir__encode_scanline(stbir__info *stbir_info, int num_pixels, void *output_buffer, + float *encode_buffer, int channels, int alpha_channel, + int decode) { - int x; - int n; - int num_nonalpha; - stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; + int x; + int n; + int num_nonalpha; + stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + for (x = 0; x < num_pixels; ++x) { - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; + int pixel_index = x * channels; - float alpha = encode_buffer[pixel_index + alpha_channel]; - float reciprocal_alpha = alpha ? 1.0f / alpha : 0; + float alpha = encode_buffer[pixel_index + alpha_channel]; + float reciprocal_alpha = alpha ? 1.0f / alpha : 0; - // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb - for (n = 0; n < channels; n++) - if (n != alpha_channel) - encode_buffer[pixel_index + n] *= reciprocal_alpha; + // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb + for (n = 0; n < channels; n++) + if (n != alpha_channel) + encode_buffer[pixel_index + n] *= reciprocal_alpha; - // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. - // Because we only add it for integer types, it will automatically be discarded on integer - // conversion, so we don't need to subtract it back out (which would be problematic for - // numeric precision reasons). - } + // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. + // Because we only add it for integer types, it will automatically be discarded on integer + // conversion, so we don't need to subtract it back out (which would be problematic for + // numeric precision reasons). } + } - // build a table of all channels that need colorspace correction, so - // we don't perform colorspace correction on channels that don't need it. - for (x = 0, num_nonalpha = 0; x < channels; ++x) + // build a table of all channels that need colorspace correction, so + // we don't perform colorspace correction on channels that don't need it. + for (x = 0, num_nonalpha = 0; x < channels; ++x) + { + if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) { - if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) - { - nonalpha[num_nonalpha++] = (stbir_uint16)x; - } + nonalpha[num_nonalpha++] = (stbir_uint16) x; } + } + +# define STBIR__ROUND_INT(f) ((int) ((f) + 0.5)) +# define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f) + 0.5)) + +# ifdef STBIR__SATURATE_INT +# define STBIR__ENCODE_LINEAR8(f) \ + stbir__saturate8(STBIR__ROUND_INT((f) *stbir__max_uint8_as_float)) +# define STBIR__ENCODE_LINEAR16(f) \ + stbir__saturate16(STBIR__ROUND_INT((f) *stbir__max_uint16_as_float)) +# else +# define STBIR__ENCODE_LINEAR8(f) \ + (unsigned char) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint8_as_float) +# define STBIR__ENCODE_LINEAR16(f) \ + (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint16_as_float) +# endif + + switch (decode) + { + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): + for (x = 0; x < num_pixels; ++x) + { + int pixel_index = x * channels; - #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) - #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) - - #ifdef STBIR__SATURATE_INT - #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * stbir__max_uint8_as_float )) - #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * stbir__max_uint16_as_float)) - #else - #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint8_as_float ) - #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint16_as_float) - #endif - - switch (decode) - { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); - } - - if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]); - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * stbir__max_uint16_as_float); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); - } - - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * stbir__max_uint32_as_float); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * stbir__max_uint32_as_float); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * stbir__max_uint32_as_float); - } - break; + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned char *) output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); + } + } + break; - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; + case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): + for (x = 0; x < num_pixels; ++x) + { + int pixel_index = x * channels; - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((float*)output_buffer)[index] = encode_buffer[index]; - } - } - break; + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned char *) output_buffer)[index] = + stbir__linear_to_srgb_uchar(encode_buffer[index]); + } - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned char *) output_buffer)[pixel_index + alpha_channel] = + STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index + alpha_channel]); + } + break; - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); - } + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): + for (x = 0; x < num_pixels; ++x) + { + int pixel_index = x * channels; - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; - } - break; + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned short *) output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); + } + } + break; - default: - STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); - break; - } -} + case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): + for (x = 0; x < num_pixels; ++x) + { + int pixel_index = x * channels; -static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n) -{ - int x, k; - int output_w = stbir_info->output_w; - stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; - float* vertical_coefficients = stbir_info->vertical_coefficients; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int ring_buffer_entries = stbir_info->ring_buffer_num_entries; - void* output_data = stbir_info->output_data; - float* encode_buffer = stbir_info->encode_buffer; - int decode = STBIR__DECODE(type, colorspace); - int coefficient_width = stbir_info->vertical_coefficient_width; - int coefficient_counter; - int contributor = n; + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned short *) output_buffer)[index] = (unsigned short) STBIR__ROUND_INT( + stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * + stbir__max_uint16_as_float); + } - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned short *) output_buffer)[pixel_index + alpha_channel] = + STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); + } - int n0,n1, output_row_start; - int coefficient_group = coefficient_width * contributor; + break; - n0 = vertical_contributors[contributor].n0; - n1 = vertical_contributors[contributor].n1; + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): + for (x = 0; x < num_pixels; ++x) + { + int pixel_index = x * channels; - output_row_start = n * stbir_info->output_stride_bytes; + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((unsigned int *) output_buffer)[index] = (unsigned int) STBIR__ROUND_UINT( + ((double) stbir__saturate(encode_buffer[index])) * stbir__max_uint32_as_float); + } + } + break; - STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); + case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): + for (x = 0; x < num_pixels; ++x) + { + int pixel_index = x * channels; - memset(encode_buffer, 0, output_w * sizeof(float) * channels); + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((unsigned int *) output_buffer)[index] = (unsigned int) STBIR__ROUND_UINT( + ((double) stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * + stbir__max_uint32_as_float); + } - // I tried reblocking this for better cache usage of encode_buffer - // (using x_outer, k, x_inner), but it lost speed. -- stb + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((unsigned int *) output_buffer)[pixel_index + alpha_channel] = + (unsigned int) STBIR__ROUND_INT( + ((double) stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * + stbir__max_uint32_as_float); + } + break; - coefficient_counter = 0; - switch (channels) { - case 1: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 1; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - } - } - break; - case 2: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 2; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - } - } - break; - case 3: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 3; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; - } - } - break; - case 4: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 4; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; - encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; - } - } - break; - default: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * channels; - int c; - for (c = 0; c < channels; c++) - encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; - } - } - break; - } - stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); -} + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): + for (x = 0; x < num_pixels; ++x) + { + int pixel_index = x * channels; -static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n) -{ - int x, k; - int output_w = stbir_info->output_w; - stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; - float* vertical_coefficients = stbir_info->vertical_coefficients; - int channels = stbir_info->channels; - int ring_buffer_entries = stbir_info->ring_buffer_num_entries; - float* horizontal_buffer = stbir_info->horizontal_buffer; - int coefficient_width = stbir_info->vertical_coefficient_width; - int contributor = n + stbir_info->vertical_filter_pixel_margin; + for (n = 0; n < channels; n++) + { + int index = pixel_index + n; + ((float *) output_buffer)[index] = encode_buffer[index]; + } + } + break; - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - int n0,n1; + case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): + for (x = 0; x < num_pixels; ++x) + { + int pixel_index = x * channels; - n0 = vertical_contributors[contributor].n0; - n1 = vertical_contributors[contributor].n1; + for (n = 0; n < num_nonalpha; n++) + { + int index = pixel_index + nonalpha[n]; + ((float *) output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); + } - STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); + if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) + ((float *) output_buffer)[pixel_index + alpha_channel] = + encode_buffer[pixel_index + alpha_channel]; + } + break; - for (k = n0; k <= n1; k++) - { - int coefficient_index = k - n0; - int coefficient_group = coefficient_width * contributor; + default: + STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); + break; + } +} + +static void stbir__resample_vertical_upsample(stbir__info *stbir_info, int n) +{ + int x, k; + int output_w = stbir_info->output_w; + stbir__contributors *vertical_contributors = stbir_info->vertical_contributors; + float *vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int ring_buffer_entries = stbir_info->ring_buffer_num_entries; + void *output_data = stbir_info->output_data; + float *encode_buffer = stbir_info->encode_buffer; + int decode = STBIR__DECODE(type, colorspace); + int coefficient_width = stbir_info->vertical_coefficient_width; + int coefficient_counter; + int contributor = n; + + float *ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes / sizeof(float); + + int n0, n1, output_row_start; + int coefficient_group = coefficient_width * contributor; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + output_row_start = n * stbir_info->output_stride_bytes; + + STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); + + memset(encode_buffer, 0, output_w * sizeof(float) * channels); + + // I tried reblocking this for better cache usage of encode_buffer + // (using x_outer, k, x_inner), but it lost speed. -- stb + + coefficient_counter = 0; + switch (channels) + { + case 1: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float *ring_buffer_entry = stbir__get_ring_buffer_scanline( + k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, + ring_buffer_entries, ring_buffer_length); float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 1; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + } + } + break; + case 2: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float *ring_buffer_entry = stbir__get_ring_buffer_scanline( + k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, + ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 2; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + } + } + break; + case 3: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float *ring_buffer_entry = stbir__get_ring_buffer_scanline( + k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, + ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 3; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + } + } + break; + case 4: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float *ring_buffer_entry = stbir__get_ring_buffer_scanline( + k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, + ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * 4; + encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; + encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; + encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; + encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; + } + } + break; + default: + for (k = n0; k <= n1; k++) + { + int coefficient_index = coefficient_counter++; + float *ring_buffer_entry = stbir__get_ring_buffer_scanline( + k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, + ring_buffer_entries, ring_buffer_length); + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + for (x = 0; x < output_w; ++x) + { + int in_pixel_index = x * channels; + int c; + for (c = 0; c < channels; c++) + encode_buffer[in_pixel_index + c] += + ring_buffer_entry[in_pixel_index + c] * coefficient; + } + } + break; + } + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, + encode_buffer, channels, alpha_channel, decode); +} + +static void stbir__resample_vertical_downsample(stbir__info *stbir_info, int n) +{ + int x, k; + int output_w = stbir_info->output_w; + stbir__contributors *vertical_contributors = stbir_info->vertical_contributors; + float *vertical_coefficients = stbir_info->vertical_coefficients; + int channels = stbir_info->channels; + int ring_buffer_entries = stbir_info->ring_buffer_num_entries; + float *horizontal_buffer = stbir_info->horizontal_buffer; + int coefficient_width = stbir_info->vertical_coefficient_width; + int contributor = n + stbir_info->vertical_filter_pixel_margin; + + float *ring_buffer = stbir_info->ring_buffer; + int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; + int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes / sizeof(float); + int n0, n1; + + n0 = vertical_contributors[contributor].n0; + n1 = vertical_contributors[contributor].n1; + + STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); + + for (k = n0; k <= n1; k++) + { + int coefficient_index = k - n0; + int coefficient_group = coefficient_width * contributor; + float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; + + float *ring_buffer_entry = stbir__get_ring_buffer_scanline( + k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, + ring_buffer_length); + + switch (channels) + { + case 1: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 1; + ring_buffer_entry[in_pixel_index + 0] += + horizontal_buffer[in_pixel_index + 0] * coefficient; + } + break; + case 2: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 2; + ring_buffer_entry[in_pixel_index + 0] += + horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += + horizontal_buffer[in_pixel_index + 1] * coefficient; + } + break; + case 3: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 3; + ring_buffer_entry[in_pixel_index + 0] += + horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += + horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += + horizontal_buffer[in_pixel_index + 2] * coefficient; + } + break; + case 4: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * 4; + ring_buffer_entry[in_pixel_index + 0] += + horizontal_buffer[in_pixel_index + 0] * coefficient; + ring_buffer_entry[in_pixel_index + 1] += + horizontal_buffer[in_pixel_index + 1] * coefficient; + ring_buffer_entry[in_pixel_index + 2] += + horizontal_buffer[in_pixel_index + 2] * coefficient; + ring_buffer_entry[in_pixel_index + 3] += + horizontal_buffer[in_pixel_index + 3] * coefficient; + } + break; + default: + for (x = 0; x < output_w; x++) + { + int in_pixel_index = x * channels; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - - switch (channels) { - case 1: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 1; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - } - break; - case 2: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 2; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - } - break; - case 3: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 3; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; - } - break; - case 4: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 4; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; - ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient; - } - break; - default: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * channels; - - int c; - for (c = 0; c < channels; c++) - ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; - } - break; + int c; + for (c = 0; c < channels; c++) + ring_buffer_entry[in_pixel_index + c] += + horizontal_buffer[in_pixel_index + c] * coefficient; } + break; } + } } -static void stbir__buffer_loop_upsample(stbir__info* stbir_info) +static void stbir__buffer_loop_upsample(stbir__info *stbir_info) { - int y; - float scale_ratio = stbir_info->vertical_scale; - float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; + int y; + float scale_ratio = stbir_info->vertical_scale; + float out_scanlines_radius = + stbir__filter_info_table[stbir_info->vertical_filter].support(1 / scale_ratio) * scale_ratio; - STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); + STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); - for (y = 0; y < stbir_info->output_h; y++) - { - float in_center_of_out = 0; // Center of the current out scanline in the in scanline space - int in_first_scanline = 0, in_last_scanline = 0; + for (y = 0; y < stbir_info->output_h; y++) + { + float in_center_of_out = + 0; // Center of the current out scanline in the in scanline space + int in_first_scanline = 0, in_last_scanline = 0; - stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); + stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, + stbir_info->vertical_shift, &in_first_scanline, + &in_last_scanline, &in_center_of_out); - STBIR_ASSERT(in_last_scanline - in_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); + STBIR_ASSERT(in_last_scanline - in_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); - if (stbir_info->ring_buffer_begin_index >= 0) + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (in_first_scanline > stbir_info->ring_buffer_first_scanline) + { + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) { - // Get rid of whatever we don't need anymore. - while (in_first_scanline > stbir_info->ring_buffer_first_scanline) - { - if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbir_info->ring_buffer_begin_index = -1; - stbir_info->ring_buffer_first_scanline = 0; - stbir_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; - } - } + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = + (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; + } + } + } - // Load in new ones. - if (stbir_info->ring_buffer_begin_index < 0) - stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); - while (in_last_scanline > stbir_info->ring_buffer_last_scanline) - stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + while (in_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); - // Now all buffers should be ready to write a row of vertical sampling. - stbir__resample_vertical_upsample(stbir_info, y); + // Now all buffers should be ready to write a row of vertical sampling. + stbir__resample_vertical_upsample(stbir_info, y); - STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); - } + STBIR_PROGRESS_REPORT((float) y / stbir_info->output_h); + } } -static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) +static void stbir__empty_ring_buffer(stbir__info *stbir_info, int first_necessary_scanline) { - int output_stride_bytes = stbir_info->output_stride_bytes; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int output_w = stbir_info->output_w; - void* output_data = stbir_info->output_data; - int decode = STBIR__DECODE(type, colorspace); + int output_stride_bytes = stbir_info->output_stride_bytes; + int channels = stbir_info->channels; + int alpha_channel = stbir_info->alpha_channel; + int type = stbir_info->type; + int colorspace = stbir_info->colorspace; + int output_w = stbir_info->output_w; + void *output_data = stbir_info->output_data; + int decode = STBIR__DECODE(type, colorspace); - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); + float *ring_buffer = stbir_info->ring_buffer; + int ring_buffer_length = stbir_info->ring_buffer_length_bytes / sizeof(float); - if (stbir_info->ring_buffer_begin_index >= 0) + if (stbir_info->ring_buffer_begin_index >= 0) + { + // Get rid of whatever we don't need anymore. + while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) { - // Get rid of whatever we don't need anymore. - while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) - { - if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) - { - int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; - float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); - stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); - STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); - } - - if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbir_info->ring_buffer_begin_index = -1; - stbir_info->ring_buffer_first_scanline = 0; - stbir_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; - } - } + if (stbir_info->ring_buffer_first_scanline >= 0 && + stbir_info->ring_buffer_first_scanline < stbir_info->output_h) + { + int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; + float *ring_buffer_entry = stbir__get_ring_buffer_entry( + ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); + stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, + ring_buffer_entry, channels, alpha_channel, decode); + STBIR_PROGRESS_REPORT((float) stbir_info->ring_buffer_first_scanline / + stbir_info->output_h); + } + + if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) + { + // We just popped the last scanline off the ring buffer. + // Reset it to the empty state. + stbir_info->ring_buffer_begin_index = -1; + stbir_info->ring_buffer_first_scanline = 0; + stbir_info->ring_buffer_last_scanline = 0; + break; + } + else + { + stbir_info->ring_buffer_first_scanline++; + stbir_info->ring_buffer_begin_index = + (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; + } } + } } -static void stbir__buffer_loop_downsample(stbir__info* stbir_info) +static void stbir__buffer_loop_downsample(stbir__info *stbir_info) { - int y; - float scale_ratio = stbir_info->vertical_scale; - int output_h = stbir_info->output_h; - float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; - int pixel_margin = stbir_info->vertical_filter_pixel_margin; - int max_y = stbir_info->input_h + pixel_margin; + int y; + float scale_ratio = stbir_info->vertical_scale; + int output_h = stbir_info->output_h; + float in_pixels_radius = + stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; + int pixel_margin = stbir_info->vertical_filter_pixel_margin; + int max_y = stbir_info->input_h + pixel_margin; - STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); + STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); - for (y = -pixel_margin; y < max_y; y++) - { - float out_center_of_in; // Center of the current out scanline in the in scanline space - int out_first_scanline, out_last_scanline; + for (y = -pixel_margin; y < max_y; y++) + { + float out_center_of_in; // Center of the current out scanline in the in scanline space + int out_first_scanline, out_last_scanline; - stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); + stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, + stbir_info->vertical_shift, &out_first_scanline, + &out_last_scanline, &out_center_of_in); - STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); + STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); - if (out_last_scanline < 0 || out_first_scanline >= output_h) - continue; + if (out_last_scanline < 0 || out_first_scanline >= output_h) + continue; - stbir__empty_ring_buffer(stbir_info, out_first_scanline); + stbir__empty_ring_buffer(stbir_info, out_first_scanline); - stbir__decode_and_resample_downsample(stbir_info, y); + stbir__decode_and_resample_downsample(stbir_info, y); - // Load in new ones. - if (stbir_info->ring_buffer_begin_index < 0) - stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); + // Load in new ones. + if (stbir_info->ring_buffer_begin_index < 0) + stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); - while (out_last_scanline > stbir_info->ring_buffer_last_scanline) - stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); + while (out_last_scanline > stbir_info->ring_buffer_last_scanline) + stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); - // Now the horizontal buffer is ready to write to all ring buffer rows. - stbir__resample_vertical_downsample(stbir_info, y); - } + // Now the horizontal buffer is ready to write to all ring buffer rows. + stbir__resample_vertical_downsample(stbir_info, y); + } - stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); + stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); } -static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) +static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, + int channels) { - info->input_w = input_w; - info->input_h = input_h; - info->output_w = output_w; - info->output_h = output_h; - info->channels = channels; + info->input_w = input_w; + info->input_h = input_h; + info->output_w = output_w; + info->output_h = output_h; + info->channels = channels; } -static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) +static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, + float *transform) { - info->s0 = s0; - info->t0 = t0; - info->s1 = s1; - info->t1 = t1; + info->s0 = s0; + info->t0 = t0; + info->s1 = s1; + info->t1 = t1; - if (transform) - { - info->horizontal_scale = transform[0]; - info->vertical_scale = transform[1]; - info->horizontal_shift = transform[2]; - info->vertical_shift = transform[3]; - } - else - { - info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); - info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); + if (transform) + { + info->horizontal_scale = transform[0]; + info->vertical_scale = transform[1]; + info->horizontal_shift = transform[2]; + info->vertical_shift = transform[3]; + } + else + { + info->horizontal_scale = ((float) info->output_w / info->input_w) / (s1 - s0); + info->vertical_scale = ((float) info->output_h / info->input_h) / (t1 - t0); - info->horizontal_shift = s0 * info->output_w / (s1 - s0); - info->vertical_shift = t0 * info->output_h / (t1 - t0); - } + info->horizontal_shift = s0 * info->output_w / (s1 - s0); + info->vertical_shift = t0 * info->output_h / (t1 - t0); + } } static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter) { - if (h_filter == 0) - h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; - if (v_filter == 0) - v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; - info->horizontal_filter = h_filter; - info->vertical_filter = v_filter; + if (h_filter == 0) + h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : + STBIR_DEFAULT_FILTER_DOWNSAMPLE; + if (v_filter == 0) + v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : + STBIR_DEFAULT_FILTER_DOWNSAMPLE; + info->horizontal_filter = h_filter; + info->vertical_filter = v_filter; } static stbir_uint32 stbir__calculate_memory(stbir__info *info) { - int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); - int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); - - info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); - info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h); - - // One extra entry because floating point precision problems sometimes cause an extra to be necessary. - info->ring_buffer_num_entries = filter_height + 1; - - info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors); - info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); - info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); - info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); - info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); - info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); - info->ring_buffer_size = info->output_w * info->channels * info->ring_buffer_num_entries * sizeof(float); - info->encode_buffer_size = info->output_w * info->channels * sizeof(float); - - STBIR_ASSERT(info->horizontal_filter != 0); - STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - STBIR_ASSERT(info->vertical_filter != 0); - STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - - if (stbir__use_height_upsampling(info)) - // The horizontal buffer is for when we're downsampling the height and we - // can't output the result of sampling the decode buffer directly into the - // ring buffers. - info->horizontal_buffer_size = 0; - else - // The encode buffer is to retain precision in the height upsampling method - // and isn't used when height downsampling. - info->encode_buffer_size = 0; - - return info->horizontal_contributors_size + info->horizontal_coefficients_size - + info->vertical_contributors_size + info->vertical_coefficients_size - + info->decode_buffer_size + info->horizontal_buffer_size - + info->ring_buffer_size + info->encode_buffer_size; -} - -static int stbir__resize_allocated(stbir__info *info, - const void* input_data, int input_stride_in_bytes, - void* output_data, int output_stride_in_bytes, - int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, - void* tempmem, size_t tempmem_size_in_bytes) -{ - size_t memory_required = stbir__calculate_memory(info); - - int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; - int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; - -#ifdef STBIR_DEBUG_OVERWRITE_TEST -#define OVERWRITE_ARRAY_SIZE 8 - unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; - - size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; - memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); -#endif - - STBIR_ASSERT(info->channels >= 0); - STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); - - if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) - return 0; - - STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - - if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) - return 0; - if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) - return 0; - - if (alpha_channel < 0) - flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; - - if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) { - STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); - } - - if (alpha_channel >= info->channels) - return 0; - - STBIR_ASSERT(tempmem); - - if (!tempmem) - return 0; - - STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); - - if (tempmem_size_in_bytes < memory_required) - return 0; - - memset(tempmem, 0, tempmem_size_in_bytes); - - info->input_data = input_data; - info->input_stride_bytes = width_stride_input; - - info->output_data = output_data; - info->output_stride_bytes = width_stride_output; - - info->alpha_channel = alpha_channel; - info->flags = flags; - info->type = type; - info->edge_horizontal = edge_horizontal; - info->edge_vertical = edge_vertical; - info->colorspace = colorspace; - - info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); - info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale ); - info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); - info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale ); - info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); - info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale ); - - info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); - info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; - -#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) - - info->horizontal_contributors = (stbir__contributors *) tempmem; - info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); - info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); - info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); - info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); - - if (stbir__use_height_upsampling(info)) - { - info->horizontal_buffer = NULL; - info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); - info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); - - STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } - else - { - info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); - info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); - info->encode_buffer = NULL; - - STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } - -#undef STBIR__NEXT_MEMPTR - - // This signals that the ring buffer is empty - info->ring_buffer_begin_index = -1; + int pixel_margin = + stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); + + info->horizontal_num_contributors = stbir__get_contributors( + info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); + info->vertical_num_contributors = stbir__get_contributors( + info->vertical_scale, info->vertical_filter, info->input_h, info->output_h); + + // One extra entry because floating point precision problems sometimes cause an extra to be necessary. + info->ring_buffer_num_entries = filter_height + 1; + + info->horizontal_contributors_size = + info->horizontal_num_contributors * sizeof(stbir__contributors); + info->horizontal_coefficients_size = + stbir__get_total_horizontal_coefficients(info) * sizeof(float); + info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); + info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); + info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); + info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); + info->ring_buffer_size = + info->output_w * info->channels * info->ring_buffer_num_entries * sizeof(float); + info->encode_buffer_size = info->output_w * info->channels * sizeof(float); + + STBIR_ASSERT(info->horizontal_filter != 0); + STBIR_ASSERT(info->horizontal_filter < + STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + STBIR_ASSERT(info->vertical_filter != 0); + STBIR_ASSERT(info->vertical_filter < + STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late + + if (stbir__use_height_upsampling(info)) + // The horizontal buffer is for when we're downsampling the height and we + // can't output the result of sampling the decode buffer directly into the + // ring buffers. + info->horizontal_buffer_size = 0; + else + // The encode buffer is to retain precision in the height upsampling method + // and isn't used when height downsampling. + info->encode_buffer_size = 0; + + return info->horizontal_contributors_size + info->horizontal_coefficients_size + + info->vertical_contributors_size + info->vertical_coefficients_size + + info->decode_buffer_size + info->horizontal_buffer_size + info->ring_buffer_size + + info->encode_buffer_size; +} + +static int stbir__resize_allocated(stbir__info *info, const void *input_data, + int input_stride_in_bytes, void *output_data, + int output_stride_in_bytes, int alpha_channel, + stbir_uint32 flags, stbir_datatype type, + stbir_edge edge_horizontal, stbir_edge edge_vertical, + stbir_colorspace colorspace, void *tempmem, + size_t tempmem_size_in_bytes) +{ + size_t memory_required = stbir__calculate_memory(info); + + int width_stride_input = input_stride_in_bytes ? + input_stride_in_bytes : + info->channels * info->input_w * stbir__type_size[type]; + int width_stride_output = output_stride_in_bytes ? + output_stride_in_bytes : + info->channels * info->output_w * stbir__type_size[type]; + +# ifdef STBIR_DEBUG_OVERWRITE_TEST +# define OVERWRITE_ARRAY_SIZE 8 + unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; + unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; + + size_t begin_forbidden = width_stride_output * (info->output_h - 1) + + info->output_w * info->channels * stbir__type_size[type]; + memcpy(overwrite_output_before_pre, &((unsigned char *) output_data)[-OVERWRITE_ARRAY_SIZE], + OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_output_after_pre, &((unsigned char *) output_data)[begin_forbidden], + OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_before_pre, &((unsigned char *) tempmem)[-OVERWRITE_ARRAY_SIZE], + OVERWRITE_ARRAY_SIZE); + memcpy(overwrite_tempmem_after_pre, &((unsigned char *) tempmem)[tempmem_size_in_bytes], + OVERWRITE_ARRAY_SIZE); +# endif + + STBIR_ASSERT(info->channels >= 0); + STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); + + if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) + return 0; - stbir__calculate_filters(info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); - stbir__calculate_filters(info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); + STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); + STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - STBIR_PROGRESS_REPORT(0); + if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; + if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) + return 0; - if (stbir__use_height_upsampling(info)) - stbir__buffer_loop_upsample(info); - else - stbir__buffer_loop_downsample(info); + if (alpha_channel < 0) + flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; - STBIR_PROGRESS_REPORT(1); + if (!(flags & STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) + { + STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); + } -#ifdef STBIR_DEBUG_OVERWRITE_TEST - STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); -#endif + if (alpha_channel >= info->channels) + return 0; - return 1; -} + STBIR_ASSERT(tempmem); + if (!tempmem) + return 0; -static int stbir__resize_arbitrary( - void *alloc_context, - const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, float *transform, - int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_filter h_filter, stbir_filter v_filter, - stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) -{ - stbir__info info; - int result; - size_t memory_required; - void* extra_memory; + STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); - stbir__setup(&info, input_w, input_h, output_w, output_h, channels); - stbir__calculate_transform(&info, s0,t0,s1,t1,transform); - stbir__choose_filter(&info, h_filter, v_filter); - memory_required = stbir__calculate_memory(&info); - extra_memory = STBIR_MALLOC(memory_required, alloc_context); + if (tempmem_size_in_bytes < memory_required) + return 0; - if (!extra_memory) - return 0; + memset(tempmem, 0, tempmem_size_in_bytes); + + info->input_data = input_data; + info->input_stride_bytes = width_stride_input; + + info->output_data = output_data; + info->output_stride_bytes = width_stride_output; + + info->alpha_channel = alpha_channel; + info->flags = flags; + info->type = type; + info->edge_horizontal = edge_horizontal; + info->edge_vertical = edge_vertical; + info->colorspace = colorspace; + + info->horizontal_coefficient_width = + stbir__get_coefficient_width(info->horizontal_filter, info->horizontal_scale); + info->vertical_coefficient_width = + stbir__get_coefficient_width(info->vertical_filter, info->vertical_scale); + info->horizontal_filter_pixel_width = + stbir__get_filter_pixel_width(info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_width = + stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); + info->horizontal_filter_pixel_margin = + stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); + info->vertical_filter_pixel_margin = + stbir__get_filter_pixel_margin(info->vertical_filter, info->vertical_scale); + + info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); + info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; + +# define STBIR__NEXT_MEMPTR(current, newtype) \ + (newtype *) (((unsigned char *) current) + current##_size) + + info->horizontal_contributors = (stbir__contributors *) tempmem; + info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); + info->vertical_contributors = + STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); + info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); + info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); + + if (stbir__use_height_upsampling(info)) + { + info->horizontal_buffer = NULL; + info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); + + STBIR_ASSERT((size_t) STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == + (size_t) tempmem + tempmem_size_in_bytes); + } + else + { + info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); + info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); + info->encode_buffer = NULL; + + STBIR_ASSERT((size_t) STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == + (size_t) tempmem + tempmem_size_in_bytes); + } + +# undef STBIR__NEXT_MEMPTR + + // This signals that the ring buffer is empty + info->ring_buffer_begin_index = -1; + + stbir__calculate_filters(info->horizontal_contributors, info->horizontal_coefficients, + info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, + info->input_w, info->output_w); + stbir__calculate_filters(info->vertical_contributors, info->vertical_coefficients, + info->vertical_filter, info->vertical_scale, info->vertical_shift, + info->input_h, info->output_h); + + STBIR_PROGRESS_REPORT(0); + + if (stbir__use_height_upsampling(info)) + stbir__buffer_loop_upsample(info); + else + stbir__buffer_loop_downsample(info); + + STBIR_PROGRESS_REPORT(1); + +# ifdef STBIR_DEBUG_OVERWRITE_TEST + STBIR_ASSERT(memcmp(overwrite_output_before_pre, + &((unsigned char *) output_data)[-OVERWRITE_ARRAY_SIZE], + OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char *) output_data)[begin_forbidden], + OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, + &((unsigned char *) tempmem)[-OVERWRITE_ARRAY_SIZE], + OVERWRITE_ARRAY_SIZE) == 0); + STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, + &((unsigned char *) tempmem)[tempmem_size_in_bytes], + OVERWRITE_ARRAY_SIZE) == 0); +# endif + + return 1; +} + +static int stbir__resize_arbitrary(void *alloc_context, const void *input_data, int input_w, + int input_h, int input_stride_in_bytes, void *output_data, + int output_w, int output_h, int output_stride_in_bytes, float s0, + float t0, float s1, float t1, float *transform, int channels, + int alpha_channel, stbir_uint32 flags, stbir_datatype type, + stbir_filter h_filter, stbir_filter v_filter, + stbir_edge edge_horizontal, stbir_edge edge_vertical, + stbir_colorspace colorspace) +{ + stbir__info info; + int result; + size_t memory_required; + void *extra_memory; + + stbir__setup(&info, input_w, input_h, output_w, output_h, channels); + stbir__calculate_transform(&info, s0, t0, s1, t1, transform); + stbir__choose_filter(&info, h_filter, v_filter); + memory_required = stbir__calculate_memory(&info); + extra_memory = STBIR_MALLOC(memory_required, alloc_context); + + if (!extra_memory) + return 0; - result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, - output_data, output_stride_in_bytes, - alpha_channel, flags, type, - edge_horizontal, edge_vertical, - colorspace, extra_memory, memory_required); + result = stbir__resize_allocated( + &info, input_data, input_stride_in_bytes, output_data, output_stride_in_bytes, alpha_channel, + flags, type, edge_horizontal, edge_vertical, colorspace, extra_memory, memory_required); - STBIR_FREE(extra_memory, alloc_context); + STBIR_FREE(extra_memory, alloc_context); - return result; + return result; } -STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels) +STBIRDEF int stbir_resize_uint8(const unsigned char *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, unsigned char *output_pixels, + int output_w, int output_h, int output_stride_in_bytes, + int num_channels) { - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, 0, 0, 1, + 1, NULL, num_channels, -1, 0, STBIR_TYPE_UINT8, + STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_EDGE_CLAMP, + STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); } -STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels) +STBIRDEF int stbir_resize_float(const float *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, float *output_pixels, int output_w, + int output_h, int output_stride_in_bytes, int num_channels) { - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, 0, 0, 1, + 1, NULL, num_channels, -1, 0, STBIR_TYPE_FLOAT, + STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_EDGE_CLAMP, + STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); } -STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, +STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, unsigned char *output_pixels, + int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags) { - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, 0, 0, 1, + 1, NULL, num_channels, alpha_channel, flags, STBIR_TYPE_UINT8, + STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, STBIR_EDGE_CLAMP, + STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); } -STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, +STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels, int input_w, + int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, + int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, stbir_edge edge_wrap_mode) { - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); + return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, 0, 0, 1, + 1, NULL, num_channels, alpha_channel, flags, STBIR_TYPE_UINT8, + STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, edge_wrap_mode, + edge_wrap_mode, STBIR_COLORSPACE_SRGB); } -STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) +STBIRDEF int stbir_resize_uint8_generic(const unsigned char *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, unsigned char *output_pixels, + int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, + stbir_colorspace space, void *alloc_context) { - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); + return stbir__resize_arbitrary( + alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, + output_h, output_stride_in_bytes, 0, 0, 1, 1, NULL, num_channels, alpha_channel, flags, + STBIR_TYPE_UINT8, filter, filter, edge_wrap_mode, edge_wrap_mode, space); } - -STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, +STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, stbir_uint16 *output_pixels, + int output_w, int output_h, int output_stride_in_bytes, int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); -} - - -STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - - -STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float x_scale, float y_scale, + stbir_edge edge_wrap_mode, stbir_filter filter, + stbir_colorspace space, void *alloc_context) +{ + return stbir__resize_arbitrary( + alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, + output_h, output_stride_in_bytes, 0, 0, 1, 1, NULL, num_channels, alpha_channel, flags, + STBIR_TYPE_UINT16, filter, filter, edge_wrap_mode, edge_wrap_mode, space); +} + +STBIRDEF int stbir_resize_float_generic(const float *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, float *output_pixels, + int output_w, int output_h, int output_stride_in_bytes, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_wrap_mode, stbir_filter filter, + stbir_colorspace space, void *alloc_context) +{ + return stbir__resize_arbitrary( + alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, output_pixels, output_w, + output_h, output_stride_in_bytes, 0, 0, 1, 1, NULL, num_channels, alpha_channel, flags, + STBIR_TYPE_FLOAT, filter, filter, edge_wrap_mode, edge_wrap_mode, space); +} + +STBIRDEF int stbir_resize(const void *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, void *output_pixels, int output_w, + int output_h, int output_stride_in_bytes, stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, + input_stride_in_bytes, output_pixels, output_w, output_h, + output_stride_in_bytes, 0, 0, 1, 1, NULL, num_channels, + alpha_channel, flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +STBIRDEF int stbir_resize_subpixel(const void *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, void *output_pixels, int output_w, + int output_h, int output_stride_in_bytes, + stbir_datatype datatype, int num_channels, int alpha_channel, + int flags, stbir_edge edge_mode_horizontal, + stbir_edge edge_mode_vertical, stbir_filter filter_horizontal, + stbir_filter filter_vertical, stbir_colorspace space, + void *alloc_context, float x_scale, float y_scale, float x_offset, float y_offset) { - float transform[4]; - transform[0] = x_scale; - transform[1] = y_scale; - transform[2] = x_offset; - transform[3] = y_offset; - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - -STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float s0, float t0, float s1, float t1) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - -#endif // STB_IMAGE_RESIZE_IMPLEMENTATION + float transform[4]; + transform[0] = x_scale; + transform[1] = y_scale; + transform[2] = x_offset; + transform[3] = y_offset; + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, + input_stride_in_bytes, output_pixels, output_w, output_h, + output_stride_in_bytes, 0, 0, 1, 1, transform, num_channels, + alpha_channel, flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +STBIRDEF int stbir_resize_region(const void *input_pixels, int input_w, int input_h, + int input_stride_in_bytes, void *output_pixels, int output_w, + int output_h, int output_stride_in_bytes, stbir_datatype datatype, + int num_channels, int alpha_channel, int flags, + stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, + stbir_filter filter_horizontal, stbir_filter filter_vertical, + stbir_colorspace space, void *alloc_context, float s0, float t0, + float s1, float t1) +{ + return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, + input_stride_in_bytes, output_pixels, output_w, output_h, + output_stride_in_bytes, s0, t0, s1, t1, NULL, num_channels, + alpha_channel, flags, datatype, filter_horizontal, filter_vertical, + edge_mode_horizontal, edge_mode_vertical, space); +} + +#endif // STB_IMAGE_RESIZE_IMPLEMENTATION /* ------------------------------------------------------------------------------ diff --git a/ashura/include/ashura/string.h b/ashura/include/ashura/string.h index 8724fb369..23dbe10c3 100644 --- a/ashura/include/ashura/string.h +++ b/ashura/include/ashura/string.h @@ -11,13 +11,15 @@ using utf8_codepoint = u32; enum class StringEncoding : u8 { Ascii = 0, - Utf8 = 1 + Utf8 = 1 }; struct ASCIIString -{}; +{ +}; struct ASCIIStringView -{}; +{ +}; struct StringView { @@ -30,7 +32,8 @@ struct StringView struct String { String(char const *static_string_literal) - {} + { + } usize ncodepoints = 0; usize size = 0; }; diff --git a/ashura/include/ashura/subsystem.h b/ashura/include/ashura/subsystem.h index a22e27d39..ecffa81ce 100644 --- a/ashura/include/ashura/subsystem.h +++ b/ashura/include/ashura/subsystem.h @@ -19,13 +19,16 @@ struct Subsystem } virtual constexpr void on_startup(Context &ctx) - {} + { + } virtual constexpr void tick(Context &ctx, std::chrono::nanoseconds interval) - {} + { + } virtual constexpr void on_exit(Context &ctx) - {} + { + } virtual constexpr std::string_view get_name() { @@ -33,7 +36,8 @@ struct Subsystem } virtual constexpr ~Subsystem() - {} + { + } }; } // namespace ash diff --git a/ashura/include/ashura/subsystems/http_client.h b/ashura/include/ashura/subsystems/http_client.h index 251212549..49aea8fa3 100644 --- a/ashura/include/ashura/subsystems/http_client.h +++ b/ashura/include/ashura/subsystems/http_client.h @@ -95,7 +95,7 @@ struct HttpProgressMonitorState STX_DEFAULT_CONSTRUCTOR(HttpProgressMonitorState) STX_MAKE_PINNED(HttpProgressMonitorState) - HttpProgress progress; + HttpProgress progress; stx::SpinLock lock; HttpProgress load() @@ -137,8 +137,7 @@ inline stx::Result, stx::All HttpProgressMonitor progressMonitor{state.share()}; - return stx::Ok(std::make_pair(std::move(progressMonitor), - HttpProgressUpdater{std::move(state)})); + return stx::Ok(std::make_pair(std::move(progressMonitor), HttpProgressUpdater{std::move(state)})); } struct HttpCurlMultiHandleImpl; @@ -164,9 +163,10 @@ inline auto make_curl_multi_handle(stx::Allocator allocator) CURLM *multi = curl_multi_init(); if (multi == nullptr) { - stx::panic("unexpected error from curl"); // Panic if initialization fails + stx::panic("unexpected error from curl"); // Panic if initialization fails } - return stx::rc::make_inplace(allocator, multi); // Create and return the HttpCurlMultiHandle + return stx::rc::make_inplace( + allocator, multi); // Create and return the HttpCurlMultiHandle } struct HttpCurlEasyHandleImpl; @@ -178,7 +178,7 @@ struct HttpCurlEasyHandle STX_DISABLE_MOVE(HttpCurlEasyHandle) HttpCurlEasyHandle(CURL *easy_easy, curl_slist *easy_header, - stx::Rc easy_parent); + stx::Rc easy_parent); friend struct HttpTask; friend struct HttpClient; @@ -189,12 +189,12 @@ struct HttpCurlEasyHandle struct HttpTaskInfo { - stx::Rc easy; - stx::Vec header; - stx::Vec content; - stx::Promise promise; - HttpProgressUpdater updater; - stx::FutureStatus last_status_poll = stx::FutureStatus::Executing; + stx::Rc easy; + stx::Vec header; + stx::Vec content; + stx::Promise promise; + HttpProgressUpdater updater; + stx::FutureStatus last_status_poll = stx::FutureStatus::Executing; }; struct HttpTaskImpl; @@ -203,8 +203,9 @@ struct HttpTask { stx::Unique info; - static stx::Result, stx::AllocError> prepare_request( - stx::Allocator allocator, stx::Rc const &parent, HttpRequest const &request); + static stx::Result, stx::AllocError> + prepare_request(stx::Allocator allocator, stx::Rc const &parent, + HttpRequest const &request); static void begin_request(CURL *easy, CURLM *multi, HttpTaskInfo *info_addr); @@ -216,7 +217,8 @@ struct HttpTask static stx::Result>, stx::AllocError> - launch(stx::Allocator allocator, HttpRequest const &request, stx::Rc const &parent); + launch(stx::Allocator allocator, HttpRequest const &request, + stx::Rc const &parent); void finish(stx::Allocator allocator); }; @@ -230,18 +232,17 @@ struct HttpClient : public Subsystem tasks_{allocator}, lock_{}, allocator_{allocator} - {} + { + } - std::tuple, HttpProgressMonitor> get( - stx::String url, std::map header = {}, - u32 max_redirects = 69) + std::tuple, HttpProgressMonitor> + get(stx::String url, std::map header = {}, u32 max_redirects = 69) { stx::LockGuard guard{lock_}; auto [task, monitor, future] = - HttpTask::launch(allocator_, - HttpRequest{std::move(url), std::move(header), HttpMethod::Get, - max_redirects}, - multi_) + HttpTask::launch( + allocator_, + HttpRequest{std::move(url), std::move(header), HttpMethod::Get, max_redirects}, multi_) .unwrap(); tasks_.push(std::move(task)).unwrap(); @@ -249,16 +250,14 @@ struct HttpClient : public Subsystem return std::make_tuple(std::move(future), std::move(monitor)); } - std::tuple, HttpProgressMonitor> head( - stx::String url, std::map header = {}, - u32 max_redirects = 69) + std::tuple, HttpProgressMonitor> + head(stx::String url, std::map header = {}, u32 max_redirects = 69) { stx::LockGuard guard{lock_}; auto [task, monitor, future] = - HttpTask::launch(allocator_, - HttpRequest{std::move(url), std::move(header), - HttpMethod::Head, max_redirects}, - multi_) + HttpTask::launch( + allocator_, + HttpRequest{std::move(url), std::move(header), HttpMethod::Head, max_redirects}, multi_) .unwrap(); tasks_.push(std::move(task)).unwrap(); @@ -267,7 +266,8 @@ struct HttpClient : public Subsystem } virtual constexpr void on_startup(Context &ctx) override - {} + { + } virtual void tick(Context &ctx, std::chrono::nanoseconds interval) override; @@ -277,13 +277,13 @@ struct HttpClient : public Subsystem } virtual constexpr void on_exit(Context &ctx) override - {} + { + } stx::Rc multi_; stx::Vec tasks_; - stx::SpinLock lock_; - stx::Allocator allocator_; + stx::SpinLock lock_; + stx::Allocator allocator_; }; - } // namespace ash \ No newline at end of file diff --git a/ashura/include/ashura/subsystems/image_loader.h b/ashura/include/ashura/subsystems/image_loader.h index 58ef9d120..dcb128b7b 100644 --- a/ashura/include/ashura/subsystems/image_loader.h +++ b/ashura/include/ashura/subsystems/image_loader.h @@ -7,8 +7,8 @@ #include "ashura/context.h" #include "ashura/image.h" #include "ashura/image_decoder.h" -#include "ashura/subsystem.h" #include "ashura/primitives.h" +#include "ashura/subsystem.h" #include "stx/async.h" #include "stx/memory.h" #include "stx/result.h" @@ -28,10 +28,12 @@ struct ImageLoader : public Subsystem } virtual constexpr void tick(Context &ctx, std::chrono::nanoseconds interval) override - {} + { + } virtual constexpr void on_exit(Context &ctx) override - {} + { + } virtual constexpr std::string_view get_name() override { @@ -39,16 +41,20 @@ struct ImageLoader : public Subsystem } virtual constexpr ~ImageLoader() override - {} + { + } stx::Future> load_from_file(std::string_view path) { ASH_LOG_INFO(ImageLoader, "Loading image from path: {}", path); return stx::sched::fn( - *task_scheduler, [path_ = stx::string::make(stx::os_allocator, path).unwrap()]() -> stx::Result { + *task_scheduler, + [path_ = stx::string::make(stx::os_allocator, path).unwrap()]() + -> stx::Result { if (!std::filesystem::exists(path_.view())) { - ASH_LOG_ERR(ImageLoader, "Failed to load image from path: {}, path does not exist", path_.view()); + ASH_LOG_ERR(ImageLoader, "Failed to load image from path: {}, path does not exist", + path_.view()); return stx::Err(ImageLoadError::InvalidPath); } @@ -68,15 +74,20 @@ struct ImageLoader : public Subsystem ASH_CHECK(std::fclose(file) == 0); - stx::Result result = decode_image(stx::Span{AS(u8 const *, memory.handle), AS(usize, file_size)}); + stx::Result result = + decode_image(stx::Span{AS(u8 const *, memory.handle), AS(usize, file_size)}); if (result.is_ok()) { - ASH_LOG_INFO(ImageLoader, "Loaded and decoded {}x{} image at path: {} with size={} bytes", result.value().extent.width, result.value().extent.height, path_.view(), result.value().span().size()); + ASH_LOG_INFO(ImageLoader, + "Loaded and decoded {}x{} image at path: {} with size={} bytes", + result.value().extent.width, result.value().extent.height, path_.view(), + result.value().span().size()); } else { - ASH_LOG_ERR(ImageLoader, "Failed to decode image at path: {}, error: {}", path_.view(), (i64) result.err()); + ASH_LOG_ERR(ImageLoader, "Failed to decode image at path: {}, error: {}", path_.view(), + (i64) result.err()); } return result; diff --git a/ashura/include/ashura/subsystems/image_manager.h b/ashura/include/ashura/subsystems/image_manager.h index 0af22a83d..f3caa9685 100644 --- a/ashura/include/ashura/subsystems/image_manager.h +++ b/ashura/include/ashura/subsystems/image_manager.h @@ -14,13 +14,16 @@ namespace ash struct ImageManager : public Subsystem { virtual constexpr void on_startup(Context &ctx) override - {} + { + } virtual constexpr void tick(Context &ctx, std::chrono::nanoseconds interval) override - {} + { + } virtual constexpr void on_exit(Context &ctx) override - {} + { + } virtual constexpr std::string_view get_name() override { @@ -28,7 +31,8 @@ struct ImageManager : public Subsystem } virtual constexpr ~ImageManager() override - {} + { + } virtual gfx::image add(ImageView view, bool is_real_time) { diff --git a/ashura/include/ashura/subsystems/vulkan_image_manager.h b/ashura/include/ashura/subsystems/vulkan_image_manager.h index 0204315e7..c234a630f 100644 --- a/ashura/include/ashura/subsystems/vulkan_image_manager.h +++ b/ashura/include/ashura/subsystems/vulkan_image_manager.h @@ -23,21 +23,25 @@ namespace ash struct VulkanImageManager : public ImageManager { - VulkanImageManager(vk::RenderResourceManager &imgr) : - mgr{&imgr} - {} + VulkanImageManager(vk::RenderResourceManager &imgr) : mgr{&imgr} + { + } virtual constexpr void on_startup(Context &ctx) override - {} + { + } virtual constexpr void tick(Context &ctx, std::chrono::nanoseconds interval) override - {} + { + } virtual constexpr void on_exit(Context &ctx) override - {} + { + } virtual constexpr ~VulkanImageManager() override - {} + { + } virtual gfx::image add(ImageView view, bool is_real_time) override { diff --git a/ashura/include/ashura/svg.h b/ashura/include/ashura/svg.h index be79525d2..9cfd6fedd 100644 --- a/ashura/include/ashura/svg.h +++ b/ashura/include/ashura/svg.h @@ -1,4 +1,3 @@ - // TODO(lamarrr): request v/h DPI for knowing vertical and horizontal DPIs to use for the point intervals \ No newline at end of file diff --git a/ashura/include/ashura/text.h b/ashura/include/ashura/text.h index 679f0e87c..7b7d101e3 100644 --- a/ashura/include/ashura/text.h +++ b/ashura/include/ashura/text.h @@ -31,32 +31,35 @@ enum class TextDirection : u8 struct TextStyle { - std::string_view font = {}; // name to use to match the font. if font is not found or empty the fallback fonts are tried. - stx::Span fallback_fonts = {}; // font to fallback to if {font} is not available. if none of the specified fallback fonts are found the first font in the font bundle will be used - f32 font_height = 20; // px - Color foreground_color = colors::BLACK; // - Color outline_color = colors::BLACK; // - f32 outline_thickness = 0; // - Color shadow_color = colors::BLACK; // - f32 shadow_scale = 0; // relative. multiplied by font_height - Vec2 shadow_offset = Vec2{0, 0}; // px. offset from center of glyph - Color background_color = colors::TRANSPARENT; // - Color underline_color = colors::BLACK; // - f32 underline_thickness = 0; // px - Color strikethrough_color = colors::BLACK; // - f32 strikethrough_thickness = 0; // px - f32 letter_spacing = 0; // px. additional letter spacing, can be negative - f32 word_spacing = 0; // px. additional word spacing, can be negative - f32 line_height = 1.2f; // relative. multiplied by font_height - bool use_kerning = true; // use provided font kerning - bool use_ligatures = true; // use standard and contextual font ligature substitution + std::string_view font = + {}; // name to use to match the font. if font is not found or empty the fallback fonts are tried. + stx::Span fallback_fonts = + {}; // font to fallback to if {font} is not available. if none of the specified fallback fonts are found the first font in the font bundle will be used + f32 font_height = 20; // px + Color foreground_color = colors::BLACK; // + Color outline_color = colors::BLACK; // + f32 outline_thickness = 0; // + Color shadow_color = colors::BLACK; // + f32 shadow_scale = 0; // relative. multiplied by font_height + Vec2 shadow_offset = Vec2{0, 0}; // px. offset from center of glyph + Color background_color = colors::TRANSPARENT; // + Color underline_color = colors::BLACK; // + f32 underline_thickness = 0; // px + Color strikethrough_color = colors::BLACK; // + f32 strikethrough_thickness = 0; // px + f32 letter_spacing = 0; // px. additional letter spacing, can be negative + f32 word_spacing = 0; // px. additional word spacing, can be negative + f32 line_height = 1.2f; // relative. multiplied by font_height + bool use_kerning = true; // use provided font kerning + bool use_ligatures = true; // use standard and contextual font ligature substitution }; /// A text run is a sequence of characters sharing a single property. /// i.e. foreground color, font etc. struct TextRun { - usize size = 0; // byte size coverage of this run. i.e. for the first run with size 20 all text within [0, 20] bytes range of the text will be styled using this run + usize size = + 0; // byte size coverage of this run. i.e. for the first run with size 20 all text within [0, 20] bytes range of the text will be styled using this run usize style = 0; // run style to use }; @@ -75,38 +78,42 @@ enum class TextOverflow struct TextBlock { - std::string_view text; // utf-8-encoded text, Span because string view doesnt support non-string types - stx::Span runs; // parts of text not styled by a run will use the paragraphs run style - stx::Span styles; // styles for the text block's contents - TextStyle default_style; // default run styling + std::string_view + text; // utf-8-encoded text, Span because string view doesnt support non-string types + stx::Span + runs; // parts of text not styled by a run will use the paragraphs run style + stx::Span styles; // styles for the text block's contents + TextStyle default_style; // default run styling TextAlign align = TextAlign::Start; // text alignment TextDirection direction = TextDirection::LeftToRight; // base text direction - std::string_view language = {}; // base language to use for selecting opentype features to used on the text, uses default if not set + std::string_view language = + {}; // base language to use for selecting opentype features to used on the text, uses default if not set }; /// RunSegment is a part of a text run split by groups of spacing characters word contained in a run. /// The spacing characters translate to break opportunities. struct TextRunSegment { - bool has_spacing = false; // if it has trailing spacing characters (tabs and spaces) where we can break the text, this corresponds to the unicode Break-After (BA) - stx::Span text = {}; // utf8 text of segment - TextDirection direction = TextDirection::LeftToRight; // direction of text - usize style = 0; // resolved run text styling - usize font = 0; // resolved font index in font bundle + bool has_spacing = + false; // if it has trailing spacing characters (tabs and spaces) where we can break the text, this corresponds to the unicode Break-After (BA) + stx::Span text = {}; // utf8 text of segment + TextDirection direction = TextDirection::LeftToRight; // direction of text + usize style = 0; // resolved run text styling + usize font = 0; // resolved font index in font bundle usize glyph_shapings_offset = 0; usize nglyph_shapings = 0; - f32 width = 0; // sum of advances, letter spacing & word spacing + f32 width = 0; // sum of advances, letter spacing & word spacing }; struct LineMetrics { - f32 width = 0; // width of the line - f32 ascent = 0; // maximum ascent of all the runs on the line - f32 descent = 0; // maximum descent of all the runs on the line - f32 line_height = 0; // maximum line height of all the runs on the line - TextDirection base_direction = TextDirection::LeftToRight; // base direction of the line - usize run_segments_offset = 0; // begin index of line's segments - usize nrun_segments = 0; // number of segments + f32 width = 0; // width of the line + f32 ascent = 0; // maximum ascent of all the runs on the line + f32 descent = 0; // maximum descent of all the runs on the line + f32 line_height = 0; // maximum line height of all the runs on the line + TextDirection base_direction = TextDirection::LeftToRight; // base direction of the line + usize run_segments_offset = 0; // begin index of line's segments + usize nrun_segments = 0; // number of segments }; struct GlyphShaping @@ -126,37 +133,49 @@ struct TextLayout Vec2 span; f32 text_scale_factor = 0; - static std::pair, stx::Span> shape_text_harfbuzz( - Font const &font, f32 render_font_height, u32 not_found_glyph, stx::Span text, - hb_buffer_t *shaping_buffer, hb_script_t script, hb_direction_t direction, - hb_language_t language, bool use_kerning, bool use_ligatures) + static std::pair, stx::Span> + shape_text_harfbuzz(Font const &font, f32 render_font_height, u32 not_found_glyph, + stx::Span text, hb_buffer_t *shaping_buffer, + hb_script_t script, hb_direction_t direction, hb_language_t language, + bool use_kerning, bool use_ligatures) { // tags are opentype feature tags - hb_feature_t const shaping_features[] = {{.tag = HB_TAG('k', 'e', 'r', 'n'), // kerning operations - .value = use_kerning, - .start = HB_FEATURE_GLOBAL_START, - .end = HB_FEATURE_GLOBAL_END}, - {.tag = HB_TAG('l', 'i', 'g', 'a'), // standard ligature glyph substitution - .value = use_ligatures, - .start = HB_FEATURE_GLOBAL_START, - .end = HB_FEATURE_GLOBAL_END}, - {.tag = HB_TAG('c', 'l', 'i', 'g'), // contextual ligature glyph substitution - .value = use_ligatures, - .start = HB_FEATURE_GLOBAL_START, - .end = HB_FEATURE_GLOBAL_END}}; + hb_feature_t const shaping_features[] = { + {.tag = HB_TAG('k', 'e', 'r', 'n'), // kerning operations + .value = use_kerning, + .start = HB_FEATURE_GLOBAL_START, + .end = HB_FEATURE_GLOBAL_END}, + {.tag = HB_TAG('l', 'i', 'g', 'a'), // standard ligature glyph substitution + .value = use_ligatures, + .start = HB_FEATURE_GLOBAL_START, + .end = HB_FEATURE_GLOBAL_END}, + {.tag = HB_TAG('c', 'l', 'i', 'g'), // contextual ligature glyph substitution + .value = use_ligatures, + .start = HB_FEATURE_GLOBAL_START, + .end = HB_FEATURE_GLOBAL_END}}; hb_buffer_reset(shaping_buffer); - hb_buffer_set_replacement_codepoint(shaping_buffer, HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT); // invalid character replacement - hb_buffer_set_not_found_glyph(shaping_buffer, not_found_glyph); // default glyphs for characters without defined glyphs - hb_buffer_set_script(shaping_buffer, script); // OpenType (ISO15924) Script Tag. See: https://unicode.org/reports/tr24/#Relation_To_ISO15924 + hb_buffer_set_replacement_codepoint( + shaping_buffer, + HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT); // invalid character replacement + hb_buffer_set_not_found_glyph( + shaping_buffer, + not_found_glyph); // default glyphs for characters without defined glyphs + hb_buffer_set_script( + shaping_buffer, + script); // OpenType (ISO15924) Script Tag. See: https://unicode.org/reports/tr24/#Relation_To_ISO15924 hb_buffer_set_direction(shaping_buffer, direction); - hb_buffer_set_language(shaping_buffer, language); // OpenType BCP-47 language tag for performing certain shaping operations as defined in the font - hb_font_set_scale(font.hb_font, (int) (64 * render_font_height), (int) (64 * render_font_height)); + hb_buffer_set_language( + shaping_buffer, + language); // OpenType BCP-47 language tag for performing certain shaping operations as defined in the font + hb_font_set_scale(font.hb_font, (int) (64 * render_font_height), + (int) (64 * render_font_height)); hb_buffer_add_utf8(shaping_buffer, text.data(), (int) text.size(), 0, (int) text.size()); hb_shape(font.hb_font, shaping_buffer, shaping_features, (uint) std::size(shaping_features)); uint nglyph_pos; - hb_glyph_position_t const *glyph_pos = hb_buffer_get_glyph_positions(shaping_buffer, &nglyph_pos); + hb_glyph_position_t const *glyph_pos = + hb_buffer_get_glyph_positions(shaping_buffer, &nglyph_pos); ASH_CHECK(!(glyph_pos == nullptr && nglyph_pos > 0)); uint nglyph_infos; @@ -169,7 +188,8 @@ struct TextLayout } // TODO(lamarrr): remove all uses of char for utf8 text or byte strings - void layout(TextBlock const &block, f32 text_scale_factor, stx::Span const font_bundle, f32 const max_line_width) + void layout(TextBlock const &block, f32 text_scale_factor, + stx::Span const font_bundle, f32 const max_line_width) { lines.clear(); run_segments.clear(); @@ -186,7 +206,10 @@ struct TextLayout } // uses setLocale to get default language from locale which isn't thread-safe - hb_language_t const language_hb = block.language.empty() ? hb_language_get_default() : hb_language_from_string(block.language.data(), (int) block.language.size()); + hb_language_t const language_hb = + block.language.empty() ? + hb_language_get_default() : + hb_language_from_string(block.language.data(), (int) block.language.size()); hb_buffer_t *const shaping_buffer = hb_buffer_create(); ASH_CHECK(shaping_buffer != nullptr); @@ -198,16 +221,13 @@ struct TextLayout { usize font = match_font(style.font, style.fallback_fonts, font_bundle); // use first font in the font bundle if specified font and fallback fonts are not found - resolved_fonts - .push(font < font_bundle.size() ? font : 0) - .unwrap(); + resolved_fonts.push(font < font_bundle.size() ? font : 0).unwrap(); } { - usize font = match_font(block.default_style.font, block.default_style.fallback_fonts, font_bundle); - resolved_fonts - .push(font < font_bundle.size() ? font : 0) - .unwrap(); + usize font = + match_font(block.default_style.font, block.default_style.fallback_fonts, font_bundle); + resolved_fonts.push(font < font_bundle.size() ? font : 0).unwrap(); } SBCodepointSequence const codepoint_sequence{.stringEncoding = SBStringEncodingUTF8, @@ -233,34 +253,48 @@ struct TextLayout /// Paragraph Breaking /// for (SBUInteger paragraph_begin = 0; paragraph_begin < block.text.size();) { - SBParagraphRef const sb_paragraph = SBAlgorithmCreateParagraph(algorithm, paragraph_begin, UINTPTR_MAX, block.direction == TextDirection::LeftToRight ? (SBLevel) SBLevelDefaultLTR : (SBLevel) SBLevelDefaultRTL); + SBParagraphRef const sb_paragraph = SBAlgorithmCreateParagraph( + algorithm, paragraph_begin, UINTPTR_MAX, + block.direction == TextDirection::LeftToRight ? (SBLevel) SBLevelDefaultLTR : + (SBLevel) SBLevelDefaultRTL); ASH_CHECK(sb_paragraph != nullptr); - SBUInteger const paragraph_length = SBParagraphGetLength(sb_paragraph); // number of bytes of the paragraph - SBLevel const paragraph_base_level = SBParagraphGetBaseLevel(sb_paragraph); // base direction, 0 - LTR, 1 - RTL - SBLevel const *const paragraph_levels = SBParagraphGetLevelsPtr(sb_paragraph); // SheenBidi expands to byte level representation of the codepoints - char const *const paragraph_text_begin = block.text.data() + paragraph_begin; - char const *const paragraph_text_end = paragraph_text_begin + paragraph_length; - usize const paragraph_run_segments_offset = run_segments.size(); + SBUInteger const paragraph_length = + SBParagraphGetLength(sb_paragraph); // number of bytes of the paragraph + SBLevel const paragraph_base_level = + SBParagraphGetBaseLevel(sb_paragraph); // base direction, 0 - LTR, 1 - RTL + SBLevel const *const paragraph_levels = SBParagraphGetLevelsPtr( + sb_paragraph); // SheenBidi expands to byte level representation of the codepoints + char const *const paragraph_text_begin = block.text.data() + paragraph_begin; + char const *const paragraph_text_end = paragraph_text_begin + paragraph_length; + usize const paragraph_run_segments_offset = run_segments.size(); for (char const *run_text_begin = paragraph_text_begin; run_text_begin < paragraph_text_end;) { char const *run_text_end = run_text_begin; usize const run_block_text_offset = (usize) (run_text_begin - block.text.data()); - SBLevel const run_level = paragraph_levels[run_text_begin - paragraph_text_begin]; + SBLevel const run_level = paragraph_levels[run_text_begin - paragraph_text_begin]; stx::utf8_next((u8 const *&) run_text_end); - while (!(run_block_text_offset >= script_agent->offset && run_block_text_offset < (script_agent->offset + script_agent->length))) [[unlikely]] - { - ASH_CHECK(SBScriptLocatorMoveNext(script_locator)); - } - SBScript const run_script = script_agent->script; - TextDirection const run_direction = (run_level & 0x1) == 0 ? TextDirection::LeftToRight : TextDirection::RightToLeft; - hb_direction_t const run_direction_hb = (run_level & 0x1) == 0 ? HB_DIRECTION_LTR : HB_DIRECTION_RTL; - hb_script_t const run_script_hb = hb_script_from_iso15924_tag(SBScriptGetOpenTypeTag(run_script)); // Note that unicode scripts are different from OpenType (iso15924) scripts though they are similar - usize irun_style = block.styles.size(); // find the style intended for this text run (if any, otherwise default) + while (!(run_block_text_offset >= script_agent->offset && + run_block_text_offset < (script_agent->offset + script_agent->length))) + [[unlikely]] + { + ASH_CHECK(SBScriptLocatorMoveNext(script_locator)); + } + SBScript const run_script = script_agent->script; + TextDirection const run_direction = + (run_level & 0x1) == 0 ? TextDirection::LeftToRight : TextDirection::RightToLeft; + hb_direction_t const run_direction_hb = + (run_level & 0x1) == 0 ? HB_DIRECTION_LTR : HB_DIRECTION_RTL; + hb_script_t const run_script_hb = hb_script_from_iso15924_tag(SBScriptGetOpenTypeTag( + run_script)); // Note that unicode scripts are different from OpenType (iso15924) scripts though they are similar + usize irun_style = + block.styles + .size(); // find the style intended for this text run (if any, otherwise default) while (run_it < block.runs.end()) { - if (run_block_text_offset >= style_text_offset && run_block_text_offset < (style_text_offset + run_it->size)) [[likely]] + if (run_block_text_offset >= style_text_offset && + run_block_text_offset < (style_text_offset + run_it->size)) [[likely]] { irun_style = run_it->style; break; @@ -272,8 +306,9 @@ struct TextLayout } } - TextStyle const &run_style = irun_style >= block.styles.size() ? block.default_style : block.styles[irun_style]; - usize const run_font = resolved_fonts[irun_style]; + TextStyle const &run_style = + irun_style >= block.styles.size() ? block.default_style : block.styles[irun_style]; + usize const run_font = resolved_fonts[irun_style]; // find the last codepoint that belongs to this text run while (run_text_end < paragraph_text_end) @@ -282,11 +317,14 @@ struct TextLayout SBLevel const level = paragraph_levels[run_text_end - paragraph_text_begin]; char const *p_next_codepoint = run_text_end; stx::utf8_next((u8 const *&) p_next_codepoint); - usize istyle = block.styles.size(); // find the style intended for this code point (if any, otherwise default) + usize istyle = + block.styles + .size(); // find the style intended for this code point (if any, otherwise default) while (run_it < block.runs.end()) { - if (block_text_offset >= style_text_offset && block_text_offset < (style_text_offset + run_it->size)) [[likely]] + if (block_text_offset >= style_text_offset && + block_text_offset < (style_text_offset + run_it->size)) [[likely]] { istyle = run_it->style; break; @@ -298,7 +336,9 @@ struct TextLayout } } - bool const is_in_script_run = (block_text_offset >= script_agent->offset) && (block_text_offset < (script_agent->offset + script_agent->length)); + bool const is_in_script_run = + (block_text_offset >= script_agent->offset) && + (block_text_offset < (script_agent->offset + script_agent->length)); if (level != run_level || !is_in_script_run || istyle != irun_style) [[unlikely]] { @@ -334,14 +374,16 @@ struct TextLayout segment_text_end = p_next_codepoint; } - stx::Span const segment_text{segment_text_begin, (usize) (segment_text_end - segment_text_begin)}; + stx::Span const segment_text{segment_text_begin, + (usize) (segment_text_end - segment_text_begin)}; - auto const [glyph_infos, glyph_positions] = shape_text_harfbuzz(*font_bundle[run_font].font, font_bundle[run_font].atlas.font_height, - font_bundle[run_font].atlas.space_glyph, segment_text, - shaping_buffer, run_script_hb, run_direction_hb, - language_hb, run_style.use_kerning, run_style.use_ligatures); + auto const [glyph_infos, glyph_positions] = shape_text_harfbuzz( + *font_bundle[run_font].font, font_bundle[run_font].atlas.font_height, + font_bundle[run_font].atlas.space_glyph, segment_text, shaping_buffer, run_script_hb, + run_direction_hb, language_hb, run_style.use_kerning, run_style.use_ligatures); - f32 const scale = ((f32) run_style.font_height * text_scale_factor) / (f32) font_bundle[run_font].atlas.font_height; + f32 const scale = ((f32) run_style.font_height * text_scale_factor) / + (f32) font_bundle[run_font].atlas.font_height; f32 segment_width = 0; @@ -350,9 +392,15 @@ struct TextLayout for (usize i = 0; i < glyph_infos.size(); i++) { f32 const advance = scale * (f32) glyph_positions[i].x_advance / 64.0f; - Vec2 const offset = scale * Vec2{(f32) glyph_positions[i].x_offset / 64.0f, (f32) glyph_positions[i].y_offset / -64.0f}; + Vec2 const offset = scale * Vec2{(f32) glyph_positions[i].x_offset / 64.0f, + (f32) glyph_positions[i].y_offset / -64.0f}; - glyph_shapings.push(GlyphShaping{.glyph = glyph_infos[i].codepoint, .cluster = glyph_infos[i].cluster, .advance = advance, .offset = offset}).unwrap(); + glyph_shapings + .push(GlyphShaping{.glyph = glyph_infos[i].codepoint, + .cluster = glyph_infos[i].cluster, + .advance = advance, + .offset = offset}) + .unwrap(); segment_width += advance + text_scale_factor * run_style.letter_spacing; } @@ -361,14 +409,15 @@ struct TextLayout segment_width += has_spacing ? (text_scale_factor * run_style.word_spacing) : 0; - run_segments.push(TextRunSegment{.has_spacing = has_spacing, - .text = segment_text, - .direction = run_direction, - .style = irun_style, - .font = run_font, - .glyph_shapings_offset = glyph_shapings_offset, - .nglyph_shapings = nglyph_shapings, - .width = segment_width}) + run_segments + .push(TextRunSegment{.has_spacing = has_spacing, + .text = segment_text, + .direction = run_direction, + .style = irun_style, + .font = run_font, + .glyph_shapings_offset = glyph_shapings_offset, + .nglyph_shapings = nglyph_shapings, + .width = segment_width}) .unwrap(); segment_text_begin = segment_text_end; @@ -379,11 +428,14 @@ struct TextLayout SBParagraphRelease(sb_paragraph); - usize const nparagraph_run_segments = run_segments.size() - paragraph_run_segments_offset; - TextRunSegment *const paragraph_run_segments_begin = run_segments.data() + paragraph_run_segments_offset; - TextRunSegment *const paragraph_run_segments_end = paragraph_run_segments_begin + nparagraph_run_segments; + usize const nparagraph_run_segments = run_segments.size() - paragraph_run_segments_offset; + TextRunSegment *const paragraph_run_segments_begin = + run_segments.data() + paragraph_run_segments_offset; + TextRunSegment *const paragraph_run_segments_end = + paragraph_run_segments_begin + nparagraph_run_segments; - for (TextRunSegment *line_begin = paragraph_run_segments_begin; line_begin < paragraph_run_segments_end;) + for (TextRunSegment *line_begin = paragraph_run_segments_begin; + line_begin < paragraph_run_segments_end;) { TextRunSegment *line_end = line_begin; f32 line_width = 0; @@ -452,31 +504,39 @@ struct TextLayout if (direction == TextDirection::RightToLeft) { // re-order consecutive RTL segments on the line to match visual reading direction - stx::Span{direction_run_begin, (usize) (direction_run_end - direction_run_begin)}.reverse(); + stx::Span{direction_run_begin, (usize) (direction_run_end - direction_run_begin)} + .reverse(); } - for (TextRunSegment const &run_segment : stx::Span{direction_run_begin, (usize) (direction_run_end - direction_run_begin)}) + for (TextRunSegment const &run_segment : + stx::Span{direction_run_begin, (usize) (direction_run_end - direction_run_begin)}) { - TextStyle const &style = run_segment.style >= block.styles.size() ? block.default_style : block.styles[run_segment.style]; - f32 const ascent = text_scale_factor * font_bundle[run_segment.font].atlas.ascent * style.font_height; - f32 const descent = text_scale_factor * font_bundle[run_segment.font].atlas.descent * style.font_height; - f32 const height = ascent + descent; - line_height = std::max(line_height, style.line_height * height); - line_ascent = std::max(line_ascent, ascent); - line_descent = std::max(line_descent, descent); + TextStyle const &style = run_segment.style >= block.styles.size() ? + block.default_style : + block.styles[run_segment.style]; + f32 const ascent = + text_scale_factor * font_bundle[run_segment.font].atlas.ascent * style.font_height; + f32 const descent = + text_scale_factor * font_bundle[run_segment.font].atlas.descent * style.font_height; + f32 const height = ascent + descent; + line_height = std::max(line_height, style.line_height * height); + line_ascent = std::max(line_ascent, ascent); + line_descent = std::max(line_descent, descent); } direction_run_begin = direction_run_end; } - lines.push_inplace(LineMetrics{ - .width = line_width, - .ascent = line_ascent, - .descent = line_descent, - .line_height = line_height, - .base_direction = (paragraph_base_level & 0x1) == 0 ? TextDirection::LeftToRight : TextDirection::RightToLeft, - .run_segments_offset = (usize) (line_begin - run_segments.data()), - .nrun_segments = (usize) (line_end - line_begin)}) + lines + .push_inplace(LineMetrics{ + .width = line_width, + .ascent = line_ascent, + .descent = line_descent, + .line_height = line_height, + .base_direction = (paragraph_base_level & 0x1) == 0 ? TextDirection::LeftToRight : + TextDirection::RightToLeft, + .run_segments_offset = (usize) (line_begin - run_segments.data()), + .nrun_segments = (usize) (line_end - line_begin)}) .unwrap(); span.y += line_height; diff --git a/ashura/include/ashura/utils.h b/ashura/include/ashura/utils.h index 93a5ebcd0..4c0895a69 100644 --- a/ashura/include/ashura/utils.h +++ b/ashura/include/ashura/utils.h @@ -38,8 +38,9 @@ #define AS_F32(...) AS(::ash::f32, __VA_ARGS__) #define AS_F64(...) AS(::ash::f64, __VA_ARGS__) -#define ASH_U8_CLAMP(...) \ - ((__VA_ARGS__) < 0 ? (::ash::u8) 0 : ((__VA_ARGS__) > 255 ? (::ash::u8) 255 : (::ash::u8)(__VA_ARGS__))) +#define ASH_U8_CLAMP(...) \ + ((__VA_ARGS__) < 0 ? (::ash::u8) 0 : \ + ((__VA_ARGS__) > 255 ? (::ash::u8) 255 : (::ash::u8)(__VA_ARGS__))) namespace ash { diff --git a/ashura/include/ashura/vulkan.h b/ashura/include/ashura/vulkan.h index 21c1391ff..c1d872b36 100644 --- a/ashura/include/ashura/vulkan.h +++ b/ashura/include/ashura/vulkan.h @@ -22,12 +22,13 @@ #include "vulkan/vk_enum_string_helper.h" #include "vulkan/vulkan.h" -#define ASH_VK_CHECK(...) \ - do \ - { \ - VkResult operation_result = (__VA_ARGS__); \ - ASH_CHECK(operation_result == VK_SUCCESS, "Vulkan Operation: (" #__VA_ARGS__ ") failed! (VK_SUCCESS not returned)", \ - std::string_view{string_VkResult(operation_result)}); \ +#define ASH_VK_CHECK(...) \ + do \ + { \ + VkResult operation_result = (__VA_ARGS__); \ + ASH_CHECK(operation_result == VK_SUCCESS, \ + "Vulkan Operation: (" #__VA_ARGS__ ") failed! (VK_SUCCESS not returned)", \ + std::string_view{string_VkResult(operation_result)}); \ } while (false) namespace ash @@ -63,8 +64,9 @@ inline void ensure_extensions_supported(stx::Span a ASH_LOG_INFO(Vulkan, "All required extensions are supported!"); } -inline void ensure_validation_layers_supported(stx::Span available_validation_layers, - stx::Span required_layers) +inline void ensure_validation_layers_supported( + stx::Span available_validation_layers, + stx::Span required_layers) { bool all_layers_available = true; @@ -72,7 +74,8 @@ inline void ensure_validation_layers_supported(stx::SpanpObjects; // callback_data->objectCount; if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { - ASH_LOG_ERR(Vulkan_ValidationLayer, "(Message ID: ({}) {}, type: {}) {}", callback_data->messageIdNumber, - callback_data->pMessageIdName, string_VkDebugUtilsMessageSeverityFlagsEXT(message_severity), + ASH_LOG_ERR(Vulkan_ValidationLayer, "(Message ID: ({}) {}, type: {}) {}", + callback_data->messageIdNumber, callback_data->pMessageIdName, + string_VkDebugUtilsMessageSeverityFlagsEXT(message_severity), callback_data->pMessage); } else if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { - ASH_LOG_WARN(Vulkan_ValidationLayer, "(Message ID: ({}) {}, type: {}) {}", callback_data->messageIdNumber, - callback_data->pMessageIdName, string_VkDebugUtilsMessageSeverityFlagsEXT(message_severity), + ASH_LOG_WARN(Vulkan_ValidationLayer, "(Message ID: ({}) {}, type: {}) {}", + callback_data->messageIdNumber, callback_data->pMessageIdName, + string_VkDebugUtilsMessageSeverityFlagsEXT(message_severity), callback_data->pMessage); } else { - ASH_LOG_INFO(Vulkan_ValidationLayer, "(Message ID: ({}) {}, type: {}) {}", callback_data->messageIdNumber, - callback_data->pMessageIdName, string_VkDebugUtilsMessageSeverityFlagsEXT(message_severity), + ASH_LOG_INFO(Vulkan_ValidationLayer, "(Message ID: ({}) {}, type: {}) {}", + callback_data->messageIdNumber, callback_data->pMessageIdName, + string_VkDebugUtilsMessageSeverityFlagsEXT(message_severity), callback_data->pMessage); } @@ -116,16 +123,20 @@ inline VkBool32 VKAPI_ATTR VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverity inline std::pair create_vulkan_instance(stx::Span irequired_extensions, - stx::Span required_validation_layers, char const *const application_name, - u32 application_version, char const *const engine_name, u32 engine_version) + stx::Span required_validation_layers, + char const *const application_name, u32 application_version, + char const *const engine_name, u32 engine_version) { VkDebugUtilsMessengerCreateInfoEXT debug_utils_messenger_create_info{ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, .pNext = nullptr, .flags = 0, - .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, - .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, + .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, .pfnUserCallback = debug_callback, .pUserData = nullptr}; @@ -144,13 +155,15 @@ inline std::pair u32 available_extensions_count = 0; - ASH_VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &available_extensions_count, nullptr)); + ASH_VK_CHECK( + vkEnumerateInstanceExtensionProperties(nullptr, &available_extensions_count, nullptr)); stx::Vec available_extensions; available_extensions.resize(available_extensions_count).unwrap(); - ASH_VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &available_extensions_count, available_extensions.data())); + ASH_VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &available_extensions_count, + available_extensions.data())); ASH_LOG_INFO(Vulkan, "Available Vulkan Extensions:"); @@ -167,17 +180,19 @@ inline std::pair available_validation_layers.resize(available_validation_layers_count).unwrap(); - ASH_VK_CHECK(vkEnumerateInstanceLayerProperties(&available_validation_layers_count, available_validation_layers.data())); + ASH_VK_CHECK(vkEnumerateInstanceLayerProperties(&available_validation_layers_count, + available_validation_layers.data())); ASH_LOG_INFO(Vulkan, "Available Vulkan Validation Layers:"); for (VkLayerProperties const &layer : available_validation_layers) { - ASH_LOG_INFO(Vulkan, "\t{} (spec version: {}.{}.{} api-variant-{}, implementation version: " - "{})", - layer.layerName, VK_API_VERSION_MAJOR(layer.specVersion), VK_API_VERSION_MINOR(layer.specVersion), - VK_API_VERSION_PATCH(layer.specVersion), VK_API_VERSION_VARIANT(layer.specVersion), - layer.implementationVersion); + ASH_LOG_INFO(Vulkan, + "\t{} (spec version: {}.{}.{} api-variant-{}, implementation version: " + "{})", + layer.layerName, VK_API_VERSION_MAJOR(layer.specVersion), + VK_API_VERSION_MINOR(layer.specVersion), VK_API_VERSION_PATCH(layer.specVersion), + VK_API_VERSION_VARIANT(layer.specVersion), layer.implementationVersion); } ensure_extensions_supported(available_extensions, required_extensions); @@ -197,16 +212,17 @@ inline std::pair .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // debug messenger for when // the installed debug // messenger is uninstalled. - .pNext = required_validation_layers.is_empty() ? nullptr : - &debug_utils_messenger_create_info, // this helps to debug issues - // with vkDestroyInstance and - // vkCreateInstance i.e. - // (before and after the debug - // messenger is installed) - .flags = 0, - .pApplicationInfo = &app_info, - .enabledLayerCount = AS(u32, required_validation_layers.size()), // validation layers - .ppEnabledLayerNames = required_validation_layers.data(), + .pNext = required_validation_layers.is_empty() ? + nullptr : + &debug_utils_messenger_create_info, // this helps to debug issues + // with vkDestroyInstance and + // vkCreateInstance i.e. + // (before and after the debug + // messenger is installed) + .flags = 0, + .pApplicationInfo = &app_info, + .enabledLayerCount = AS(u32, required_validation_layers.size()), // validation layers + .ppEnabledLayerNames = required_validation_layers.data(), .enabledExtensionCount = AS(u32, required_extensions.size()), .ppEnabledExtensionNames = required_extensions.data(), }; @@ -219,11 +235,15 @@ inline std::pair if (!required_validation_layers.is_empty()) { - PFN_vkCreateDebugUtilsMessengerEXT createDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); + PFN_vkCreateDebugUtilsMessengerEXT createDebugUtilsMessengerEXT = + (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr( + instance, "vkCreateDebugUtilsMessengerEXT"); - ASH_CHECK(createDebugUtilsMessengerEXT != nullptr, "unable to get procedure address for vkCreateDebugUtilsMessengerEXT"); + ASH_CHECK(createDebugUtilsMessengerEXT != nullptr, + "unable to get procedure address for vkCreateDebugUtilsMessengerEXT"); - ASH_VK_CHECK(createDebugUtilsMessengerEXT(instance, &debug_utils_messenger_create_info, nullptr, &debug_utils_messenger)); + ASH_VK_CHECK(createDebugUtilsMessengerEXT(instance, &debug_utils_messenger_create_info, nullptr, + &debug_utils_messenger)); } return std::make_pair(instance, debug_utils_messenger); @@ -241,13 +261,15 @@ inline stx::Vec get_queue_families(VkPhysicalDevice dev queue_families_properties.resize(queue_families_count).unwrap(); - vkGetPhysicalDeviceQueueFamilyProperties(dev, &queue_families_count, queue_families_properties.data()); + vkGetPhysicalDeviceQueueFamilyProperties(dev, &queue_families_count, + queue_families_properties.data()); return queue_families_properties; } -inline stx::Vec get_command_queue_support(stx::Span queue_families, - VkQueueFlagBits required_command_queue) +inline stx::Vec + get_command_queue_support(stx::Span queue_families, + VkQueueFlagBits required_command_queue) { stx::Vec supports; @@ -260,36 +282,41 @@ inline stx::Vec get_command_queue_support(stx::Span get_surface_presentation_command_queue_support(VkPhysicalDevice phy_dev, - stx::Span queue_families, - VkSurfaceKHR surface) +inline stx::Vec get_surface_presentation_command_queue_support( + VkPhysicalDevice phy_dev, stx::Span queue_families, + VkSurfaceKHR surface) { stx::Vec supports; for (u32 i = 0; i < AS(u32, queue_families.size()); i++) { VkBool32 surface_presentation_supported; - ASH_VK_CHECK(vkGetPhysicalDeviceSurfaceSupportKHR(phy_dev, i, surface, &surface_presentation_supported)); + ASH_VK_CHECK( + vkGetPhysicalDeviceSurfaceSupportKHR(phy_dev, i, surface, &surface_presentation_supported)); supports.push_inplace(surface_presentation_supported == VK_TRUE).unwrap(); } return supports; } -inline VkDevice create_logical_device(VkPhysicalDevice phy_dev, stx::Span required_extensions, - stx::Span required_validation_layers, - stx::Span command_queue_create_infos, - VkPhysicalDeviceFeatures const &required_features) +inline VkDevice + create_logical_device(VkPhysicalDevice phy_dev, + stx::Span required_extensions, + stx::Span required_validation_layers, + stx::Span command_queue_create_infos, + VkPhysicalDeviceFeatures const &required_features) { u32 available_extensions_count; - ASH_VK_CHECK(vkEnumerateDeviceExtensionProperties(phy_dev, nullptr, &available_extensions_count, nullptr)); + ASH_VK_CHECK( + vkEnumerateDeviceExtensionProperties(phy_dev, nullptr, &available_extensions_count, nullptr)); // device specific extensions stx::Vec available_device_extensions; available_device_extensions.resize(available_extensions_count).unwrap(); - ASH_VK_CHECK(vkEnumerateDeviceExtensionProperties(phy_dev, nullptr, &available_extensions_count, available_device_extensions.data())); + ASH_VK_CHECK(vkEnumerateDeviceExtensionProperties(phy_dev, nullptr, &available_extensions_count, + available_device_extensions.data())); ASH_LOG_INFO(Vulkan, "Required Device Extensions: "); @@ -297,27 +324,30 @@ inline VkDevice create_logical_device(VkPhysicalDevice phy_dev, stx::Span - create_swapchain(VkDevice dev, VkSurfaceKHR surface, VkExtent2D preferred_extent, VkSurfaceFormatKHR surface_format, u32 preferred_nbuffers, - VkPresentModeKHR present_mode, SwapChainProperties const &properties, - VkSharingMode accessing_queue_families_sharing_mode, VkImageUsageFlags image_usages, - VkCompositeAlphaFlagBitsKHR alpha_blending, VkBool32 clipped) +inline std::tuple create_swapchain( + VkDevice dev, VkSurfaceKHR surface, VkExtent2D preferred_extent, + VkSurfaceFormatKHR surface_format, u32 preferred_nbuffers, VkPresentModeKHR present_mode, + SwapChainProperties const &properties, VkSharingMode accessing_queue_families_sharing_mode, + VkImageUsageFlags image_usages, VkCompositeAlphaFlagBitsKHR alpha_blending, VkBool32 clipped) { VkExtent2D selected_extent = select_swapchain_extent(properties.capabilities, preferred_extent); @@ -496,15 +535,17 @@ inline VkMemoryRequirements get_memory_requirements(VkDevice dev, VkImage image) } // returns index of the heap on the physical device, could be RAM, SWAP, or VRAM -inline stx::Option find_suitable_memory_type(VkPhysicalDeviceMemoryProperties const &memory_properties, - VkMemoryRequirements const &memory_requirements, - VkMemoryPropertyFlags required_properties) +inline stx::Option + find_suitable_memory_type(VkPhysicalDeviceMemoryProperties const &memory_properties, + VkMemoryRequirements const &memory_requirements, + VkMemoryPropertyFlags required_properties) { // different types of memory exist within the graphics card heap memory. // this can affect performance. for (u32 i = 0; i < memory_properties.memoryTypeCount; i++) { - if ((memory_properties.memoryTypes[i].propertyFlags & required_properties) == required_properties && + if ((memory_properties.memoryTypes[i].propertyFlags & required_properties) == + required_properties && (memory_requirements.memoryTypeBits & (1 << i))) { return stx::Some(AS(u32, i)); @@ -520,7 +561,8 @@ struct Instance Instance(VkInstance ainstance, stx::Option adebug_utils_messenger) : instance{ainstance}, debug_utils_messenger{std::move(adebug_utils_messenger)} - {} + { + } STX_MAKE_PINNED(Instance) @@ -528,9 +570,12 @@ struct Instance { if (debug_utils_messenger.is_some()) { - PFN_vkDestroyDebugUtilsMessengerEXT func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); + PFN_vkDestroyDebugUtilsMessengerEXT func = + (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr( + instance, "vkDestroyDebugUtilsMessengerEXT"); - ASH_CHECK(func != nullptr, "unable to get procedure address for vkDestroyDebugUtilsMessengerEXT"); + ASH_CHECK(func != nullptr, + "unable to get procedure address for vkDestroyDebugUtilsMessengerEXT"); func(instance, debug_utils_messenger.value(), nullptr); } @@ -554,7 +599,9 @@ struct PhyDeviceInfo nfamily_properties.extend(family_properties).unwrap(); - return PhyDeviceInfo{phy_device, properties, features, memory_properties, std::move(nfamily_properties), instance.share()}; + return PhyDeviceInfo{ + phy_device, properties, features, memory_properties, std::move(nfamily_properties), + instance.share()}; } bool has_geometry_shader() const @@ -564,19 +611,22 @@ struct PhyDeviceInfo bool has_transfer_command_queue_family() const { - return family_properties.span().is_any( - [](VkQueueFamilyProperties const &prop) -> bool { return prop.queueFlags & VK_QUEUE_TRANSFER_BIT; }); + return family_properties.span().is_any([](VkQueueFamilyProperties const &prop) -> bool { + return prop.queueFlags & VK_QUEUE_TRANSFER_BIT; + }); } bool has_graphics_command_queue_family() const { - return family_properties.span().is_any( - [](VkQueueFamilyProperties const &prop) -> bool { return prop.queueFlags & VK_QUEUE_GRAPHICS_BIT; }); + return family_properties.span().is_any([](VkQueueFamilyProperties const &prop) -> bool { + return prop.queueFlags & VK_QUEUE_GRAPHICS_BIT; + }); } VkSampleCountFlagBits get_max_sample_count() const { - VkSampleCountFlags counts = properties.limits.framebufferColorSampleCounts & properties.limits.framebufferDepthSampleCounts; + VkSampleCountFlags counts = properties.limits.framebufferColorSampleCounts & + properties.limits.framebufferDepthSampleCounts; if (counts & VK_SAMPLE_COUNT_64_BIT) return VK_SAMPLE_COUNT_64_BIT; @@ -660,9 +710,11 @@ struct Device stx::Rc phy_dev; stx::Vec command_queues; - Device(VkDevice adevice, stx::Rc aphy_device, stx::Vec acommand_queues) : + Device(VkDevice adevice, stx::Rc aphy_device, + stx::Vec acommand_queues) : dev{adevice}, phy_dev{std::move(aphy_device)}, command_queues{std::move(acommand_queues)} - {} + { + } STX_MAKE_PINNED(Device) @@ -678,39 +730,49 @@ struct CommandQueue stx::Rc device; }; -inline stx::Rc create_instance(char const *app_name, u32 app_version, char const *engine_name, u32 engine_version, +inline stx::Rc create_instance(char const *app_name, u32 app_version, + char const *engine_name, u32 engine_version, stx::Span required_extensions, stx::Span validation_layers) { - auto [instance, debug_utils_messenger] = create_vulkan_instance(required_extensions, validation_layers, app_name, app_version, engine_name, engine_version); + auto [instance, debug_utils_messenger] = create_vulkan_instance( + required_extensions, validation_layers, app_name, app_version, engine_name, engine_version); - return stx::rc::make_inplace(stx::os_allocator, instance, debug_utils_messenger == VK_NULL_HANDLE ? stx::None : stx::make_some(std::move(debug_utils_messenger))) + return stx::rc::make_inplace(stx::os_allocator, instance, + debug_utils_messenger == VK_NULL_HANDLE ? + stx::None : + stx::make_some(std::move(debug_utils_messenger))) .unwrap(); } // can also be used for transfer -inline stx::Option get_graphics_command_queue(stx::Rc const &phy_dev) +inline stx::Option + get_graphics_command_queue(stx::Rc const &phy_dev) { auto pos = std::find_if(phy_dev->family_properties.begin(), phy_dev->family_properties.end(), - [](VkQueueFamilyProperties const &prop) -> bool { return prop.queueFlags & VK_QUEUE_GRAPHICS_BIT; }); + [](VkQueueFamilyProperties const &prop) -> bool { + return prop.queueFlags & VK_QUEUE_GRAPHICS_BIT; + }); if (pos == phy_dev->family_properties.end()) { return stx::None; } - return stx::Some( - CommandQueueFamilyInfo{.index = AS(u32, pos - phy_dev->family_properties.begin()), .phy_device = phy_dev.share()}); + return stx::Some(CommandQueueFamilyInfo{ + .index = AS(u32, pos - phy_dev->family_properties.begin()), .phy_device = phy_dev.share()}); } -inline stx::Rc create_device(stx::Rc const &phy_dev, - stx::Span command_queue_create_info, - stx::Span required_extensions, - stx::Span required_validation_layers, - VkPhysicalDeviceFeatures required_features) +inline stx::Rc + create_device(stx::Rc const &phy_dev, + stx::Span command_queue_create_info, + stx::Span required_extensions, + stx::Span required_validation_layers, + VkPhysicalDeviceFeatures required_features) { - VkDevice dev = create_logical_device(phy_dev->phy_device, required_extensions, required_validation_layers, - command_queue_create_info, required_features); + VkDevice dev = + create_logical_device(phy_dev->phy_device, required_extensions, required_validation_layers, + command_queue_create_info, required_features); stx::Vec command_queues; @@ -721,7 +783,8 @@ inline stx::Rc create_device(stx::Rc const & u32 queue_count = create_info.queueCount; ASH_CHECK(command_queue_family_index < phy_dev->family_properties.size()); - for (u32 queue_index_in_family = 0; queue_index_in_family < queue_count; queue_index_in_family++) + for (u32 queue_index_in_family = 0; queue_index_in_family < queue_count; + queue_index_in_family++) { f32 priority = create_info.pQueuePriorities[i]; @@ -736,16 +799,20 @@ inline stx::Rc create_device(stx::Rc const & .queue = command_queue, .create_index = AS(u32, i), .priority = priority, - .family = CommandQueueFamilyInfo{.index = command_queue_family_index, .phy_device = phy_dev.share()}, + .family = CommandQueueFamilyInfo{.index = command_queue_family_index, + .phy_device = phy_dev.share()}, }) .unwrap(); } } - return stx::rc::make_inplace(stx::os_allocator, dev, phy_dev.share(), std::move(command_queues)).unwrap(); + return stx::rc::make_inplace(stx::os_allocator, dev, phy_dev.share(), + std::move(command_queues)) + .unwrap(); } -inline stx::Option get_command_queue(stx::Rc const &device, CommandQueueFamilyInfo const &family, +inline stx::Option get_command_queue(stx::Rc const &device, + CommandQueueFamilyInfo const &family, u32 command_queue_create_index) { // We shouldn't have to perform checks? @@ -762,12 +829,15 @@ inline stx::Option get_command_queue(stx::Rc const &devi CommandQueueInfo const &queue = queue_s[0]; - return stx::Some( - CommandQueue{.info = CommandQueueInfo{.queue = queue.queue, - .create_index = queue.create_index, - .priority = queue.priority, - .family = CommandQueueFamilyInfo{.index = queue.family.index, .phy_device = queue.family.phy_device.share()}}, - .device = device.share()}); + return stx::Some(CommandQueue{ + .info = + CommandQueueInfo{ + .queue = queue.queue, + .create_index = queue.create_index, + .priority = queue.priority, + .family = CommandQueueFamilyInfo{.index = queue.family.index, + .phy_device = queue.family.phy_device.share()}}, + .device = device.share()}); } struct Buffer @@ -793,7 +863,11 @@ struct Buffer { std::memcpy(memory_map, data, size); - VkMappedMemoryRange range{.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, .pNext = nullptr, .memory = memory, .offset = 0, .size = VK_WHOLE_SIZE}; + VkMappedMemoryRange range{.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .pNext = nullptr, + .memory = memory, + .offset = 0, + .size = VK_WHOLE_SIZE}; ASH_VK_CHECK(vkFlushMappedMemoryRanges(dev, 1, &range)); } @@ -816,7 +890,8 @@ struct VecBuffer vkDestroyBuffer(dev, buffer, nullptr); } - void init(VkDevice adev, VkPhysicalDeviceMemoryProperties const &memory_properties, VkBufferUsageFlags ausage) + void init(VkDevice adev, VkPhysicalDeviceMemoryProperties const &memory_properties, + VkBufferUsageFlags ausage) { dev = adev; size = 0; @@ -843,7 +918,8 @@ struct VecBuffer } u32 memory_type_index = find_suitable_memory_type(memory_properties, memory_requirements, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT) + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_CACHED_BIT) .unwrap(); VkMemoryAllocateInfo alloc_info{.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, @@ -901,10 +977,10 @@ struct VecBuffer { vkFreeMemory(dev, memory, nullptr); - u32 memory_type_index = - find_suitable_memory_type(memory_properties, memory_requirements, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT) - .unwrap(); + u32 memory_type_index = find_suitable_memory_type(memory_properties, memory_requirements, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_CACHED_BIT) + .unwrap(); VkMemoryAllocateInfo alloc_info{.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = nullptr, @@ -923,14 +999,19 @@ struct VecBuffer memcpy(memory_map, span.data(), span.size()); - VkMappedMemoryRange range{.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, .pNext = nullptr, .memory = memory, .offset = 0, .size = VK_WHOLE_SIZE}; + VkMappedMemoryRange range{.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .pNext = nullptr, + .memory = memory, + .offset = 0, + .size = VK_WHOLE_SIZE}; ASH_VK_CHECK(vkFlushMappedMemoryRanges(dev, 1, &range)); } }; -inline Buffer create_host_visible_buffer(VkDevice dev, VkPhysicalDeviceMemoryProperties const &memory_properties, usize size_bytes, - VkBufferUsageFlags usage) +inline Buffer create_host_visible_buffer(VkDevice dev, + VkPhysicalDeviceMemoryProperties const &memory_properties, + usize size_bytes, VkBufferUsageFlags usage) { VkBufferCreateInfo create_info{.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, @@ -952,7 +1033,8 @@ inline Buffer create_host_visible_buffer(VkDevice dev, VkPhysicalDeviceMemoryPro vkGetBufferMemoryRequirements(dev, buffer, &memory_requirements); u32 memory_type_index = find_suitable_memory_type(memory_properties, memory_requirements, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT) + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_CACHED_BIT) .unwrap(); VkMemoryAllocateInfo alloc_info{.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, @@ -968,7 +1050,8 @@ inline Buffer create_host_visible_buffer(VkDevice dev, VkPhysicalDeviceMemoryPro ASH_VK_CHECK(vkMapMemory(dev, memory, 0, VK_WHOLE_SIZE, 0, &memory_map)); - return Buffer{.buffer = buffer, .memory = memory, .memory_map = memory_map, .size = size_bytes, .dev = dev}; + return Buffer{ + .buffer = buffer, .memory = memory, .memory_map = memory_map, .size = size_bytes, .dev = dev}; } struct Image @@ -986,21 +1069,25 @@ struct Image } }; -inline Image create_msaa_color_resource(VkDevice dev, VkPhysicalDeviceMemoryProperties const &memory_properties, +inline Image create_msaa_color_resource(VkDevice dev, + VkPhysicalDeviceMemoryProperties const &memory_properties, VkFormat swapchain_format, VkExtent2D swapchain_extent, VkSampleCountFlagBits sample_count) { - VkImageCreateInfo create_info{.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .imageType = VK_IMAGE_TYPE_2D, - .format = swapchain_format, - .extent = VkExtent3D{.width = swapchain_extent.width, .height = swapchain_extent.height, .depth = 1}, - .mipLevels = 1, - .arrayLayers = 1, - .samples = sample_count, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + VkImageCreateInfo create_info{.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .imageType = VK_IMAGE_TYPE_2D, + .format = swapchain_format, + .extent = VkExtent3D{.width = swapchain_extent.width, + .height = swapchain_extent.height, + .depth = 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = sample_count, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -1014,8 +1101,9 @@ inline Image create_msaa_color_resource(VkDevice dev, VkPhysicalDeviceMemoryProp vkGetImageMemoryRequirements(dev, image, &memory_requirements); - u32 memory_type_index = - find_suitable_memory_type(memory_properties, memory_requirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT).unwrap(); + u32 memory_type_index = find_suitable_memory_type(memory_properties, memory_requirements, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + .unwrap(); VkMemoryAllocateInfo alloc_info{.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = nullptr, @@ -1035,8 +1123,15 @@ inline Image create_msaa_color_resource(VkDevice dev, VkPhysicalDeviceMemoryProp .image = image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = swapchain_format, - .components = VkComponentMapping{.r = VK_COMPONENT_SWIZZLE_IDENTITY, .g = VK_COMPONENT_SWIZZLE_IDENTITY, .b = VK_COMPONENT_SWIZZLE_IDENTITY, .a = VK_COMPONENT_SWIZZLE_IDENTITY}, - .subresourceRange = VkImageSubresourceRange{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1}}; + .components = VkComponentMapping{.r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY}, + .subresourceRange = VkImageSubresourceRange{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1}}; VkImageView view; @@ -1045,21 +1140,25 @@ inline Image create_msaa_color_resource(VkDevice dev, VkPhysicalDeviceMemoryProp return Image{.image = image, .view = view, .memory = memory, .dev = dev}; } -inline Image create_msaa_depth_resource(VkDevice dev, VkPhysicalDeviceMemoryProperties const &memory_properties, - VkFormat depth_format, VkExtent2D swapchain_extent, VkSampleCountFlagBits sample_count) +inline Image create_msaa_depth_resource(VkDevice dev, + VkPhysicalDeviceMemoryProperties const &memory_properties, + VkFormat depth_format, VkExtent2D swapchain_extent, + VkSampleCountFlagBits sample_count) { - VkImageCreateInfo create_info{.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .imageType = VK_IMAGE_TYPE_2D, - .format = depth_format, - .extent = VkExtent3D{.width = swapchain_extent.width, .height = swapchain_extent.height, .depth = 1}, - .mipLevels = 1, - .arrayLayers = 1, - .samples = sample_count, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + VkImageCreateInfo create_info{.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .imageType = VK_IMAGE_TYPE_2D, + .format = depth_format, + .extent = VkExtent3D{.width = swapchain_extent.width, + .height = swapchain_extent.height, + .depth = 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = sample_count, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED}; @@ -1072,7 +1171,9 @@ inline Image create_msaa_depth_resource(VkDevice dev, VkPhysicalDeviceMemoryProp vkGetImageMemoryRequirements(dev, image, &memory_requirements); - u32 memory_type_index = find_suitable_memory_type(memory_properties, memory_requirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT).unwrap(); + u32 memory_type_index = find_suitable_memory_type(memory_properties, memory_requirements, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + .unwrap(); VkMemoryAllocateInfo alloc_info{.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = nullptr, @@ -1092,8 +1193,15 @@ inline Image create_msaa_depth_resource(VkDevice dev, VkPhysicalDeviceMemoryProp .image = image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = depth_format, - .components = VkComponentMapping{.r = VK_COMPONENT_SWIZZLE_IDENTITY, .g = VK_COMPONENT_SWIZZLE_IDENTITY, .b = VK_COMPONENT_SWIZZLE_IDENTITY, .a = VK_COMPONENT_SWIZZLE_IDENTITY}, - .subresourceRange = VkImageSubresourceRange{.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1}}; + .components = VkComponentMapping{.r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY}, + .subresourceRange = VkImageSubresourceRange{.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1}}; VkImageView view; @@ -1103,7 +1211,9 @@ inline Image create_msaa_depth_resource(VkDevice dev, VkPhysicalDeviceMemoryProp } // choose a specific swapchain format available on the surface -inline VkSurfaceFormatKHR select_swapchain_surface_formats(stx::Span formats, stx::Span preferred_formats) +inline VkSurfaceFormatKHR + select_swapchain_surface_formats(stx::Span formats, + stx::Span preferred_formats) { ASH_CHECK(!formats.is_empty(), "no window surface format supported by physical device"); @@ -1111,7 +1221,8 @@ inline VkSurfaceFormatKHR select_swapchain_surface_formats(stx::Span available_presentation_modes, - stx::Span preferred_present_modes) noexcept +inline VkPresentModeKHR select_swapchain_presentation_mode( + stx::Span available_presentation_modes, + stx::Span preferred_present_modes) noexcept { /// - VK_PRESENT_MODE_IMMEDIATE_KHR: Images submitted by your application /// are transferred to the screen right away, which may result in tearing. @@ -1159,7 +1271,8 @@ inline VkPresentModeKHR select_swapchain_presentation_mode(stx::Span candidates, VkImageTiling tiling, +inline VkFormat find_supported_format(VkPhysicalDevice phy_dev, + stx::Span candidates, VkImageTiling tiling, VkFormatFeatureFlags features) { for (VkFormat format : candidates) @@ -1171,7 +1284,8 @@ inline VkFormat find_supported_format(VkPhysicalDevice phy_dev, stx::Span images; // the images in the swapchain, which image is used and the order they are used for frame is determined by the presentation mode - stx::Vec image_views; // the image views pointing to a part of a whole texture (images in the swapchain) + u32 frame = 0; + u32 max_nframes_in_flight = + 0; // can be smaller or equal to the number of images on the swapchain but never larger + VkSurfaceFormatKHR color_format{.format = VK_FORMAT_R8G8B8A8_UNORM, + .colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR}; + VkFormat depth_format = VK_FORMAT_D32_SFLOAT; + VkPresentModeKHR present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR; + VkExtent2D image_extent{.width = 0, .height = 0}; + VkExtent2D window_extent{.width = 0, .height = 0}; + VkSampleCountFlagBits msaa_sample_count = VK_SAMPLE_COUNT_1_BIT; + stx::Vec + images; // the images in the swapchain, which image is used and the order they are used for frame is determined by the presentation mode + stx::Vec + image_views; // the image views pointing to a part of a whole texture (images in the swapchain) stx::Vec framebuffers; - stx::Vec render_semaphores; // the rendering semaphores correspond to the frame indexes and not the swapchain images - stx::Vec image_acquisition_semaphores; - stx::Vec render_fences; - stx::Vec image_acquisition_fences; - Image msaa_color_image; - Image msaa_depth_image; - VkRenderPass render_pass = VK_NULL_HANDLE; // Render Passes describe the outputs, attachments, depth, and msaa process - VkSwapchainKHR swapchain = VK_NULL_HANDLE; - VkDevice dev = VK_NULL_HANDLE; - - bool init(VkPhysicalDevice phy, VkPhysicalDeviceMemoryProperties const &memory_properties, VkDevice adev, - VkSurfaceKHR target_surface, u32 amax_nframes_in_flight, stx::Span preferred_formats, - stx::Span preferred_present_modes, VkExtent2D preferred_extent, VkExtent2D awindow_extent, - VkSampleCountFlagBits amsaa_sample_count, VkCompositeAlphaFlagBitsKHR alpha_blending) + stx::Vec + render_semaphores; // the rendering semaphores correspond to the frame indexes and not the swapchain images + stx::Vec image_acquisition_semaphores; + stx::Vec render_fences; + stx::Vec image_acquisition_fences; + Image msaa_color_image; + Image msaa_depth_image; + VkRenderPass render_pass = + VK_NULL_HANDLE; // Render Passes describe the outputs, attachments, depth, and msaa process + VkSwapchainKHR swapchain = VK_NULL_HANDLE; + VkDevice dev = VK_NULL_HANDLE; + + bool init(VkPhysicalDevice phy, VkPhysicalDeviceMemoryProperties const &memory_properties, + VkDevice adev, VkSurfaceKHR target_surface, u32 amax_nframes_in_flight, + stx::Span preferred_formats, + stx::Span preferred_present_modes, VkExtent2D preferred_extent, + VkExtent2D awindow_extent, VkSampleCountFlagBits amsaa_sample_count, + VkCompositeAlphaFlagBitsKHR alpha_blending) { max_nframes_in_flight = amax_nframes_in_flight; dev = adev; @@ -1254,34 +1378,43 @@ struct SwapChain ASH_LOG_INFO(Vulkan, "Device Supported Surface Formats:"); for (VkSurfaceFormatKHR const &format : properties.supported_formats) { - ASH_LOG_INFO(Vulkan, "\tFormat: {}, Color Space: {}", string_VkFormat(format.format), string_VkColorSpaceKHR(format.colorSpace)); + ASH_LOG_INFO(Vulkan, "\tFormat: {}, Color Space: {}", string_VkFormat(format.format), + string_VkColorSpaceKHR(format.colorSpace)); } // swapchain formats are device-dependent - VkSurfaceFormatKHR selected_format = select_swapchain_surface_formats(properties.supported_formats, preferred_formats); + VkSurfaceFormatKHR selected_format = + select_swapchain_surface_formats(properties.supported_formats, preferred_formats); - ASH_LOG_INFO(Vulkan, "Selected swapchain surface config with format: {}, depth stencil format: {}, and color space: {}, and {} swapchain images", string_VkFormat(selected_format.format), - string_VkFormat(depth_format), + ASH_LOG_INFO(Vulkan, + "Selected swapchain surface config with format: {}, depth stencil format: {}, and " + "color space: {}, and {} swapchain images", + string_VkFormat(selected_format.format), string_VkFormat(depth_format), string_VkColorSpaceKHR(selected_format.colorSpace), max_nframes_in_flight); ASH_LOG_INFO(Vulkan, "Available swapchain presentation modes:"); // swapchain presentation modes are device-dependent - VkPresentModeKHR selected_present_mode = select_swapchain_presentation_mode(properties.presentation_modes, preferred_present_modes); - - ASH_LOG_INFO(Vulkan, "Selected swapchain presentation mode: {}", string_VkPresentModeKHR(selected_present_mode)); - - auto [new_swapchain, new_extent, is_visible_extent] = create_swapchain( - dev, target_surface, preferred_extent, selected_format, max_nframes_in_flight, selected_present_mode, properties, - // not thread-safe since GPUs typically have one graphics queue - VK_SHARING_MODE_EXCLUSIVE, - // render target image - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, alpha_blending, - // we don't care about the color of pixels that are obscured, for - // example because another window is in front of them. Unless you really - // need to be able to read these pixels back and get predictable - // results, you'll get the best performance by enabling clipping. - VK_TRUE); + VkPresentModeKHR selected_present_mode = + select_swapchain_presentation_mode(properties.presentation_modes, preferred_present_modes); + + ASH_LOG_INFO(Vulkan, "Selected swapchain presentation mode: {}", + string_VkPresentModeKHR(selected_present_mode)); + + auto [new_swapchain, new_extent, is_visible_extent] = + create_swapchain(dev, target_surface, preferred_extent, selected_format, + max_nframes_in_flight, selected_present_mode, properties, + // not thread-safe since GPUs typically have one graphics queue + VK_SHARING_MODE_EXCLUSIVE, + // render target image + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + alpha_blending, + // we don't care about the color of pixels that are obscured, for + // example because another window is in front of them. Unless you really + // need to be able to read these pixels back and get predictable + // results, you'll get the best performance by enabling clipping. + VK_TRUE); ASH_LOG_INFO(Vulkan, "Selected swapchain extent : {}, {}", new_extent.width, new_extent.height); @@ -1297,28 +1430,31 @@ struct SwapChain window_extent = awindow_extent; msaa_sample_count = amsaa_sample_count; - images = get_swapchain_images(dev, swapchain); - msaa_color_image = create_msaa_color_resource(dev, memory_properties, color_format.format, new_extent, msaa_sample_count); - msaa_depth_image = create_msaa_depth_resource(dev, memory_properties, depth_format, new_extent, msaa_sample_count); + images = get_swapchain_images(dev, swapchain); + msaa_color_image = create_msaa_color_resource(dev, memory_properties, color_format.format, + new_extent, msaa_sample_count); + msaa_depth_image = create_msaa_depth_resource(dev, memory_properties, depth_format, new_extent, + msaa_sample_count); max_nframes_in_flight = std::min(AS(u32, images.size()), max_nframes_in_flight); for (VkImage image : images) { - VkImageViewCreateInfo create_info{.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .image = image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = color_format.format, - .components = VkComponentMapping{.r = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY}, - .subresourceRange = VkImageSubresourceRange{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1}}; + VkImageViewCreateInfo create_info{ + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .image = image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = color_format.format, + .components = VkComponentMapping{.r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY}, + .subresourceRange = VkImageSubresourceRange{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1}}; VkImageView view; @@ -1336,7 +1472,8 @@ struct SwapChain .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; + .finalLayout = + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; VkAttachmentDescription depth_attachment{.flags = 0, .format = depth_format, @@ -1346,25 +1483,31 @@ struct SwapChain .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL}; + .finalLayout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL}; - VkAttachmentDescription color_attachment_resolve{.flags = 0, - .format = color_format.format, - .samples = VK_SAMPLE_COUNT_1_BIT, - .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, - .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, - .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, - .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR}; + VkAttachmentDescription color_attachment_resolve{ + .flags = 0, + .format = color_format.format, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR}; - VkAttachmentDescription attachments[] = {color_attachment, depth_attachment, color_attachment_resolve}; + VkAttachmentDescription attachments[] = {color_attachment, depth_attachment, + color_attachment_resolve}; - VkAttachmentReference color_attachment_reference{.attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; + VkAttachmentReference color_attachment_reference{ + .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; - VkAttachmentReference depth_attachment_reference{.attachment = 1, .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL}; + VkAttachmentReference depth_attachment_reference{ + .attachment = 1, .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL}; - VkAttachmentReference color_attachment_resolve_reference{.attachment = 2, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; + VkAttachmentReference color_attachment_resolve_reference{ + .attachment = 2, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}; VkSubpassDescription subpass{.flags = 0, .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, @@ -1377,24 +1520,27 @@ struct SwapChain .preserveAttachmentCount = 0, .pPreserveAttachments = nullptr}; - VkSubpassDependency dependency{ - .srcSubpass = VK_SUBPASS_EXTERNAL, - .dstSubpass = 0, - .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, - .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, - .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT}; - - VkRenderPassCreateInfo render_pass_create_info{.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .attachmentCount = AS(u32, std::size(attachments)), - .pAttachments = attachments, - .subpassCount = 1, - .pSubpasses = &subpass, - .dependencyCount = 1, - .pDependencies = &dependency}; + VkSubpassDependency dependency{.srcSubpass = VK_SUBPASS_EXTERNAL, + .dstSubpass = 0, + .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, + .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT}; + + VkRenderPassCreateInfo render_pass_create_info{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .attachmentCount = AS(u32, std::size(attachments)), + .pAttachments = attachments, + .subpassCount = 1, + .pSubpasses = &subpass, + .dependencyCount = 1, + .pDependencies = &dependency}; // we have an implicit render subpass since we specified the msaa sample count ASH_VK_CHECK(vkCreateRenderPass(dev, &render_pass_create_info, nullptr, &render_pass)); @@ -1405,10 +1551,10 @@ struct SwapChain VkImageView attachments[] = {msaa_color_image.view, msaa_depth_image.view, image_views[i]}; - VkFramebufferCreateInfo create_info{.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .renderPass = render_pass, + VkFramebufferCreateInfo create_info{.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .renderPass = render_pass, .attachmentCount = AS(u32, std::size(attachments)), .pAttachments = attachments, .width = image_extent.width, @@ -1433,19 +1579,24 @@ struct SwapChain VkSemaphore image_acquisition_semaphore; - ASH_VK_CHECK(vkCreateSemaphore(dev, &semaphore_create_info, nullptr, &image_acquisition_semaphore)); + ASH_VK_CHECK( + vkCreateSemaphore(dev, &semaphore_create_info, nullptr, &image_acquisition_semaphore)); image_acquisition_semaphores.push_inplace(image_acquisition_semaphore).unwrap(); - VkFenceCreateInfo image_acquisition_fence_create_info{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = nullptr, .flags = 0}; + VkFenceCreateInfo image_acquisition_fence_create_info{ + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = nullptr, .flags = 0}; VkFence image_acquisition_fence; - ASH_VK_CHECK(vkCreateFence(dev, &image_acquisition_fence_create_info, nullptr, &image_acquisition_fence)); + ASH_VK_CHECK(vkCreateFence(dev, &image_acquisition_fence_create_info, nullptr, + &image_acquisition_fence)); image_acquisition_fences.push_inplace(image_acquisition_fence).unwrap(); - VkFenceCreateInfo rendering_fence_create_info{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = nullptr, .flags = VK_FENCE_CREATE_SIGNALED_BIT}; + VkFenceCreateInfo rendering_fence_create_info{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .pNext = nullptr, + .flags = VK_FENCE_CREATE_SIGNALED_BIT}; VkFence rendering_fence; @@ -1534,8 +1685,10 @@ struct Surface } void change_swapchain(stx::Rc const &queue, u32 max_nframes_in_flight, - stx::Span preferred_formats, stx::Span preferred_present_modes, - VkExtent2D preferred_extent, VkExtent2D window_extent, VkSampleCountFlagBits msaa_sample_count, + stx::Span preferred_formats, + stx::Span preferred_present_modes, + VkExtent2D preferred_extent, VkExtent2D window_extent, + VkSampleCountFlagBits msaa_sample_count, VkCompositeAlphaFlagBitsKHR alpha_blending) { // don't want to have two existing at once @@ -1544,9 +1697,10 @@ struct Surface SwapChain new_swapchain; - if (!new_swapchain.init(queue->device->phy_dev->phy_device, queue->device->phy_dev->memory_properties, queue->device->dev, - surface, max_nframes_in_flight, preferred_formats, preferred_present_modes, preferred_extent, - window_extent, msaa_sample_count, alpha_blending)) + if (!new_swapchain.init(queue->device->phy_dev->phy_device, + queue->device->phy_dev->memory_properties, queue->device->dev, surface, + max_nframes_in_flight, preferred_formats, preferred_present_modes, + preferred_extent, window_extent, msaa_sample_count, alpha_blending)) { is_zero_sized_swapchain = true; return; @@ -1563,41 +1717,49 @@ struct Pipeline VkPipelineLayout layout = VK_NULL_HANDLE; VkDevice dev = VK_NULL_HANDLE; - void build(VkDevice adev, VkShaderModule vertex_shader, VkShaderModule fragment_shader, VkRenderPass target_render_pass, - VkSampleCountFlagBits msaa_sample_count, stx::Span descriptor_set_layout, - stx::Span vertex_input_attr, u32 vertex_input_stride, u32 push_constant_size) + void build(VkDevice adev, VkShaderModule vertex_shader, VkShaderModule fragment_shader, + VkRenderPass target_render_pass, VkSampleCountFlagBits msaa_sample_count, + stx::Span descriptor_set_layout, + stx::Span vertex_input_attr, + u32 vertex_input_stride, u32 push_constant_size) { dev = adev; - VkPipelineShaderStageCreateInfo vert_shader_stage{.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_VERTEX_BIT, - .module = vertex_shader, - .pName = "main", - .pSpecializationInfo = nullptr}; - - VkPipelineShaderStageCreateInfo frag_shader_stage{.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_FRAGMENT_BIT, - .module = fragment_shader, - .pName = "main", - .pSpecializationInfo = nullptr}; + VkPipelineShaderStageCreateInfo vert_shader_stage{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .module = vertex_shader, + .pName = "main", + .pSpecializationInfo = nullptr}; + + VkPipelineShaderStageCreateInfo frag_shader_stage{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .module = fragment_shader, + .pName = "main", + .pSpecializationInfo = nullptr}; VkPipelineShaderStageCreateInfo stages[] = {vert_shader_stage, frag_shader_stage}; ASH_CHECK(push_constant_size % 4 == 0); - VkPushConstantRange push_constant{.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, .offset = 0, .size = push_constant_size}; + VkPushConstantRange push_constant{.stageFlags = + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + .offset = 0, + .size = push_constant_size}; - VkPipelineLayoutCreateInfo layout_create_info{.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .setLayoutCount = AS(u32, descriptor_set_layout.size()), - .pSetLayouts = descriptor_set_layout.data(), - .pushConstantRangeCount = 1, - .pPushConstantRanges = &push_constant}; + VkPipelineLayoutCreateInfo layout_create_info{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .setLayoutCount = AS(u32, descriptor_set_layout.size()), + .pSetLayouts = descriptor_set_layout.data(), + .pushConstantRangeCount = 1, + .pPushConstantRanges = &push_constant}; ASH_VK_CHECK(vkCreatePipelineLayout(dev, &layout_create_info, nullptr, &layout)); @@ -1609,101 +1771,110 @@ struct Pipeline .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .alphaBlendOp = VK_BLEND_OP_ADD, - .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT}}; - - VkPipelineColorBlendStateCreateInfo color_blend_state{.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .logicOpEnable = VK_FALSE, - .logicOp = VK_LOGIC_OP_COPY, - .attachmentCount = AS(u32, std::size(color_blend_attachment_states)), - .pAttachments = color_blend_attachment_states, - .blendConstants = {0, 0, 0, 0}}; - - VkPipelineDepthStencilStateCreateInfo depth_stencil_state{.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .depthTestEnable = VK_TRUE, - .depthWriteEnable = VK_TRUE, - .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL, - .depthBoundsTestEnable = VK_FALSE, - .stencilTestEnable = VK_FALSE, - .front = VkStencilOpState{.failOp = VK_STENCIL_OP_KEEP, - .passOp = VK_STENCIL_OP_KEEP, - .depthFailOp = VK_STENCIL_OP_KEEP, - .compareOp = VK_COMPARE_OP_NEVER, - .compareMask = 0, - .writeMask = 0, - .reference = 0}, - .back = VkStencilOpState{.failOp = VK_STENCIL_OP_KEEP, - .passOp = VK_STENCIL_OP_KEEP, - .depthFailOp = VK_STENCIL_OP_KEEP, - .compareOp = VK_COMPARE_OP_ALWAYS, - .compareMask = 0, - .writeMask = 0, - .reference = 0}, - .minDepthBounds = 0, - .maxDepthBounds = 1}; - - VkPipelineInputAssemblyStateCreateInfo input_assembly_state{.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - .primitiveRestartEnable = VK_FALSE}; - - VkPipelineMultisampleStateCreateInfo multisample_state{.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .rasterizationSamples = msaa_sample_count, - .sampleShadingEnable = VK_FALSE, - .minSampleShading = 1, - .pSampleMask = nullptr, - .alphaToCoverageEnable = VK_FALSE, - .alphaToOneEnable = VK_FALSE}; - - VkPipelineRasterizationStateCreateInfo rasterization_state{.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .depthClampEnable = VK_FALSE, - .rasterizerDiscardEnable = VK_FALSE, - .polygonMode = VK_POLYGON_MODE_FILL, - .cullMode = VK_CULL_MODE_NONE, - .frontFace = VK_FRONT_FACE_CLOCKWISE, - .depthBiasEnable = VK_FALSE, - .depthBiasConstantFactor = 0, - .depthBiasClamp = 0, - .depthBiasSlopeFactor = 0, - .lineWidth = 1}; + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT}}; + + VkPipelineColorBlendStateCreateInfo color_blend_state{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .logicOpEnable = VK_FALSE, + .logicOp = VK_LOGIC_OP_COPY, + .attachmentCount = AS(u32, std::size(color_blend_attachment_states)), + .pAttachments = color_blend_attachment_states, + .blendConstants = {0, 0, 0, 0}}; + + VkPipelineDepthStencilStateCreateInfo depth_stencil_state{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .depthTestEnable = VK_TRUE, + .depthWriteEnable = VK_TRUE, + .depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL, + .depthBoundsTestEnable = VK_FALSE, + .stencilTestEnable = VK_FALSE, + .front = VkStencilOpState{.failOp = VK_STENCIL_OP_KEEP, + .passOp = VK_STENCIL_OP_KEEP, + .depthFailOp = VK_STENCIL_OP_KEEP, + .compareOp = VK_COMPARE_OP_NEVER, + .compareMask = 0, + .writeMask = 0, + .reference = 0}, + .back = VkStencilOpState{.failOp = VK_STENCIL_OP_KEEP, + .passOp = VK_STENCIL_OP_KEEP, + .depthFailOp = VK_STENCIL_OP_KEEP, + .compareOp = VK_COMPARE_OP_ALWAYS, + .compareMask = 0, + .writeMask = 0, + .reference = 0}, + .minDepthBounds = 0, + .maxDepthBounds = 1}; + + VkPipelineInputAssemblyStateCreateInfo input_assembly_state{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + .primitiveRestartEnable = VK_FALSE}; + + VkPipelineMultisampleStateCreateInfo multisample_state{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .rasterizationSamples = msaa_sample_count, + .sampleShadingEnable = VK_FALSE, + .minSampleShading = 1, + .pSampleMask = nullptr, + .alphaToCoverageEnable = VK_FALSE, + .alphaToOneEnable = VK_FALSE}; + + VkPipelineRasterizationStateCreateInfo rasterization_state{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .depthClampEnable = VK_FALSE, + .rasterizerDiscardEnable = VK_FALSE, + .polygonMode = VK_POLYGON_MODE_FILL, + .cullMode = VK_CULL_MODE_NONE, + .frontFace = VK_FRONT_FACE_CLOCKWISE, + .depthBiasEnable = VK_FALSE, + .depthBiasConstantFactor = 0, + .depthBiasClamp = 0, + .depthBiasSlopeFactor = 0, + .lineWidth = 1}; VkVertexInputBindingDescription vertex_binding_descriptions[] = { {.binding = 0, .stride = vertex_input_stride, .inputRate = VK_VERTEX_INPUT_RATE_VERTEX}}; VkPipelineVertexInputStateCreateInfo vertex_input_state{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .vertexBindingDescriptionCount = AS(u32, std::size(vertex_binding_descriptions)), - .pVertexBindingDescriptions = vertex_binding_descriptions, + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .vertexBindingDescriptionCount = AS(u32, std::size(vertex_binding_descriptions)), + .pVertexBindingDescriptions = vertex_binding_descriptions, .vertexAttributeDescriptionCount = AS(u32, vertex_input_attr.size()), .pVertexAttributeDescriptions = vertex_input_attr.data()}; - VkPipelineViewportStateCreateInfo viewport_state{.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .viewportCount = 1, - .pViewports = nullptr, - .scissorCount = 1, - .pScissors = nullptr}; + VkPipelineViewportStateCreateInfo viewport_state{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .viewportCount = 1, + .pViewports = nullptr, + .scissorCount = 1, + .pScissors = nullptr}; VkDynamicState dynamic_states[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR}; - VkPipelineDynamicStateCreateInfo dynamic_state{.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .dynamicStateCount = AS(u32, std::size(dynamic_states)), - .pDynamicStates = dynamic_states}; + VkPipelineDynamicStateCreateInfo dynamic_state{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .dynamicStateCount = AS(u32, std::size(dynamic_states)), + .pDynamicStates = dynamic_states}; - VkGraphicsPipelineCreateInfo create_info{.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + VkGraphicsPipelineCreateInfo create_info{.sType = + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stageCount = AS(u32, std::size(stages)), @@ -1723,7 +1894,8 @@ struct Pipeline .basePipelineHandle = VK_NULL_HANDLE, .basePipelineIndex = 0}; - ASH_VK_CHECK(vkCreateGraphicsPipelines(dev, VK_NULL_HANDLE, 1, &create_info, nullptr, &pipeline)); + ASH_VK_CHECK( + vkCreateGraphicsPipelines(dev, VK_NULL_HANDLE, 1, &create_info, nullptr, &pipeline)); } void destroy() diff --git a/ashura/include/ashura/vulkan_canvas_renderer.h b/ashura/include/ashura/vulkan_canvas_renderer.h index 235824033..02a9cf48e 100644 --- a/ashura/include/ashura/vulkan_canvas_renderer.h +++ b/ashura/include/ashura/vulkan_canvas_renderer.h @@ -16,13 +16,14 @@ namespace vk struct CanvasRenderer { - static constexpr VkQueryPipelineStatisticFlags PIPELINE_STATISTIC_QUERIES = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT | - VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT | - VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT | - VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT | - VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT | - VK_QUERY_PIPELINE_STATISTIC_TASK_SHADER_INVOCATIONS_BIT_EXT | - VK_QUERY_PIPELINE_STATISTIC_MESH_SHADER_INVOCATIONS_BIT_EXT; + static constexpr VkQueryPipelineStatisticFlags PIPELINE_STATISTIC_QUERIES = + VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT | + VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT | + VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT | + VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT | + VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT | + VK_QUERY_PIPELINE_STATISTIC_TASK_SHADER_INVOCATIONS_BIT_EXT | + VK_QUERY_PIPELINE_STATISTIC_MESH_SHADER_INVOCATIONS_BIT_EXT; static constexpr u32 NPIPELINE_STATISTIC_QUERIES = 7; static constexpr u32 NPIPELINE_TIMESTAMP_QUERIES = 2; u32 max_nframes_in_flight = 0; @@ -38,7 +39,8 @@ struct CanvasRenderer VkQueue queue = VK_NULL_HANDLE; VkDevice dev = VK_NULL_HANDLE; - void init(VkDevice adev, VkQueue aqueue, u32 aqueue_family_index, f32 atimestamp_period, VkPhysicalDeviceMemoryProperties const &amemory_properties, u32 amax_nframes_in_flight) + void init(VkDevice adev, VkQueue aqueue, u32 aqueue_family_index, f32 atimestamp_period, + VkPhysicalDeviceMemoryProperties const &amemory_properties, u32 amax_nframes_in_flight) { max_nframes_in_flight = amax_nframes_in_flight; memory_properties = amemory_properties; @@ -62,45 +64,51 @@ struct CanvasRenderer index_buffers.push_inplace(index_buffer).unwrap(); } - VkCommandPoolCreateInfo cmd_pool_create_info{.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - .pNext = nullptr, - .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, - .queueFamilyIndex = queue_family_index}; + VkCommandPoolCreateInfo cmd_pool_create_info{ + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = queue_family_index}; ASH_VK_CHECK(vkCreateCommandPool(dev, &cmd_pool_create_info, nullptr, &cmd_pool)); cmd_buffers.unsafe_resize_uninitialized(max_nframes_in_flight).unwrap(); - VkCommandBufferAllocateInfo cmd_buffers_allocate_info{.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .pNext = nullptr, - .commandPool = cmd_pool, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandBufferCount = AS(u32, max_nframes_in_flight)}; + VkCommandBufferAllocateInfo cmd_buffers_allocate_info{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .pNext = nullptr, + .commandPool = cmd_pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = AS(u32, max_nframes_in_flight)}; ASH_VK_CHECK(vkAllocateCommandBuffers(dev, &cmd_buffers_allocate_info, cmd_buffers.data())); - VkQueryPoolCreateInfo pipeline_statistics_query_pool_create_info{.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .queryType = VK_QUERY_TYPE_PIPELINE_STATISTICS, - .queryCount = NPIPELINE_STATISTIC_QUERIES, - .pipelineStatistics = PIPELINE_STATISTIC_QUERIES}; - - VkQueryPoolCreateInfo timestamp_query_pool_create_info{.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .queryType = VK_QUERY_TYPE_TIMESTAMP, - .queryCount = NPIPELINE_TIMESTAMP_QUERIES, - .pipelineStatistics = 0}; + VkQueryPoolCreateInfo pipeline_statistics_query_pool_create_info{ + .sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queryType = VK_QUERY_TYPE_PIPELINE_STATISTICS, + .queryCount = NPIPELINE_STATISTIC_QUERIES, + .pipelineStatistics = PIPELINE_STATISTIC_QUERIES}; + + VkQueryPoolCreateInfo timestamp_query_pool_create_info{ + .sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queryType = VK_QUERY_TYPE_TIMESTAMP, + .queryCount = NPIPELINE_TIMESTAMP_QUERIES, + .pipelineStatistics = 0}; for (u32 i = 0; i < max_nframes_in_flight; i++) { VkQueryPool pipeline_statistics_query_pool; - ASH_VK_CHECK(vkCreateQueryPool(dev, &pipeline_statistics_query_pool_create_info, nullptr, &pipeline_statistics_query_pool)); + ASH_VK_CHECK(vkCreateQueryPool(dev, &pipeline_statistics_query_pool_create_info, nullptr, + &pipeline_statistics_query_pool)); pipeline_statistics_query_pools.push_inplace(pipeline_statistics_query_pool).unwrap(); VkQueryPool timestamp_query_pool; - ASH_VK_CHECK(vkCreateQueryPool(dev, ×tamp_query_pool_create_info, nullptr, ×tamp_query_pool)); + ASH_VK_CHECK(vkCreateQueryPool(dev, ×tamp_query_pool_create_info, nullptr, + ×tamp_query_pool)); pipeline_timestamp_query_pools.push_inplace(timestamp_query_pool).unwrap(); } } @@ -133,20 +141,12 @@ struct CanvasRenderer vkDestroyCommandPool(dev, cmd_pool, nullptr); } - void submit(VkExtent2D viewport_extent, - VkExtent2D image_extent, - u32 frame, - VkFence render_fence, - VkSemaphore image_acquisition_semaphore, - VkSemaphore render_semaphore, - VkRenderPass render_pass, - VkFramebuffer framebuffer, - stx::Span cmds, - stx::Span vertices, - stx::Span indices, - CanvasPipelineManager const &pipeline_manager, - RenderResourceManager const &image_manager, - FrameStats &frame_stats) + void submit(VkExtent2D viewport_extent, VkExtent2D image_extent, u32 frame, VkFence render_fence, + VkSemaphore image_acquisition_semaphore, VkSemaphore render_semaphore, + VkRenderPass render_pass, VkFramebuffer framebuffer, + stx::Span cmds, stx::Span vertices, + stx::Span indices, CanvasPipelineManager const &pipeline_manager, + RenderResourceManager const &image_manager, FrameStats &frame_stats) { ASH_CHECK(frame < max_nframes_in_flight); @@ -169,7 +169,10 @@ struct CanvasRenderer u64 pipeline_statistics_query_query_results[NPIPELINE_STATISTIC_QUERIES]; u64 pipeline_timestamp_query_results[NPIPELINE_TIMESTAMP_QUERIES]; - if (vkGetQueryPoolResults(dev, pipeline_statistics_query_pools[frame], 0, 1, sizeof(pipeline_statistics_query_query_results), pipeline_statistics_query_query_results, sizeof(u64), VK_QUERY_RESULT_64_BIT) == VK_SUCCESS) + if (vkGetQueryPoolResults(dev, pipeline_statistics_query_pools[frame], 0, 1, + sizeof(pipeline_statistics_query_query_results), + pipeline_statistics_query_query_results, sizeof(u64), + VK_QUERY_RESULT_64_BIT) == VK_SUCCESS) { frame_stats.input_assembly_vertices = pipeline_statistics_query_query_results[0]; frame_stats.input_assembly_primitives = pipeline_statistics_query_query_results[1]; @@ -180,9 +183,14 @@ struct CanvasRenderer frame_stats.mesh_shader_invocations = pipeline_statistics_query_query_results[6]; } - if (vkGetQueryPoolResults(dev, pipeline_timestamp_query_pools[frame], 0, NPIPELINE_TIMESTAMP_QUERIES, sizeof(pipeline_timestamp_query_results), pipeline_timestamp_query_results, sizeof(u64), VK_QUERY_RESULT_64_BIT) == VK_SUCCESS) + if (vkGetQueryPoolResults(dev, pipeline_timestamp_query_pools[frame], 0, + NPIPELINE_TIMESTAMP_QUERIES, sizeof(pipeline_timestamp_query_results), + pipeline_timestamp_query_results, sizeof(u64), + VK_QUERY_RESULT_64_BIT) == VK_SUCCESS) { - frame_stats.gpu_time = nanoseconds{(nanoseconds::rep)(((f64) timestamp_period) * (f64) (pipeline_timestamp_query_results[1] - pipeline_timestamp_query_results[0]))}; + frame_stats.gpu_time = nanoseconds{(nanoseconds::rep)( + ((f64) timestamp_period) * + (f64) (pipeline_timestamp_query_results[1] - pipeline_timestamp_query_results[0]))}; } ASH_VK_CHECK(vkResetCommandBuffer(cmd_buffer, 0)); @@ -196,21 +204,27 @@ struct CanvasRenderer ASH_VK_CHECK(vkBeginCommandBuffer(cmd_buffer, &command_buffer_begin_info)); - vkCmdResetQueryPool(cmd_buffer, pipeline_statistics_query_pools[frame], 0, NPIPELINE_STATISTIC_QUERIES); - vkCmdResetQueryPool(cmd_buffer, pipeline_timestamp_query_pools[frame], 0, NPIPELINE_TIMESTAMP_QUERIES); + vkCmdResetQueryPool(cmd_buffer, pipeline_statistics_query_pools[frame], 0, + NPIPELINE_STATISTIC_QUERIES); + vkCmdResetQueryPool(cmd_buffer, pipeline_timestamp_query_pools[frame], 0, + NPIPELINE_TIMESTAMP_QUERIES); vkCmdBeginQuery(cmd_buffer, pipeline_statistics_query_pools[frame], 0, 0); - vkCmdWriteTimestamp(cmd_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, pipeline_timestamp_query_pools[frame], 0); - - VkClearValue clear_values[] = {{.color = VkClearColorValue{{0, 0, 0, 0}}}, {.depthStencil = VkClearDepthStencilValue{.depth = 1, .stencil = 0}}}; - - VkRenderPassBeginInfo render_pass_begin_info{.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - .pNext = nullptr, - .renderPass = render_pass, - .framebuffer = framebuffer, - .renderArea = VkRect2D{.offset = VkOffset2D{0, 0}, .extent = image_extent}, - .clearValueCount = AS(u32, std::size(clear_values)), - .pClearValues = clear_values}; + vkCmdWriteTimestamp(cmd_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + pipeline_timestamp_query_pools[frame], 0); + + VkClearValue clear_values[] = { + {.color = VkClearColorValue{{0, 0, 0, 0}}}, + {.depthStencil = VkClearDepthStencilValue{.depth = 1, .stencil = 0}}}; + + VkRenderPassBeginInfo render_pass_begin_info{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext = nullptr, + .renderPass = render_pass, + .framebuffer = framebuffer, + .renderArea = VkRect2D{.offset = VkOffset2D{0, 0}, .extent = image_extent}, + .clearValueCount = AS(u32, std::size(clear_values)), + .pClearValues = clear_values}; vkCmdBeginRenderPass(cmd_buffer, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); @@ -259,15 +273,18 @@ struct CanvasRenderer vkCmdSetViewport(cmd_buffer, 0, 1, &viewport); } - VkRect2D scissor{.offset = VkOffset2D{AS(i32, cmd.scissor.offset.x), AS(i32, cmd.scissor.offset.y)}, - .extent = VkExtent2D{AS(u32, cmd.scissor.extent.x), AS(u32, cmd.scissor.extent.y)}}; + VkRect2D scissor{ + .offset = VkOffset2D{AS(i32, cmd.scissor.offset.x), AS(i32, cmd.scissor.offset.y)}, + .extent = VkExtent2D{AS(u32, cmd.scissor.extent.x), AS(u32, cmd.scissor.extent.y)}}; if (memcmp(&scissor, &previouse_scissor, sizeof(VkRect2D)) != 0) { vkCmdSetScissor(cmd_buffer, 0, 1, &scissor); } - vkCmdPushConstants(cmd_buffer, pipeline.layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, PUSH_CONSTANT_SIZE, cmd.push_constant); + vkCmdPushConstants(cmd_buffer, pipeline.layout, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, + PUSH_CONSTANT_SIZE, cmd.push_constant); VkDescriptorSet descriptor_sets[NIMAGES_PER_DRAWCALL]; @@ -280,10 +297,12 @@ struct CanvasRenderer if (!stx::Span{descriptor_sets}.equals(stx::Span{previous_descriptor_sets})) { - vkCmdBindDescriptorSets(cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, NIMAGES_PER_DRAWCALL, descriptor_sets, 0, nullptr); + vkCmdBindDescriptorSets(cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, + NIMAGES_PER_DRAWCALL, descriptor_sets, 0, nullptr); } - vkCmdDrawIndexed(cmd_buffer, cmd.nindices, cmd.ninstances, first_index, (i32) vertex_offset, cmd.first_instance); + vkCmdDrawIndexed(cmd_buffer, cmd.nindices, cmd.ninstances, first_index, (i32) vertex_offset, + cmd.first_instance); first_index += cmd.nindices; vertex_offset += cmd.nvertices; @@ -297,7 +316,8 @@ struct CanvasRenderer vkCmdEndRenderPass(cmd_buffer); vkCmdEndQuery(cmd_buffer, pipeline_statistics_query_pools[frame], 0); - vkCmdWriteTimestamp(cmd_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, pipeline_timestamp_query_pools[frame], 1); + vkCmdWriteTimestamp(cmd_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + pipeline_timestamp_query_pools[frame], 1); ASH_VK_CHECK(vkEndCommandBuffer(cmd_buffer)); diff --git a/ashura/include/ashura/vulkan_context.h b/ashura/include/ashura/vulkan_context.h index 2e45ce84d..07ca669d6 100644 --- a/ashura/include/ashura/vulkan_context.h +++ b/ashura/include/ashura/vulkan_context.h @@ -23,12 +23,12 @@ namespace vk struct RenderImage { - Image image; - ImageFormat format = ImageFormat::Rgba8888; // requested format from the frontend - VkFormat gpu_format = VK_FORMAT_R8G8B8A8_UNORM; // format used to store texture on GPU - VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; - VkImageLayout dst_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - ash::Extent extent; + Image image; + ImageFormat format = ImageFormat::Rgba8888; // requested format from the frontend + VkFormat gpu_format = VK_FORMAT_R8G8B8A8_UNORM; // format used to store texture on GPU + VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED; + VkImageLayout dst_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + ash::Extent extent; stx::Option staging_buffer; VkDescriptorSet descriptor_set = VK_NULL_HANDLE; bool needs_upload = false; @@ -47,12 +47,12 @@ struct RenderResourceManager { static constexpr u32 NMAX_IMAGE_DESCRIPTOR_SETS = 1024; - VkCommandPool cmd_pool = VK_NULL_HANDLE; - VkCommandBuffer cmd_buffer = VK_NULL_HANDLE; - VkFence fence = VK_NULL_HANDLE; - VkDescriptorPool descriptor_pool = VK_NULL_HANDLE; - VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE; - VkSampler sampler = VK_NULL_HANDLE; // ideally list of samplers of different types + VkCommandPool cmd_pool = VK_NULL_HANDLE; + VkCommandBuffer cmd_buffer = VK_NULL_HANDLE; + VkFence fence = VK_NULL_HANDLE; + VkDescriptorPool descriptor_pool = VK_NULL_HANDLE; + VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE; + VkSampler sampler = VK_NULL_HANDLE; // ideally list of samplers of different types stx::Option> queue; std::map images; u64 next_image_id = 0; @@ -62,30 +62,34 @@ struct RenderResourceManager queue = stx::Some(std::move(aqueue)); VkDevice dev = queue.value()->device->dev; - VkCommandPoolCreateInfo cmd_pool_create_info{.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, - .pNext = nullptr, - .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, - .queueFamilyIndex = queue.value()->info.family.index}; + VkCommandPoolCreateInfo cmd_pool_create_info{ + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + .queueFamilyIndex = queue.value()->info.family.index}; ASH_VK_CHECK(vkCreateCommandPool(dev, &cmd_pool_create_info, nullptr, &cmd_pool)); - VkCommandBufferAllocateInfo cmd_buffer_allocate_info{.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .pNext = nullptr, - .commandPool = cmd_pool, - .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, - .commandBufferCount = 1}; + VkCommandBufferAllocateInfo cmd_buffer_allocate_info{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .pNext = nullptr, + .commandPool = cmd_pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1}; ASH_VK_CHECK(vkAllocateCommandBuffers(dev, &cmd_buffer_allocate_info, &cmd_buffer)); - VkFenceCreateInfo fence_create_info{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = nullptr, .flags = 0}; + VkFenceCreateInfo fence_create_info{ + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = nullptr, .flags = 0}; ASH_VK_CHECK(vkCreateFence(dev, &fence_create_info, nullptr, &fence)); - VkDescriptorSetLayoutBinding descriptor_set_bindings[] = {{.binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, - .pImmutableSamplers = nullptr}}; + VkDescriptorSetLayoutBinding descriptor_set_bindings[] = { + {.binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = nullptr}}; VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, @@ -95,37 +99,43 @@ struct RenderResourceManager .pBindings = descriptor_set_bindings, }; - ASH_VK_CHECK(vkCreateDescriptorSetLayout(dev, &descriptor_set_layout_create_info, nullptr, &descriptor_set_layout)); - - VkDescriptorPoolSize descriptor_pool_sizes[] = {{.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = NMAX_IMAGE_DESCRIPTOR_SETS}}; - - VkDescriptorPoolCreateInfo descriptor_pool_create_info{.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .pNext = nullptr, - .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, - .maxSets = NMAX_IMAGE_DESCRIPTOR_SETS, - .poolSizeCount = AS(u32, std::size(descriptor_pool_sizes)), - .pPoolSizes = descriptor_pool_sizes}; - - ASH_VK_CHECK(vkCreateDescriptorPool(dev, &descriptor_pool_create_info, nullptr, &descriptor_pool)); - - VkSamplerCreateInfo sampler_create_info{.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .magFilter = VK_FILTER_LINEAR, - .minFilter = VK_FILTER_LINEAR, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, - .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, - .mipLodBias = 0, - .anisotropyEnable = VK_TRUE, - .maxAnisotropy = queue.value()->device->phy_dev->properties.limits.maxSamplerAnisotropy, - .compareEnable = VK_FALSE, - .compareOp = VK_COMPARE_OP_ALWAYS, - .minLod = 0, - .maxLod = 0, - .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK, - .unnormalizedCoordinates = VK_FALSE}; + ASH_VK_CHECK(vkCreateDescriptorSetLayout(dev, &descriptor_set_layout_create_info, nullptr, + &descriptor_set_layout)); + + VkDescriptorPoolSize descriptor_pool_sizes[] = { + {.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = NMAX_IMAGE_DESCRIPTOR_SETS}}; + + VkDescriptorPoolCreateInfo descriptor_pool_create_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, + .maxSets = NMAX_IMAGE_DESCRIPTOR_SETS, + .poolSizeCount = AS(u32, std::size(descriptor_pool_sizes)), + .pPoolSizes = descriptor_pool_sizes}; + + ASH_VK_CHECK( + vkCreateDescriptorPool(dev, &descriptor_pool_create_info, nullptr, &descriptor_pool)); + + VkSamplerCreateInfo sampler_create_info{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .magFilter = VK_FILTER_LINEAR, + .minFilter = VK_FILTER_LINEAR, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .mipLodBias = 0, + .anisotropyEnable = VK_TRUE, + .maxAnisotropy = queue.value()->device->phy_dev->properties.limits.maxSamplerAnisotropy, + .compareEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_ALWAYS, + .minLod = 0, + .maxLod = 0, + .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK, + .unnormalizedCoordinates = VK_FALSE}; ASH_VK_CHECK(vkCreateSampler(dev, &sampler_create_info, nullptr, &sampler)); } @@ -227,7 +237,8 @@ struct RenderResourceManager u8 *out = rep_dst.span.data(); usize const src_row_bytes = src.row_bytes(); - for (usize irow = 0; irow < src.extent.height; irow++, in += src.pitch, out += rep_dst.pitch) + for (usize irow = 0; irow < src.extent.height; + irow++, in += src.pitch, out += rep_dst.pitch) { u8 const *in_row = in; u8 *out_row = out; @@ -256,31 +267,34 @@ struct RenderResourceManager gfx::image id = next_image_id; next_image_id++; - CommandQueue const &queue = *this->queue.value(); - VkDevice dev = queue.device->dev; - VkPhysicalDeviceMemoryProperties const &memory_properties = queue.device->phy_dev->memory_properties; + CommandQueue const &queue = *this->queue.value(); + VkDevice dev = queue.device->dev; + VkPhysicalDeviceMemoryProperties const &memory_properties = + queue.device->phy_dev->memory_properties; ASH_CHECK(image_view.extent.is_visible(), "Encounted unsupported zero extent image"); ImageFormat rep_format = to_rep_format(image_view.format); VkFormat rep_format_vk = to_vk(rep_format); - VkImageCreateInfo create_info{ - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .imageType = VK_IMAGE_TYPE_2D, - .format = rep_format_vk, - .extent = VkExtent3D{.width = image_view.extent.width, .height = image_view.extent.height, .depth = 1}, - .mipLevels = 1, - .arrayLayers = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED}; + VkImageCreateInfo create_info{.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .imageType = VK_IMAGE_TYPE_2D, + .format = rep_format_vk, + .extent = VkExtent3D{.width = image_view.extent.width, + .height = image_view.extent.height, + .depth = 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED}; VkImage image; @@ -290,7 +304,9 @@ struct RenderResourceManager vkGetImageMemoryRequirements(dev, image, &memory_requirements); - u32 memory_type_index = find_suitable_memory_type(memory_properties, memory_requirements, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT).unwrap(); + u32 memory_type_index = find_suitable_memory_type(memory_properties, memory_requirements, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + .unwrap(); VkMemoryAllocateInfo alloc_info{.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = nullptr, @@ -314,31 +330,45 @@ struct RenderResourceManager .g = VK_COMPONENT_SWIZZLE_IDENTITY, .b = VK_COMPONENT_SWIZZLE_IDENTITY, .a = VK_COMPONENT_SWIZZLE_IDENTITY}, - .subresourceRange = VkImageSubresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1}}; + .subresourceRange = VkImageSubresourceRange{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1}}; VkImageView view; ASH_VK_CHECK(vkCreateImageView(dev, &view_create_info, nullptr, &view)); - Buffer staging_buffer = create_host_visible_buffer(dev, memory_properties, fitted_byte_size(image_view.extent, rep_format), VK_BUFFER_USAGE_TRANSFER_SRC_BIT); + Buffer staging_buffer = create_host_visible_buffer( + dev, memory_properties, fitted_byte_size(image_view.extent, rep_format), + VK_BUFFER_USAGE_TRANSFER_SRC_BIT); auto begin = std::chrono::steady_clock::now(); - copy_image_to_GPU_Buffer(image_view, ImageView{.span = staging_buffer.span(), .extent = image_view.extent, .pitch = image_view.extent.width * pixel_byte_size(rep_format), .format = rep_format}); - ASH_LOG_INFO(Vulkan_RenderResourceManager, "Copied Image #{} to Host Visible Staging Buffer in {} ms", id, (std::chrono::steady_clock::now() - begin).count() / 1'000'000.0f); - - VkDescriptorSetAllocateInfo descriptor_set_allocate_info{.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .pNext = nullptr, - .descriptorPool = descriptor_pool, - .descriptorSetCount = 1, - .pSetLayouts = &descriptor_set_layout}; + copy_image_to_GPU_Buffer( + image_view, ImageView{.span = staging_buffer.span(), + .extent = image_view.extent, + .pitch = image_view.extent.width * pixel_byte_size(rep_format), + .format = rep_format}); + ASH_LOG_INFO(Vulkan_RenderResourceManager, + "Copied Image #{} to Host Visible Staging Buffer in {} ms", id, + (std::chrono::steady_clock::now() - begin).count() / 1'000'000.0f); + + VkDescriptorSetAllocateInfo descriptor_set_allocate_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .pNext = nullptr, + .descriptorPool = descriptor_pool, + .descriptorSetCount = 1, + .pSetLayouts = &descriptor_set_layout}; VkDescriptorSet descriptor_set; ASH_VK_CHECK(vkAllocateDescriptorSets(dev, &descriptor_set_allocate_info, &descriptor_set)); { - VkDescriptorImageInfo image_info{.sampler = sampler, .imageView = view, .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}; + VkDescriptorImageInfo image_info{.sampler = sampler, + .imageView = view, + .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL}; VkWriteDescriptorSet write{.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .pNext = nullptr, @@ -354,19 +384,24 @@ struct RenderResourceManager vkUpdateDescriptorSets(dev, 1, &write, 0, nullptr); } - images.emplace(id, RenderImage{.image = Image{.image = image, .view = view, .memory = memory, .dev = dev}, - .format = image_view.format, - .gpu_format = rep_format_vk, - .layout = VK_IMAGE_LAYOUT_UNDEFINED, - .dst_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - .extent = image_view.extent, - .staging_buffer = stx::Some(std::move(staging_buffer)), - .descriptor_set = descriptor_set, - .needs_upload = true, - .needs_delete = false, - .is_real_time = is_real_time}); - - ASH_LOG_INFO(Vulkan_RenderResourceManager, "Created {}{} {}x{} Image #{} with format={} and size={} bytes", is_real_time ? "" : "non-", "real-time", image_view.extent.width, image_view.extent.height, id, string_VkFormat(rep_format_vk), memory_requirements.size); + images.emplace( + id, RenderImage{.image = Image{.image = image, .view = view, .memory = memory, .dev = dev}, + .format = image_view.format, + .gpu_format = rep_format_vk, + .layout = VK_IMAGE_LAYOUT_UNDEFINED, + .dst_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + .extent = image_view.extent, + .staging_buffer = stx::Some(std::move(staging_buffer)), + .descriptor_set = descriptor_set, + .needs_upload = true, + .needs_delete = false, + .is_real_time = is_real_time}); + + ASH_LOG_INFO(Vulkan_RenderResourceManager, + "Created {}{} {}x{} Image #{} with format={} and size={} bytes", + is_real_time ? "" : "non-", "real-time", image_view.extent.width, + image_view.extent.height, id, string_VkFormat(rep_format_vk), + memory_requirements.size); return id; } @@ -384,19 +419,23 @@ struct RenderResourceManager if (rimage.needs_upload || rimage.is_real_time) { - copy_image_to_GPU_Buffer(view, ImageView{.span = rimage.staging_buffer.value().span(), - .extent = rimage.extent, - .pitch = rimage.extent.width * pixel_byte_size(rep_format), - .format = rep_format}); + copy_image_to_GPU_Buffer( + view, ImageView{.span = rimage.staging_buffer.value().span(), + .extent = rimage.extent, + .pitch = rimage.extent.width * pixel_byte_size(rep_format), + .format = rep_format}); } else { CommandQueue const &queue = *this->queue.value(); - Buffer staging_buffer = create_host_visible_buffer(queue.device->dev, queue.device->phy_dev->memory_properties, fitted_byte_size(rimage.extent, rep_format), VK_BUFFER_USAGE_TRANSFER_SRC_BIT); - copy_image_to_GPU_Buffer(view, ImageView{.span = staging_buffer.span(), - .extent = rimage.extent, - .pitch = rimage.extent.width * pixel_byte_size(rep_format), - .format = rep_format}); + Buffer staging_buffer = create_host_visible_buffer( + queue.device->dev, queue.device->phy_dev->memory_properties, + fitted_byte_size(rimage.extent, rep_format), VK_BUFFER_USAGE_TRANSFER_SRC_BIT); + copy_image_to_GPU_Buffer( + view, ImageView{.span = staging_buffer.span(), + .extent = rimage.extent, + .pitch = rimage.extent.width * pixel_byte_size(rep_format), + .format = rep_format}); rimage.staging_buffer = stx::Some(std::move(staging_buffer)); } rimage.needs_upload = true; @@ -431,10 +470,11 @@ struct RenderResourceManager CommandQueue const &queue = *this->queue.value(); VkDevice dev = queue.device->dev; - VkCommandBufferBeginInfo cmd_buffer_begin_info{.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .pNext = nullptr, - .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, - .pInheritanceInfo = nullptr}; + VkCommandBufferBeginInfo cmd_buffer_begin_info{ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + .pInheritanceInfo = nullptr}; ASH_VK_CHECK(vkBeginCommandBuffer(cmd_buffer, &cmd_buffer_begin_info)); @@ -452,19 +492,32 @@ struct RenderResourceManager .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = entry.second.image.image, - .subresourceRange = VkImageSubresourceRange{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1}}; - - vkCmdPipelineBarrier(cmd_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, 0, 0, nullptr, 1, &pre_upload_barrier); - - VkBufferImageCopy copy{ - .bufferOffset = 0, - .bufferRowLength = 0, - .bufferImageHeight = 0, - .imageSubresource = VkImageSubresourceLayers{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .baseArrayLayer = 0, .layerCount = 1}, - .imageOffset = VkOffset3D{.x = 0, .y = 0, .z = 0}, - .imageExtent = VkExtent3D{.width = entry.second.extent.width, .height = entry.second.extent.height, .depth = 1}}; - - vkCmdCopyBufferToImage(cmd_buffer, entry.second.staging_buffer.value().buffer, entry.second.image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©); + .subresourceRange = VkImageSubresourceRange{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1}}; + + vkCmdPipelineBarrier(cmd_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, 0, 0, + nullptr, 1, &pre_upload_barrier); + + VkBufferImageCopy copy{.bufferOffset = 0, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = + VkImageSubresourceLayers{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1}, + .imageOffset = VkOffset3D{.x = 0, .y = 0, .z = 0}, + .imageExtent = VkExtent3D{.width = entry.second.extent.width, + .height = entry.second.extent.height, + .depth = 1}}; + + vkCmdCopyBufferToImage(cmd_buffer, entry.second.staging_buffer.value().buffer, + entry.second.image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, + ©); VkImageMemoryBarrier post_upload_barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, @@ -476,9 +529,15 @@ struct RenderResourceManager .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = entry.second.image.image, - .subresourceRange = VkImageSubresourceRange{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1}}; - - vkCmdPipelineBarrier(cmd_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, 1, &post_upload_barrier); + .subresourceRange = VkImageSubresourceRange{.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1}}; + + vkCmdPipelineBarrier(cmd_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, + nullptr, 0, nullptr, 1, &post_upload_barrier); } } @@ -557,9 +616,11 @@ struct RenderResourceManager // VkImageFormatProperties image_format_properties; // ASH_VK_CHECK(vkGetPhysicalDeviceImageFormatProperties(queue.value()->device->phy_dev->phy_device, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT, 0, &image_format_properties)); // auto [atlas, image_buffer] = render_font_atlas(font, font_height, extent{image_format_properties.maxExtent.width, image_format_properties.maxExtent.height}); - ASH_LOG_INFO(Vulkan_RenderResourceManager, "Uploading Atlas for Font: {} to GPU", font.postscript_name.c_str()); + ASH_LOG_INFO(Vulkan_RenderResourceManager, "Uploading Atlas for Font: {} to GPU", + font.postscript_name.c_str()); gfx::image image = add_image(atlas, false); - ASH_LOG_INFO(Vulkan_RenderResourceManager, "Uploaded Atlas For Font: {} to GPU", font.postscript_name.c_str()); + ASH_LOG_INFO(Vulkan_RenderResourceManager, "Uploaded Atlas For Font: {} to GPU", + font.postscript_name.c_str()); return image; } }; @@ -595,11 +656,12 @@ struct CanvasPipelineManager { dev = adev; - VkDescriptorSetLayoutBinding descriptor_set_bindings[] = {{.binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, - .pImmutableSamplers = nullptr}}; + VkDescriptorSetLayoutBinding descriptor_set_bindings[] = { + {.binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = nullptr}}; VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, @@ -608,7 +670,8 @@ struct CanvasPipelineManager .bindingCount = std::size(descriptor_set_bindings), .pBindings = descriptor_set_bindings}; - ASH_VK_CHECK(vkCreateDescriptorSetLayout(dev, &descriptor_set_layout_create_info, nullptr, &descriptor_set_layout)); + ASH_VK_CHECK(vkCreateDescriptorSetLayout(dev, &descriptor_set_layout_create_info, nullptr, + &descriptor_set_layout)); } void add_pipeline(CanvasPipelineSpec const &spec) @@ -620,7 +683,8 @@ struct CanvasPipelineManager .flags = 0, .codeSize = spec.vertex_shader.size_bytes(), .pCode = spec.vertex_shader.data()}; - ASH_VK_CHECK(vkCreateShaderModule(dev, &vertex_shader_module_create_info, nullptr, &vertex_shader)); + ASH_VK_CHECK( + vkCreateShaderModule(dev, &vertex_shader_module_create_info, nullptr, &vertex_shader)); VkShaderModule fragment_shader; VkShaderModuleCreateInfo fragment_shader_module_create_info{ @@ -629,7 +693,8 @@ struct CanvasPipelineManager .flags = 0, .codeSize = spec.fragment_shader.size_bytes(), .pCode = spec.fragment_shader.data()}; - ASH_VK_CHECK(vkCreateShaderModule(dev, &fragment_shader_module_create_info, nullptr, &fragment_shader)); + ASH_VK_CHECK( + vkCreateShaderModule(dev, &fragment_shader_module_create_info, nullptr, &fragment_shader)); pipelines.emplace(spec.name, CanvasPipeline{.vertex_shader = vertex_shader, .fragment_shader = fragment_shader, @@ -637,12 +702,22 @@ struct CanvasPipelineManager } /// re-build pipelines to meet renderpass specification - void rebuild_for_renderpass(VkRenderPass target_render_pass, VkSampleCountFlagBits msaa_sample_count) + void rebuild_for_renderpass(VkRenderPass target_render_pass, + VkSampleCountFlagBits msaa_sample_count) { static constexpr VkVertexInputAttributeDescription vertex_input_attributes[] = { - {.location = 0, .binding = 0, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(Vertex2d, position)}, - {.location = 1, .binding = 0, .format = VK_FORMAT_R32G32_SFLOAT, .offset = offsetof(Vertex2d, uv)}, - {.location = 2, .binding = 0, .format = VK_FORMAT_R32G32B32A32_SFLOAT, .offset = offsetof(Vertex2d, color)}}; + {.location = 0, + .binding = 0, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = offsetof(Vertex2d, position)}, + {.location = 1, + .binding = 0, + .format = VK_FORMAT_R32G32_SFLOAT, + .offset = offsetof(Vertex2d, uv)}, + {.location = 2, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = offsetof(Vertex2d, color)}}; VkDescriptorSetLayout descriptor_sets_layout[NIMAGES_PER_DRAWCALL]; stx::Span{descriptor_sets_layout}.fill(descriptor_set_layout); @@ -657,15 +732,9 @@ struct CanvasPipelineManager Pipeline pipeline; - pipeline.build(dev, - p.second.vertex_shader, - p.second.fragment_shader, - target_render_pass, - msaa_sample_count, - descriptor_sets_layout, - vertex_input_attributes, - sizeof(Vertex2d), - PUSH_CONSTANT_SIZE); + pipeline.build(dev, p.second.vertex_shader, p.second.fragment_shader, target_render_pass, + msaa_sample_count, descriptor_sets_layout, vertex_input_attributes, + sizeof(Vertex2d), PUSH_CONSTANT_SIZE); p.second.pipeline = stx::Some(std::move(pipeline)); diff --git a/ashura/include/ashura/widget.h b/ashura/include/ashura/widget.h index 0ea5ab6c9..2018ce8e9 100644 --- a/ashura/include/ashura/widget.h +++ b/ashura/include/ashura/widget.h @@ -49,10 +49,12 @@ struct DragData struct Widget { Widget() - {} + { + } virtual ~Widget() - {} + { + } /// @brief get child widgets /// @param ctx @@ -90,7 +92,8 @@ struct Widget /// @param children_sizes sizes of the child widgets /// @param[out] children_positions positions of the children widget on the parent /// @return this widget's fitted extent - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, stx::Span children_positions) { return Vec2{0, 0}; } @@ -110,7 +113,8 @@ struct Widget /// @param allocated_visibility /// @param[out] children_allocation visibility assigned to children /// @return - virtual Visibility get_visibility(Context &ctx, Visibility allocated_visibility, stx::Span children_allocation) + virtual Visibility get_visibility(Context &ctx, Visibility allocated_visibility, + stx::Span children_allocation) { children_allocation.fill(allocated_visibility); return allocated_visibility; @@ -159,18 +163,21 @@ struct Widget /// @param ctx /// @param interval time passed since last call to this method virtual void tick(Context &ctx, std::chrono::nanoseconds interval) - {} + { + } /// @brief called on every frame the widget is viewed on the viewport. /// @param ctx virtual void on_view_hit(Context &ctx) - {} + { + } /// @brief called on every frame that the widget is not seen on the viewport /// this can be because it has hidden visibility, is clipped away, or parent positioned out of the visible region /// @param ctx virtual void on_view_miss(Context &ctx) - {} + { + } // this needs to happen before mouse actions as some widgets .i.e. some widgets don't need to intercept or receive mouse events virtual bool hit_test(Context &ctx, Vec2 mouse_position) @@ -184,20 +191,25 @@ struct Widget } virtual void on_mouse_down(Context &ctx, MouseButton button, Vec2 mouse_position, u32 nclicks) - {} + { + } virtual void on_mouse_up(Context &ctx, MouseButton button, Vec2 mouse_position, u32 nclicks) - {} + { + } // TODO(lamarrr): how do we fix translation and zooming? i.e. positioning once transform is applied virtual void on_mouse_move(Context &ctx, Vec2 mouse_position, Vec2 translation) - {} + { + } virtual void on_mouse_enter(Context &ctx, Vec2 mouse_position) - {} + { + } virtual void on_mouse_leave(Context &ctx, stx::Option mouse_position) - {} + { + } // virtual bool on_mouse_wheel(Context& ctx, Vec2 translation, Vec2 mouse_position?). propagates up @@ -212,12 +224,15 @@ struct Widget /// @param ctx /// @param mouse_position current global drag position /// @param translation difference between this drag update and the last drag update position - virtual void on_drag_update(Context &ctx, Vec2 mouse_position, Vec2 translation, DragData const &drag_data) - {} + virtual void on_drag_update(Context &ctx, Vec2 mouse_position, Vec2 translation, + DragData const &drag_data) + { + } /// the drop of the drag data has been ended virtual void on_drag_end(Context &ctx, Vec2 mouse_position) - {} + { + } /// this widget has begun receiving drag data, i.e. it has been dragged onto /// @@ -228,11 +243,13 @@ struct Widget /// this widget has previously begun receiving drag data, but the mouse is still dragging within it virtual void on_drag_over(Context &ctx, DragData const &drag_data) - {} + { + } /// the drag event has left this widget virtual void on_drag_leave(Context &ctx, stx::Option mouse_position) - {} + { + } /// drop of drag data on this widget virtual bool on_drop(Context &ctx, Vec2 mouse_position, DragData const &drag_data) @@ -242,34 +259,42 @@ struct Widget // virtual void on_tap(Context &ctx) - {} + { + } // virtual void on_touch_cancel(Context &ctx) - {} + { + } // virtual void on_touch_end(Context &ctx) - {} + { + } // virtual void on_touch_move(Context &ctx) - {} + { + } // virtual void on_touch_start(Context &ctx) - {} + { + } // virtual void on_touch_enter(Context &ctx) - {} + { + } // virtual void on_touch_leave(Context &ctx) - {} + { + } - stx::Option id; /// id used to recognise the widget. checked every frame. if one is not present or removed. a new uuid is generated and assigned. - Rect area; /// + stx::Option + id; /// id used to recognise the widget. checked every frame. if one is not present or removed. a new uuid is generated and assigned. + Rect area; /// }; inline Widget *__find_widget_recursive(Context &ctx, Widget &widget, uuid id) diff --git a/ashura/include/ashura/widget_system.h b/ashura/include/ashura/widget_system.h index 845c3bdaa..2ff82a538 100644 --- a/ashura/include/ashura/widget_system.h +++ b/ashura/include/ashura/widget_system.h @@ -12,7 +12,8 @@ namespace ash struct WidgetSystem { - static void __assign_widget_uuids_recursive(Context &ctx, Widget &widget, PrngUuidGenerator &generator) + static void __assign_widget_uuids_recursive(Context &ctx, Widget &widget, + PrngUuidGenerator &generator) { if (widget.id.is_none()) { @@ -74,7 +75,14 @@ struct WidgetSystem bool accepted_drop = hit_widget->on_drop(ctx, event.position, drag_data.value()); if (!accepted_drop) { - drag_source.match([&](uuid source) { if(stx::Option source_widget = ctx.find_widget(source)){ source_widget.value()->on_drag_end(ctx, event.position ); } }, []() {}); + drag_source.match( + [&](uuid source) { + if (stx::Option source_widget = ctx.find_widget(source)) + { + source_widget.value()->on_drag_end(ctx, event.position); + } + }, + []() {}); } } else @@ -104,9 +112,13 @@ struct WidgetSystem if (drag_data.is_some() && drag_source.is_some()) { - ctx - .find_widget(drag_source.value()) - .match([&](Widget *pdrag_source) { pdrag_source->on_drag_update(ctx, event.position, event.translation, drag_data.value()); }, []() {}); + ctx.find_widget(drag_source.value()) + .match( + [&](Widget *pdrag_source) { + pdrag_source->on_drag_update(ctx, event.position, event.translation, + drag_data.value()); + }, + []() {}); } if (Widget *phit_widget = tree.hit(ctx, event.position)) @@ -133,19 +145,26 @@ struct WidgetSystem hit_widget = stx::Some(phit_widget->id.copy().unwrap()); } - if (last_hit_widget.is_some() && (hit_widget.is_none() || hit_widget.value() != last_hit_widget.value())) + if (last_hit_widget.is_some() && + (hit_widget.is_none() || hit_widget.value() != last_hit_widget.value())) { if (drag_data.is_some()) { - ctx - .find_widget(last_hit_widget.value()) - .match([&](Widget *plast_hit_widget) { plast_hit_widget->on_drag_leave(ctx, stx::Some(Vec2{event.position})); }, []() {}); + ctx.find_widget(last_hit_widget.value()) + .match( + [&](Widget *plast_hit_widget) { + plast_hit_widget->on_drag_leave(ctx, stx::Some(Vec2{event.position})); + }, + []() {}); } else { - ctx - .find_widget(last_hit_widget.value()) - .match([&](Widget *plast_hit_widget) { plast_hit_widget->on_mouse_leave(ctx, stx::Some(Vec2{event.position})); }, []() {}); + ctx.find_widget(last_hit_widget.value()) + .match( + [&](Widget *plast_hit_widget) { + plast_hit_widget->on_mouse_leave(ctx, stx::Some(Vec2{event.position})); + }, + []() {}); } } @@ -157,15 +176,19 @@ struct WidgetSystem { if (last_hit_widget.is_some()) { - ctx - .find_widget(last_hit_widget.value()) - .match([&](Widget *plast_hit_widget) { - if(drag_data.is_some()) - { - plast_hit_widget->on_drag_leave(ctx, stx::None); - } else{ - plast_hit_widget->on_mouse_leave(ctx, stx::None); - }; }, []() {}); + ctx.find_widget(last_hit_widget.value()) + .match( + [&](Widget *plast_hit_widget) { + if (drag_data.is_some()) + { + plast_hit_widget->on_drag_leave(ctx, stx::None); + } + else + { + plast_hit_widget->on_mouse_leave(ctx, stx::None); + }; + }, + []() {}); last_hit_widget = stx::None; } } diff --git a/ashura/include/ashura/widget_tree.h b/ashura/include/ashura/widget_tree.h index bc124f3f6..7fce97c6b 100644 --- a/ashura/include/ashura/widget_tree.h +++ b/ashura/include/ashura/widget_tree.h @@ -71,34 +71,45 @@ struct WidgetTree for (usize i = 0; i < element.children.size(); i++) { - element.children_sizes[i] = __fit_recursive(ctx, element.children[i], element.children_allocations[i]); + element.children_sizes[i] = + __fit_recursive(ctx, element.children[i], element.children_allocations[i]); } - return element.widget->area.extent = element.widget->fit(ctx, allocated_size, element.children_allocations, element.children_sizes, element.children_positions); + return element.widget->area.extent = + element.widget->fit(ctx, allocated_size, element.children_allocations, + element.children_sizes, element.children_positions); } - static void __absolute_position_recursive(Context &ctx, WidgetElement &element, Vec2 allocated_position) + static void __absolute_position_recursive(Context &ctx, WidgetElement &element, + Vec2 allocated_position) { Vec2 position = element.widget->position(ctx, allocated_position); element.widget->area.offset = position; for (usize i = 0; i < element.children.size(); i++) { - __absolute_position_recursive(ctx, element.children[i], position + element.children_positions[i]); + __absolute_position_recursive(ctx, element.children[i], + position + element.children_positions[i]); } } - void __build_render_recursive(Context &ctx, WidgetElement &element, Visibility allocated_visibility, i32 allocated_z_index, Rect allocated_clip, Rect view_region) + void __build_render_recursive(Context &ctx, WidgetElement &element, + Visibility allocated_visibility, i32 allocated_z_index, + Rect allocated_clip, Rect view_region) { - stx::Span children = element.children; - Visibility visibility = element.widget->get_visibility(ctx, allocated_visibility, element.children_visibility); - i32 z_index = element.widget->z_stack(ctx, allocated_z_index, element.children_z_indices); - Rect clip = element.widget->clip(ctx, allocated_clip, element.children_clips); - - if (visibility == Visibility::Visible && clip.overlaps(view_region) && view_region.overlaps(element.widget->area)) + stx::Span children = element.children; + Visibility visibility = + element.widget->get_visibility(ctx, allocated_visibility, element.children_visibility); + i32 z_index = element.widget->z_stack(ctx, allocated_z_index, element.children_z_indices); + Rect clip = element.widget->clip(ctx, allocated_clip, element.children_clips); + + if (visibility == Visibility::Visible && clip.overlaps(view_region) && + view_region.overlaps(element.widget->area)) { element.widget->on_view_hit(ctx); - render_elements.push(WidgetRenderElement{.widget = element.widget, .z_index = z_index, .clip = clip}).unwrap(); + render_elements + .push(WidgetRenderElement{.widget = element.widget, .z_index = z_index, .clip = clip}) + .unwrap(); } else { @@ -107,7 +118,9 @@ struct WidgetTree for (usize i = 0; i < children.size(); i++) { - __build_render_recursive(ctx, element.children[i], element.children_visibility[i], element.children_z_indices[i], element.children_clips[i], view_region); + __build_render_recursive(ctx, element.children[i], element.children_visibility[i], + element.children_z_indices[i], element.children_clips[i], + view_region); } } @@ -133,13 +146,14 @@ struct WidgetTree { render_elements.clear(); __build_render_recursive(ctx, root, Visibility::Visible, 0, root.widget->area, view_region); - render_elements.span().sort([](WidgetRenderElement const &a, WidgetRenderElement const &b) { return a.z_index < b.z_index; }); + render_elements.span().sort([](WidgetRenderElement const &a, WidgetRenderElement const &b) { + return a.z_index < b.z_index; + }); Vec2 scale = viewport_size / view_region.extent; Vec2 translation = 0 - view_region.offset; - canvas - .restart(viewport_size) + canvas.restart(viewport_size) .global_translate(translation.x, translation.y) .global_scale(scale.x, scale.y); @@ -148,9 +162,7 @@ struct WidgetTree Rect scissor; scissor.offset = (element.clip.offset - view_region.offset) * scale; scissor.extent = element.clip.extent * scale; - canvas - .save() - .scissor(scissor); + canvas.save().scissor(scissor); element.widget->draw(ctx, canvas); canvas.restore(); } @@ -161,7 +173,8 @@ struct WidgetTree for (usize i = render_elements.size(); i > 0;) { i--; - if (render_elements[i].widget->area.contains(position) && render_elements[i].widget->hit_test(ctx, position)) + if (render_elements[i].widget->area.contains(position) && + render_elements[i].widget->hit_test(ctx, position)) { return render_elements[i].widget; } diff --git a/ashura/include/ashura/widgets/box.h b/ashura/include/ashura/widgets/box.h index d886ca208..b1b417c0e 100644 --- a/ashura/include/ashura/widgets/box.h +++ b/ashura/include/ashura/widgets/box.h @@ -31,38 +31,40 @@ struct BoxProps struct Box : public Widget { template DerivedWidget> - Box(BoxProps iprops, DerivedWidget child) : - props{iprops} + Box(BoxProps iprops, DerivedWidget child) : props{iprops} { children.push(new DerivedWidget{std::move(child)}).unwrap(); } - Box(BoxProps iprops, Widget *child) : - props{iprops} + Box(BoxProps iprops, Widget *child) : props{iprops} { children.push_inplace(child).unwrap(); } - explicit Box(BoxProps iprops) : - props{iprops} - {} + explicit Box(BoxProps iprops) : props{iprops} + { + } STX_DISABLE_COPY(Box) STX_DEFAULT_MOVE(Box) - virtual void allocate_size(Context &ctx, Vec2 allocated_size, stx::Span children_allocation) override + virtual void allocate_size(Context &ctx, Vec2 allocated_size, + stx::Span children_allocation) override { Vec2 const box_size = props.border_thickness * 2 + props.padding.xy(); children_allocation.fill(max(allocated_size - box_size, Vec2{0, 0})); } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { if (children.size() > 0) { children_positions[0] = props.border_thickness + Vec2{props.padding.left, props.padding.top}; } - return props.frame.resolve(props.border_thickness * 2 + props.padding.xy() + (children.size() > 0 ? children_sizes[0] : Vec2{0, 0})); + return props.frame.resolve(props.border_thickness * 2 + props.padding.xy() + + (children.size() > 0 ? children_sizes[0] : Vec2{0, 0})); } virtual stx::Span get_children(Context &ctx) override @@ -73,18 +75,22 @@ struct Box : public Widget virtual void draw(Context &ctx, gfx::Canvas &canvas) override { Vec4 const border_radius = props.border_radius.resolve(area.extent); - if (props.background_color.is_visible() || ((!props.background_gradient.is_uniform()) && (props.background_gradient.begin.is_visible() || props.background_gradient.end.is_visible()))) + if (props.background_color.is_visible() || ((!props.background_gradient.is_uniform()) && + (props.background_gradient.begin.is_visible() || + props.background_gradient.end.is_visible()))) { Rect inner_area; inner_area.offset = area.offset + props.border_thickness * 0.88f; inner_area.extent = area.extent - props.border_thickness * 0.88f * 2; if (props.corner_shape == BoxCornerShape::Round) { - canvas.draw_round_rect_filled(inner_area, border_radius, 360, props.background_color, props.background_gradient); + canvas.draw_round_rect_filled(inner_area, border_radius, 360, props.background_color, + props.background_gradient); } else { - canvas.draw_bevel_rect_filled(inner_area, border_radius, props.background_color, props.background_gradient); + canvas.draw_bevel_rect_filled(inner_area, border_radius, props.background_color, + props.background_gradient); } } @@ -92,11 +98,13 @@ struct Box : public Widget { if (props.corner_shape == BoxCornerShape::Round) { - canvas.draw_round_rect_stroke(area, border_radius, 360, props.border_color, props.border_thickness); + canvas.draw_round_rect_stroke(area, border_radius, 360, props.border_color, + props.border_thickness); } else { - canvas.draw_bevel_rect_stroke(area, border_radius, props.border_color, props.border_thickness); + canvas.draw_bevel_rect_stroke(area, border_radius, props.border_color, + props.border_thickness); } } } diff --git a/ashura/include/ashura/widgets/checkbox.h b/ashura/include/ashura/widgets/checkbox.h index 9797631a9..9f7bb5c9b 100644 --- a/ashura/include/ashura/widgets/checkbox.h +++ b/ashura/include/ashura/widgets/checkbox.h @@ -23,19 +23,25 @@ struct CheckBox : public Widget using Callback = stx::UniqueFn; static void default_on_changed(CheckBox &checkbox, Context &ctx, bool new_value) - {} + { + } - CheckBox(Callback ion_changed = stx::fn::rc::make_unique_static(default_on_changed), bool default_value = false, CheckBoxProps iprops = CheckBoxProps{}) : + CheckBox(Callback ion_changed = stx::fn::rc::make_unique_static(default_on_changed), + bool default_value = false, CheckBoxProps iprops = CheckBoxProps{}) : on_changed{std::move(ion_changed)}, value{default_value}, props{iprops} - {} + { + } STX_DISABLE_COPY(CheckBox) STX_DEFAULT_MOVE(CheckBox) virtual ~CheckBox() override - {} + { + } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { return Vec2{props.extent, props.extent}; } @@ -49,8 +55,7 @@ struct CheckBox : public Widget if (value) { - canvas - .draw_rect_filled(area, props.box_color) + canvas.draw_rect_filled(area, props.box_color) .save() .scale(props.extent, props.extent) .draw_path(checkmark_path, area.offset, area.extent, 0.125f, false) @@ -67,7 +72,8 @@ struct CheckBox : public Widget return true; } - virtual void on_mouse_down(Context &ctx, MouseButton button, Vec2 mouse_position, u32 nclicks) override + virtual void on_mouse_down(Context &ctx, MouseButton button, Vec2 mouse_position, + u32 nclicks) override { if (button == MouseButton::Primary && !props.disabled) { diff --git a/ashura/include/ashura/widgets/flex.h b/ashura/include/ashura/widgets/flex.h index 4507d557f..7e451f6cf 100644 --- a/ashura/include/ashura/widgets/flex.h +++ b/ashura/include/ashura/widgets/flex.h @@ -14,18 +14,21 @@ namespace gui struct FlexProps { - Direction direction = Direction::H; // flex direction to layout children along - Wrap wrap = Wrap::Wrap; /// wrap to a new block or not - MainAlign main_align = MainAlign::Start; /// main-axis alignment. specifies how free space is used on the main axis - CrossAlign cross_align = CrossAlign::Start; /// cross-axis alignment. affects how free space is used on the cross axis - Constraint2D frame = Constraint2D::relative(1, 1); /// frame size to use for layout. this is not same as the actual extent of the flex + Direction direction = Direction::H; // flex direction to layout children along + Wrap wrap = Wrap::Wrap; /// wrap to a new block or not + MainAlign main_align = MainAlign:: + Start; /// main-axis alignment. specifies how free space is used on the main axis + CrossAlign cross_align = CrossAlign:: + Start; /// cross-axis alignment. affects how free space is used on the cross axis + Constraint2D frame = Constraint2D::relative( + 1, + 1); /// frame size to use for layout. this is not same as the actual extent of the flex }; struct Flex : public Widget { template ... DerivedWidget> - explicit Flex(FlexProps iprops, DerivedWidget... ichildren) : - props{iprops} + explicit Flex(FlexProps iprops, DerivedWidget... ichildren) : props{iprops} { update_children(std::move(ichildren)...); } @@ -74,12 +77,15 @@ struct Flex : public Widget return WidgetDebugInfo{.type = "Flex"}; } - virtual void allocate_size(Context &ctx, Vec2 allocated_size, stx::Span children_allocation) override + virtual void allocate_size(Context &ctx, Vec2 allocated_size, + stx::Span children_allocation) override { children_allocation.fill(props.frame.resolve(allocated_size)); } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { Vec2 frame = props.frame.resolve(allocated_size); Vec2 span; @@ -100,11 +106,14 @@ struct Flex : public Widget for (; iblock_end < children_sizes.size(); iblock_end++) { - if (props.wrap == Wrap::None || (props.wrap == Wrap::Wrap && (block_main_axis_extent + children_sizes[iblock_end].x <= frame.x))) + if (props.wrap == Wrap::None || + (props.wrap == Wrap::Wrap && + (block_main_axis_extent + children_sizes[iblock_end].x <= frame.x))) { children_positions[iblock_end].y = cross_axis_cursor; block_main_axis_extent += children_sizes[iblock_end].x; - block_cross_axis_extent = std::max(block_cross_axis_extent, children_sizes[iblock_end].y); + block_cross_axis_extent = + std::max(block_cross_axis_extent, children_sizes[iblock_end].y); } else { @@ -125,11 +134,14 @@ struct Flex : public Widget for (; iblock_end < children_sizes.size(); iblock_end++) { - if (props.wrap == Wrap::None || (props.wrap == Wrap::Wrap && (block_main_axis_extent + children_sizes[iblock_end].y <= frame.y))) + if (props.wrap == Wrap::None || + (props.wrap == Wrap::Wrap && + (block_main_axis_extent + children_sizes[iblock_end].y <= frame.y))) { children_positions[iblock_end].x = cross_axis_cursor; block_main_axis_extent += children_sizes[iblock_end].y; - block_cross_axis_extent = std::max(block_cross_axis_extent, children_sizes[iblock_end].x); + block_cross_axis_extent = + std::max(block_cross_axis_extent, children_sizes[iblock_end].x); } else { @@ -153,14 +165,16 @@ struct Flex : public Widget { for (usize iblock = i; iblock < iblock_end; iblock++) { - children_positions[iblock].y += (block_cross_axis_extent - children_sizes[iblock].y) / 2; + children_positions[iblock].y += + (block_cross_axis_extent - children_sizes[iblock].y) / 2; } } else { for (usize iblock = i; iblock < iblock_end; iblock++) { - children_positions[iblock].x += (block_cross_axis_extent - children_sizes[iblock].x) / 2; + children_positions[iblock].x += + (block_cross_axis_extent - children_sizes[iblock].x) / 2; } } break; diff --git a/ashura/include/ashura/widgets/grid.h b/ashura/include/ashura/widgets/grid.h index 4a5a5e15c..d1178ff9d 100644 --- a/ashura/include/ashura/widgets/grid.h +++ b/ashura/include/ashura/widgets/grid.h @@ -32,8 +32,7 @@ struct GridProps struct Grid : public Widget { template ... DerivedWidget> - explicit Grid(GridProps iprops, DerivedWidget... ichildren) : - props{std::move(iprops)} + explicit Grid(GridProps iprops, DerivedWidget... ichildren) : props{std::move(iprops)} { update_children(std::move(ichildren)...); } @@ -82,7 +81,8 @@ struct Grid : public Widget return WidgetDebugInfo{.type = "Grid"}; } - virtual void allocate_size(Context &ctx, Vec2 allocated_size, stx::Span children_allocation) override + virtual void allocate_size(Context &ctx, Vec2 allocated_size, + stx::Span children_allocation) override { ASH_CHECK(props.items.is_empty() || props.items.size() == children.size()); @@ -107,7 +107,8 @@ struct Grid : public Widget f32 const column_gap = (f32) (columns - 1) * props.column_gap; f32 const row_gap = (f32) (rows - 1) * props.row_gap; - Vec2 const cell_size = (self_extent - Vec2{column_gap, row_gap}) / Vec2{(f32) columns, (f32) rows}; + Vec2 const cell_size = + (self_extent - Vec2{column_gap, row_gap}) / Vec2{(f32) columns, (f32) rows}; if (props.items.is_empty()) { @@ -129,11 +130,14 @@ struct Grid : public Widget span_gap.y = props.row_gap * (f32) (item.row_span - 1); } - children_allocation[i] = cell_size * Vec2{(f32) item.column_span, (f32) item.row_span} + span_gap; + children_allocation[i] = + cell_size * Vec2{(f32) item.column_span, (f32) item.row_span} + span_gap; } } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { ASH_CHECK(props.items.is_empty() || props.items.size() == children.size()); @@ -158,15 +162,17 @@ struct Grid : public Widget f32 const column_gap = (f32) (columns - 1) * props.column_gap; f32 const row_gap = (f32) (rows - 1) * props.row_gap; - Vec2 const cell_size = (self_extent - Vec2{column_gap, row_gap}) / Vec2{(f32) columns, (f32) rows}; + Vec2 const cell_size = + (self_extent - Vec2{column_gap, row_gap}) / Vec2{(f32) columns, (f32) rows}; if (props.items.is_empty()) { for (u32 i = 0; i < nchildren; i++) { - u32 const column = i % props.columns; - u32 const row = i / props.columns; - Vec2 const position = (cell_size + Vec2{props.column_gap, props.row_gap}) * Vec2{(f32) column, (f32) row}; + u32 const column = i % props.columns; + u32 const row = i / props.columns; + Vec2 const position = + (cell_size + Vec2{props.column_gap, props.row_gap}) * Vec2{(f32) column, (f32) row}; children_positions[i] = position + (cell_size - children_sizes[i]) * props.alignment; } } @@ -174,8 +180,10 @@ struct Grid : public Widget { for (u32 i = 0; i < nchildren; i++) { - Vec2 const position = (cell_size + Vec2{props.column_gap, props.row_gap}) * Vec2{(f32) props.items[i].column, (f32) props.items[i].row}; - children_positions[i] = position + (children_allocations[i] - children_sizes[i]) * props.items[i].alignment; + Vec2 const position = (cell_size + Vec2{props.column_gap, props.row_gap}) * + Vec2{(f32) props.items[i].column, (f32) props.items[i].row}; + children_positions[i] = + position + (children_allocations[i] - children_sizes[i]) * props.items[i].alignment; } } diff --git a/ashura/include/ashura/widgets/image.h b/ashura/include/ashura/widgets/image.h index a4809dbc0..a438dacc3 100644 --- a/ashura/include/ashura/widgets/image.h +++ b/ashura/include/ashura/widgets/image.h @@ -77,9 +77,9 @@ enum class ImageState : u8 // struct Image : public Widget { - explicit Image(ImageProps image_props) : - props{std::move(image_props)} - {} + explicit Image(ImageProps image_props) : props{std::move(image_props)} + { + } STX_DISABLE_COPY(Image) STX_DEFAULT_MOVE(Image) @@ -89,7 +89,9 @@ struct Image : public Widget return WidgetDebugInfo{.type = "Image"}; } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { Vec2 extent = props.size.resolve(allocated_size); if (props.aspect_ratio.is_some()) @@ -178,8 +180,9 @@ struct Image : public Widget } else if (std::holds_alternative(props.source)) { - image_load_future = stx::Some(loader->load_from_file(std::get(props.source).path)); - state = ImageState::Loading; + image_load_future = + stx::Some(loader->load_from_file(std::get(props.source).path)); + state = ImageState::Loading; } else if (std::holds_alternative(props.source)) { @@ -199,7 +202,8 @@ struct Image : public Widget state = ImageState::Loaded; if (props.resize_on_load) { - props.size = Constraint2D::absolute(AS(f32, buffer.extent.width), AS(f32, buffer.extent.height)); + props.size = Constraint2D::absolute(AS(f32, buffer.extent.width), + AS(f32, buffer.extent.height)); } image_extent = buffer.extent; } diff --git a/ashura/include/ashura/widgets/input.h b/ashura/include/ashura/widgets/input.h index d3994f346..01d58ab5b 100644 --- a/ashura/include/ashura/widgets/input.h +++ b/ashura/include/ashura/widgets/input.h @@ -92,53 +92,64 @@ enum class NumInputState // TODO(lamarrr): accept focus, esc to cancel, enter to increase struct NumInput : public Widget { - NumInput(U8InputSpec spec, NumInputProps iprops) : - u8spec{spec}, type{NumType::U8}, props{iprops} - {} + NumInput(U8InputSpec spec, NumInputProps iprops) : u8spec{spec}, type{NumType::U8}, props{iprops} + { + } NumInput(U16InputSpec spec, NumInputProps iprops) : u16spec{spec}, type{NumType::U16}, props{iprops} - {} + { + } NumInput(U32InputSpec spec, NumInputProps iprops) : u32spec{spec}, type{NumType::U32}, props{iprops} - {} + { + } NumInput(U64InputSpec spec, NumInputProps iprops) : u64spec{spec}, type{NumType::U64}, props{iprops} - {} + { + } - NumInput(I8InputSpec spec, NumInputProps iprops) : - i8spec{spec}, type{NumType::I8}, props{iprops} - {} + NumInput(I8InputSpec spec, NumInputProps iprops) : i8spec{spec}, type{NumType::I8}, props{iprops} + { + } NumInput(I16InputSpec spec, NumInputProps iprops) : i16spec{spec}, type{NumType::I16}, props{iprops} - {} + { + } NumInput(I32InputSpec spec, NumInputProps iprops) : i32spec{spec}, type{NumType::I32}, props{iprops} - {} + { + } NumInput(I64InputSpec spec, NumInputProps iprops) : i64spec{spec}, type{NumType::I64}, props{iprops} - {} + { + } NumInput(F32InputSpec spec, NumInputProps iprops) : f32spec{spec}, type{NumType::F32}, props{iprops} - {} + { + } NumInput(F64InputSpec spec, NumInputProps iprops) : f64spec{spec}, type{NumType::F64}, props{iprops} - {} + { + } STX_DISABLE_COPY(NumInput) STX_DEFAULT_MOVE(NumInput) virtual ~NumInput() override - {} + { + } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { char buff[256]; int written = std::snprintf(buff, 256, u8spec.display_format, (void *) &u8spec.value); @@ -162,7 +173,8 @@ struct NumInput : public Widget { } - virtual void on_drag_update(Context &ctx, Vec2 mouse_position, Vec2 translation, DragData const &drag_data) override + virtual void on_drag_update(Context &ctx, Vec2 mouse_position, Vec2 translation, + DragData const &drag_data) override { } diff --git a/ashura/include/ashura/widgets/padding.h b/ashura/include/ashura/widgets/padding.h index 800257c59..48b4d3e35 100644 --- a/ashura/include/ashura/widgets/padding.h +++ b/ashura/include/ashura/widgets/padding.h @@ -10,8 +10,7 @@ namespace gui struct Padding : public Widget { template DerivedWidget> - Padding(EdgeInsets iedge_insets, DerivedWidget ichild) : - edge_insets{iedge_insets} + Padding(EdgeInsets iedge_insets, DerivedWidget ichild) : edge_insets{iedge_insets} { children.push(new DerivedWidget{std::move(ichild)}).unwrap(); } @@ -50,12 +49,15 @@ struct Padding : public Widget return WidgetDebugInfo{.type = "Padding"}; } - virtual void allocate_size(Context &ctx, Vec2 allocated_size, stx::Span children_allocation) override + virtual void allocate_size(Context &ctx, Vec2 allocated_size, + stx::Span children_allocation) override { children_allocation.fill(max(allocated_size - edge_insets.xy(), Vec2{0, 0})); } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { children_positions[0] = edge_insets.top_left(); return min(children_sizes[0] + edge_insets.xy(), allocated_size); diff --git a/ashura/include/ashura/widgets/progress_bar.h b/ashura/include/ashura/widgets/progress_bar.h index 6f99d87f3..cab9c41dc 100644 --- a/ashura/include/ashura/widgets/progress_bar.h +++ b/ashura/include/ashura/widgets/progress_bar.h @@ -31,9 +31,12 @@ struct ProgressBar : public Widget STX_DEFAULT_MOVE(ProgressBar) virtual ~ProgressBar() override - {} + { + } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { return props.size.resolve(allocated_size); } diff --git a/ashura/include/ashura/widgets/radio.h b/ashura/include/ashura/widgets/radio.h index 5c609096e..dbe7723bb 100644 --- a/ashura/include/ashura/widgets/radio.h +++ b/ashura/include/ashura/widgets/radio.h @@ -20,11 +20,12 @@ struct RadioCtx explicit RadioCtx(RadioValue value) : data{stx::rc::make(stx::os_allocator, std::move(value)).unwrap()} - {} + { + } - RadioCtx(RadioCtx const &other) : - data{other.data.share()} - {} + RadioCtx(RadioCtx const &other) : data{other.data.share()} + { + } RadioCtx &operator=(RadioCtx const &other) { @@ -51,10 +52,16 @@ struct Radio : public Widget using Callback = stx::UniqueFn; static void default_on_changed(Radio &, Context &, RadioValue const &) - {} + { + } - Radio(RadioValue ivalue, RadioCtx iradio_ctx, Callback ion_changed = stx::fn::rc::make_unique_static(default_on_changed), RadioProps iprops = {}) : - on_changed{std::move(ion_changed)}, value{std::move(ivalue)}, radio_ctx{std::move(iradio_ctx)}, props{iprops} + Radio(RadioValue ivalue, RadioCtx iradio_ctx, + Callback ion_changed = stx::fn::rc::make_unique_static(default_on_changed), + RadioProps iprops = {}) : + on_changed{std::move(ion_changed)}, + value{std::move(ivalue)}, + radio_ctx{std::move(iradio_ctx)}, + props{iprops} { __restart_state_machine(*radio_ctx.data); } @@ -66,43 +73,42 @@ struct Radio : public Widget { } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { return Vec2::splat(props.width); } virtual void tick(Context &ctx, std::chrono::nanoseconds interval) override { - if (!ctx.key_events.span().which([](KeyEvent e) { - return e.action == KeyAction::Press && e.key == ESCAPE_Key; - }) + if (!ctx.key_events.span() + .which([](KeyEvent e) { return e.action == KeyAction::Press && e.key == ESCAPE_Key; }) .is_empty()) { // TODO(lamarrr): key debouncing fmt::println("esc down!"); } - if (!ctx.key_events.span().which([](KeyEvent e) { - return e.action == KeyAction::Release && e.key == ESCAPE_Key; - }) + if (!ctx.key_events.span() + .which( + [](KeyEvent e) { return e.action == KeyAction::Release && e.key == ESCAPE_Key; }) .is_empty()) { // TODO(lamarrr): key debouncing fmt::println("esc up!"); } - if (!ctx.key_events.span().which([](KeyEvent e) { - return e.action == KeyAction::Press && e.key == m_Key; - }) + if (!ctx.key_events.span() + .which([](KeyEvent e) { return e.action == KeyAction::Press && e.key == m_Key; }) .is_empty()) { // TODO(lamarrr): key debouncing fmt::println("m down!"); } - if (!ctx.key_events.span().which([](KeyEvent e) { - return e.action == KeyAction::Release && e.key == m_Key; - }) + if (!ctx.key_events.span() + .which([](KeyEvent e) { return e.action == KeyAction::Release && e.key == m_Key; }) .is_empty()) { // TODO(lamarrr): key debouncing @@ -126,12 +132,14 @@ struct Radio : public Widget virtual void draw(Context &ctx, gfx::Canvas &canvas) override { EaseIn curve; - Rect outer_rect = area; - Vec2 inner_rect_extent = Vec2::splat(animation.animate(curve, is_active ? Tween{0.0f, props.inner_width} : Tween{props.inner_width, 0.0f})); - Rect inner_rect = Rect{.offset = area.offset + (area.extent / 2) - inner_rect_extent / 2, .extent = inner_rect_extent}; - - canvas - .draw_rect_stroke(outer_rect, props.color, 1.5f) + Rect outer_rect = area; + Vec2 inner_rect_extent = + Vec2::splat(animation.animate(curve, is_active ? Tween{0.0f, props.inner_width} : + Tween{props.inner_width, 0.0f})); + Rect inner_rect = Rect{.offset = area.offset + (area.extent / 2) - inner_rect_extent / 2, + .extent = inner_rect_extent}; + + canvas.draw_rect_stroke(outer_rect, props.color, 1.5f) .draw_rect_filled(inner_rect, props.color); } @@ -140,7 +148,8 @@ struct Radio : public Widget return true; } - virtual void on_mouse_down(Context &ctx, MouseButton button, Vec2 mouse_position, u32 nclicks) override + virtual void on_mouse_down(Context &ctx, MouseButton button, Vec2 mouse_position, + u32 nclicks) override { if (button == MouseButton::Primary && !props.disabled) { diff --git a/ashura/include/ashura/widgets/scroll_box.h b/ashura/include/ashura/widgets/scroll_box.h index bcf9c74e4..7618c9fcf 100644 --- a/ashura/include/ashura/widgets/scroll_box.h +++ b/ashura/include/ashura/widgets/scroll_box.h @@ -41,12 +41,15 @@ struct ScrollBar : public Widget { ScrollBar(Direction idirection, stx::Rc iscroll_ctx) : direction{idirection}, scroll_ctx{std::move(iscroll_ctx)} - {} + { + } STX_DISABLE_COPY(ScrollBar) STX_DEFAULT_MOVE(ScrollBar) - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { Vec2 size; if (direction == Direction::V) @@ -80,7 +83,8 @@ struct ScrollBar : public Widget virtual void draw(Context &ctx, gfx::Canvas &canvas) override { - Vec2 view_offset = scroll_ctx->view_offset.resolve(scroll_ctx->content_size - scroll_ctx->view_size); + Vec2 view_offset = + scroll_ctx->view_offset.resolve(scroll_ctx->content_size - scroll_ctx->view_size); if (direction == Direction::H && scroll_ctx->can_scroll_x()) { f32 scale = area.extent.x / scroll_ctx->content_size.x; @@ -89,8 +93,7 @@ struct ScrollBar : public Widget Rect thumb_rect = area; thumb_rect.offset.x += thumb_offset; thumb_rect.extent.x = thumb_width; - canvas - .draw_rect_filled(thumb_rect, scroll_ctx->props.thumb_color) + canvas.draw_rect_filled(thumb_rect, scroll_ctx->props.thumb_color) .draw_rect_filled(area, scroll_ctx->props.track_color); } else if (direction == Direction::V && scroll_ctx->can_scroll_y()) @@ -101,8 +104,7 @@ struct ScrollBar : public Widget Rect thumb_rect = area; thumb_rect.offset.y += thumb_offset; thumb_rect.extent.y = thumb_height; - canvas - .draw_rect_filled(thumb_rect, scroll_ctx->props.thumb_color) + canvas.draw_rect_filled(thumb_rect, scroll_ctx->props.thumb_color) .draw_rect_filled(area, scroll_ctx->props.track_color); } } @@ -116,27 +118,28 @@ struct ScrollBar : public Widget { if (direction == Direction::H) { - f32 offset = (mouse_position.x - area.offset.x) / area.extent.x * scroll_ctx->content_size.x; + f32 offset = (mouse_position.x - area.offset.x) / area.extent.x * scroll_ctx->content_size.x; scroll_ctx->view_offset.x = Constraint::absolute(offset); } else { - f32 offset = (mouse_position.y - area.offset.y) / area.extent.y * scroll_ctx->content_size.y; + f32 offset = (mouse_position.y - area.offset.y) / area.extent.y * scroll_ctx->content_size.y; scroll_ctx->view_offset.y = Constraint::absolute(offset); } return stx::Some(DragData{}); } - virtual void on_drag_update(Context &ctx, Vec2 mouse_position, Vec2 translation, DragData const &drag_data) override + virtual void on_drag_update(Context &ctx, Vec2 mouse_position, Vec2 translation, + DragData const &drag_data) override { if (direction == Direction::H) { - f32 offset = (mouse_position.x - area.offset.x) / area.extent.x * scroll_ctx->content_size.x; + f32 offset = (mouse_position.x - area.offset.x) / area.extent.x * scroll_ctx->content_size.x; scroll_ctx->view_offset.x = Constraint::absolute(offset); } else { - f32 offset = (mouse_position.y - area.offset.y) / area.extent.y * scroll_ctx->content_size.y; + f32 offset = (mouse_position.y - area.offset.y) / area.extent.y * scroll_ctx->content_size.y; scroll_ctx->view_offset.y = Constraint::absolute(offset); } } @@ -150,10 +153,10 @@ struct ScrollViewport : public Widget template DerivedWidget> ScrollViewport(stx::Rc ctx, DerivedWidget child) : ScrollViewport{std::move(ctx), new DerivedWidget{std::move(child)}} - {} + { + } - ScrollViewport(stx::Rc ictx, Widget *child) : - scroll_ctx{std::move(ictx)} + ScrollViewport(stx::Rc ictx, Widget *child) : scroll_ctx{std::move(ictx)} { children.push_inplace(child).unwrap(); } @@ -161,12 +164,15 @@ struct ScrollViewport : public Widget STX_DISABLE_COPY(ScrollViewport) STX_DEFAULT_MOVE(ScrollViewport) - virtual void allocate_size(Context &ctx, Vec2 allocated_size, stx::Span children_allocation) override + virtual void allocate_size(Context &ctx, Vec2 allocated_size, + stx::Span children_allocation) override { children_allocation.fill(scroll_ctx->props.frame.resolve(allocated_size)); } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { Vec2 view_size = scroll_ctx->props.frame.resolve(allocated_size); Vec2 content_size = children_sizes[0]; @@ -224,7 +230,8 @@ struct ScrollBox : public Widget template DerivedWidget> ScrollBox(ScrollBoxProps iprops, DerivedWidget child) : ScrollBox{std::move(iprops), new DerivedWidget{std::move(child)}} - {} + { + } ScrollBox(ScrollBoxProps iprops, Widget *child) : scroll_ctx{stx::rc::make(stx::os_allocator, ScrollCtx{.props = iprops}).unwrap()} @@ -238,12 +245,15 @@ struct ScrollBox : public Widget STX_DISABLE_COPY(ScrollBox) STX_DEFAULT_MOVE(ScrollBox) - virtual void allocate_size(Context &ctx, Vec2 allocated_size, stx::Span children_allocation) override + virtual void allocate_size(Context &ctx, Vec2 allocated_size, + stx::Span children_allocation) override { children_allocation.fill(allocated_size); } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { children_positions[0] = Vec2{0, 0}; children_positions[1] = Vec2{0, scroll_ctx->view_size.y - scroll_ctx->props.bar_width}; @@ -251,7 +261,8 @@ struct ScrollBox : public Widget return children_sizes[0]; } - virtual i32 z_stack(Context &ctx, i32 allocated_z_index, stx::Span children_allocation) override + virtual i32 z_stack(Context &ctx, i32 allocated_z_index, + stx::Span children_allocation) override { children_allocation[0] = allocated_z_index + 1; children_allocation[1] = allocated_z_index + 1 + 256 * 256; diff --git a/ashura/include/ashura/widgets/slider.h b/ashura/include/ashura/widgets/slider.h index 5b2212d3a..79685dc81 100644 --- a/ashura/include/ashura/widgets/slider.h +++ b/ashura/include/ashura/widgets/slider.h @@ -24,14 +24,20 @@ struct Slider : public Widget using Callback = stx::UniqueFn; static void default_on_changed(Slider &, Context &, f32) - {} + { + } explicit Slider(Callback ion_changed = stx::fn::rc::make_unique_static(default_on_changed), Callback ion_change_start = stx::fn::rc::make_unique_static(default_on_changed), Callback ion_change_end = stx::fn::rc::make_unique_static(default_on_changed), - f32 ivalue = 0, f32 imin = 0, f32 imax = 1, - SliderProps iprops = SliderProps{}) : - on_changed{std::move(ion_changed)}, on_change_start{std::move(ion_change_start)}, on_change_end{std::move(ion_change_end)}, value{ivalue}, min{imin}, max{imax}, props{iprops} + f32 ivalue = 0, f32 imin = 0, f32 imax = 1, SliderProps iprops = SliderProps{}) : + on_changed{std::move(ion_changed)}, + on_change_start{std::move(ion_change_start)}, + on_change_end{std::move(ion_change_end)}, + value{ivalue}, + min{imin}, + max{imax}, + props{iprops} { __transition_radius(props.thumb_radius * 0.75f, props.thumb_radius * 0.5f); } @@ -40,9 +46,12 @@ struct Slider : public Widget STX_DEFAULT_MOVE(Slider) virtual ~Slider() override - {} + { + } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { return Vec2{props.width.resolve(allocated_size.x), props.thumb_radius * 2}; } @@ -58,11 +67,13 @@ struct Slider : public Widget track_area.offset.y -= props.track_height / 2; track_area.extent.y = props.track_height; - Vec2 thumb_center{track_area.offset.x + percentage * track_area.extent.x, area.offset.y + area.extent.y / 2}; + Vec2 thumb_center{track_area.offset.x + percentage * track_area.extent.x, + area.offset.y + area.extent.y / 2}; f32 thumb_radius = thumb_animation.animate(thumb_animation_curve, thumb_tween); canvas - .draw_round_rect_filled(track_area, Vec4::splat(props.track_height / 2), 45, props.track_color) + .draw_round_rect_filled(track_area, Vec4::splat(props.track_height / 2), 45, + props.track_color) .draw_circle_filled(thumb_center, thumb_radius, 360, props.track_color); } @@ -78,10 +89,12 @@ struct Slider : public Widget virtual stx::Option on_drag_start(Context &ctx, Vec2 mouse_position) override { - return stx::Some(DragData{.type = "STUB", .data = stx::Unique{stx::Span{}, stx::manager_stub}}); + return stx::Some( + DragData{.type = "STUB", .data = stx::Unique{stx::Span{}, stx::manager_stub}}); } - virtual void on_drag_update(Context &ctx, Vec2 mouse_position, Vec2 translation, DragData const &drag_data) override + virtual void on_drag_update(Context &ctx, Vec2 mouse_position, Vec2 translation, + DragData const &drag_data) override { on_change_start.handle(*this, ctx, value); f32 diff = translation.x / track_area.extent.x; diff --git a/ashura/include/ashura/widgets/stack.h b/ashura/include/ashura/widgets/stack.h index a78f7d784..376c4fb3d 100644 --- a/ashura/include/ashura/widgets/stack.h +++ b/ashura/include/ashura/widgets/stack.h @@ -18,8 +18,7 @@ struct StackProps struct Stack : public Widget { template ... DerivedWidget> - explicit Stack(StackProps iprops, DerivedWidget... ichildren) : - props{std::move(iprops)} + explicit Stack(StackProps iprops, DerivedWidget... ichildren) : props{std::move(iprops)} { update_children(std::move(ichildren)...); } @@ -68,12 +67,15 @@ struct Stack : public Widget return WidgetDebugInfo{.type = "Stack"}; } - virtual void allocate_size(Context &ctx, Vec2 allocated_size, stx::Span children_allocation) override + virtual void allocate_size(Context &ctx, Vec2 allocated_size, + stx::Span children_allocation) override { children_allocation.fill(props.frame.resolve(allocated_size)); } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { Vec2 size; @@ -93,7 +95,8 @@ struct Stack : public Widget return size; } - virtual i32 z_stack(Context &ctx, i32 allocated_z_index, stx::Span children_allocation) override + virtual i32 z_stack(Context &ctx, i32 allocated_z_index, + stx::Span children_allocation) override { i32 next_z_index = allocated_z_index + 1; diff --git a/ashura/include/ashura/widgets/stats.h b/ashura/include/ashura/widgets/stats.h index 2f45e4c3b..ff5ec0c26 100644 --- a/ashura/include/ashura/widgets/stats.h +++ b/ashura/include/ashura/widgets/stats.h @@ -15,8 +15,12 @@ namespace gui struct StatsWidget : public Box { StatsWidget() : - Box{BoxProps{.background_color = colors::MAGENTA.with_alpha(128), .padding = EdgeInsets::all(10)}, - Flex{FlexProps{.direction = Direction::V, .wrap = Wrap::Wrap, .main_align = MainAlign::Start, .cross_align = CrossAlign::Start}, + Box{BoxProps{.background_color = colors::MAGENTA.with_alpha(128), + .padding = EdgeInsets::all(10)}, + Flex{FlexProps{.direction = Direction::V, + .wrap = Wrap::Wrap, + .main_align = MainAlign::Start, + .cross_align = CrossAlign::Start}, Text{"", {}}, Text{"", {}}, Text{"", {}}, Text{"", {}}}} { } @@ -28,10 +32,16 @@ struct StatsWidget : public Box { TextProps props{.style = TextStyle{.foreground_color = colors::WHITE}}; stx::Span cols = this->get_children(ctx)[0]->get_children(ctx); - dynamic_cast(cols[0])->update_text(fmt::format("GPU time: {:.2} ms", ctx.frame_stats.gpu_time.count() / 1'000'000.0), props); - dynamic_cast(cols[1])->update_text(fmt::format("CPU time: {:.2} ms", ctx.frame_stats.cpu_time.count() / 1'000'000.0), props); - dynamic_cast(cols[2])->update_text(fmt::format("CPU-GPU sync time: {:.2} ms", ctx.frame_stats.gpu_sync_time.count() / 1'000'000.0), props); - dynamic_cast(cols[3])->update_text(fmt::format("{} vertices", ctx.frame_stats.input_assembly_vertices), props); + dynamic_cast(cols[0])->update_text( + fmt::format("GPU time: {:.2} ms", ctx.frame_stats.gpu_time.count() / 1'000'000.0), props); + dynamic_cast(cols[1])->update_text( + fmt::format("CPU time: {:.2} ms", ctx.frame_stats.cpu_time.count() / 1'000'000.0), props); + dynamic_cast(cols[2])->update_text( + fmt::format("CPU-GPU sync time: {:.2} ms", + ctx.frame_stats.gpu_sync_time.count() / 1'000'000.0), + props); + dynamic_cast(cols[3])->update_text( + fmt::format("{} vertices", ctx.frame_stats.input_assembly_vertices), props); } }; } // namespace gui diff --git a/ashura/include/ashura/widgets/switch.h b/ashura/include/ashura/widgets/switch.h index 904da830a..856c5e588 100644 --- a/ashura/include/ashura/widgets/switch.h +++ b/ashura/include/ashura/widgets/switch.h @@ -26,9 +26,11 @@ struct Switch : public Widget using Callback = stx::UniqueFn; static void default_on_changed(Switch &, Context &, bool new_state) - {} + { + } - explicit Switch(Callback ion_changed = stx::fn::rc::make_unique_static(default_on_changed), bool istate = false, SwitchProps iprops = {}) : + explicit Switch(Callback ion_changed = stx::fn::rc::make_unique_static(default_on_changed), + bool istate = false, SwitchProps iprops = {}) : on_changed{std::move(ion_changed)}, state{istate}, props{iprops} { } @@ -40,26 +42,30 @@ struct Switch : public Widget { } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { return Vec2{props.height * 1.75f, props.height}; } virtual void draw(Context &ctx, gfx::Canvas &canvas) override { - f32 const padding = 1.75f; - f32 const thumb_radius = std::max(props.height / 2 - padding, 0.0f); - f32 const thumb_begin_x = padding + thumb_radius; - f32 const thumb_end_x = std::max(area.extent.x - padding - thumb_radius, 0.0f); - Tween color_tween = state ? Tween{props.inactive_track_color, props.active_track_color} : Tween{props.active_track_color, props.inactive_track_color}; - Tween thumb_position_tween = state ? Tween{thumb_begin_x, thumb_end_x} : Tween{thumb_end_x, thumb_begin_x}; + f32 const padding = 1.75f; + f32 const thumb_radius = std::max(props.height / 2 - padding, 0.0f); + f32 const thumb_begin_x = padding + thumb_radius; + f32 const thumb_end_x = std::max(area.extent.x - padding - thumb_radius, 0.0f); + Tween color_tween = state ? Tween{props.inactive_track_color, props.active_track_color} : + Tween{props.active_track_color, props.inactive_track_color}; + Tween thumb_position_tween = + state ? Tween{thumb_begin_x, thumb_end_x} : Tween{thumb_end_x, thumb_begin_x}; EaseIn curve; Color color = animation.animate(curve, color_tween); f32 const thumb_position = animation.animate(curve, thumb_position_tween); - canvas - .draw_round_rect_filled(area, Vec4::splat(props.height / 2), 90, color) - .draw_circle_filled(area.offset + Vec2{thumb_position, 1.5f + thumb_radius}, thumb_radius, 180, props.thumb_color); + canvas.draw_round_rect_filled(area, Vec4::splat(props.height / 2), 90, color) + .draw_circle_filled(area.offset + Vec2{thumb_position, 1.5f + thumb_radius}, thumb_radius, + 180, props.thumb_color); } virtual void tick(Context &ctx, std::chrono::nanoseconds interval) override @@ -67,7 +73,8 @@ struct Switch : public Widget animation.tick(interval); } - virtual void on_mouse_down(Context &ctx, MouseButton button, Vec2 mouse_position, u32 nclicks) override + virtual void on_mouse_down(Context &ctx, MouseButton button, Vec2 mouse_position, + u32 nclicks) override { if (button == MouseButton::Primary) { diff --git a/ashura/include/ashura/widgets/text.h b/ashura/include/ashura/widgets/text.h index 34b6437d2..5b930e20c 100644 --- a/ashura/include/ashura/widgets/text.h +++ b/ashura/include/ashura/widgets/text.h @@ -19,28 +19,29 @@ struct Text : public Widget { explicit Text(std::string_view itext, TextProps iprops = {}) : text{stx::string::make(stx::os_allocator, itext).unwrap()}, props{iprops} - {} + { + } virtual WidgetDebugInfo get_debug_info(Context &ctx) override { return WidgetDebugInfo{.type = "Text"}; } - virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, stx::Span children_sizes, stx::Span children_positions) override + virtual Vec2 fit(Context &ctx, Vec2 allocated_size, stx::Span children_allocations, + stx::Span children_sizes, + stx::Span children_positions) override { if (is_layout_dirty || text_layout.text_scale_factor != ctx.text_scale_factor) { - TextRun runs[] = { - TextRun{.size = (usize) -1, .style = 0}}; - - TextBlock text_block{ - .text = std::string_view{text.data(), text.size()}, - .runs = runs, - .styles = {}, - .default_style = props.style, - .align = TextAlign::Start, - .direction = TextDirection::LeftToRight, - .language = {}}; + TextRun runs[] = {TextRun{.size = (usize) -1, .style = 0}}; + + TextBlock text_block{.text = std::string_view{text.data(), text.size()}, + .runs = runs, + .styles = {}, + .default_style = props.style, + .align = TextAlign::Start, + .direction = TextDirection::LeftToRight, + .language = {}}; Vec2 size = props.frame.resolve(allocated_size); @@ -53,23 +54,22 @@ struct Text : public Widget virtual void draw(Context &ctx, gfx::Canvas &canvas) override { - TextRun runs[] = { - TextRun{.size = (usize) -1, .style = 0}}; - - TextBlock text_block{ - .text = std::string_view{text.data(), text.size()}, - .runs = runs, - .styles = {}, - .default_style = props.style, - .align = TextAlign::Start, - .direction = TextDirection::LeftToRight, - .language = {}}; + TextRun runs[] = {TextRun{.size = (usize) -1, .style = 0}}; + + TextBlock text_block{.text = std::string_view{text.data(), text.size()}, + .runs = runs, + .styles = {}, + .default_style = props.style, + .align = TextAlign::Start, + .direction = TextDirection::LeftToRight, + .language = {}}; canvas.draw_text(text_block, text_layout, ctx.font_bundle, area.offset); } virtual void tick(Context &ctx, std::chrono::nanoseconds interval) override - {} + { + } void update_text(std::string_view itext, TextProps iprops) { diff --git a/ashura/include/ashura/window.h b/ashura/include/ashura/window.h index 34bde6bf8..9a58cf4fa 100644 --- a/ashura/include/ashura/window.h +++ b/ashura/include/ashura/window.h @@ -42,18 +42,19 @@ enum class SwapChainState : u8 { Ok = 0, ExtentChanged = 1, // the window's extent and surface (framebuffer) extent has changed - Suboptimal = 2, // the window swapchain can still be used for presentation but is not optimal for presentation in its present state - OutOfDate = 4, // the window swapchain is now out of date and needs to be changed - All = 7 + Suboptimal = + 2, // the window swapchain can still be used for presentation but is not optimal for presentation in its present state + OutOfDate = 4, // the window swapchain is now out of date and needs to be changed + All = 7 }; STX_DEFINE_ENUM_BIT_OPS(SwapChainState) struct Window { - Window(SDL_Window *iwindow) : - window{iwindow} - {} + Window(SDL_Window *iwindow) : window{iwindow} + { + } STX_MAKE_PINNED(Window) @@ -79,7 +80,8 @@ struct Window required_instance_extensions.resize(ext_count).unwrap(); - ASH_SDL_CHECK(SDL_Vulkan_GetInstanceExtensions(&ext_count, required_instance_extensions.data()) == SDL_TRUE); + ASH_SDL_CHECK(SDL_Vulkan_GetInstanceExtensions( + &ext_count, required_instance_extensions.data()) == SDL_TRUE); return required_instance_extensions; } @@ -111,7 +113,8 @@ struct Window void center() { - ASH_SDL_CHECK(SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED) == 0); + ASH_SDL_CHECK(SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED) == + 0); } Extent get_size() @@ -181,7 +184,9 @@ struct Window break; } - SDL_Surface *icon = SDL_CreateSurfaceFrom((void *) image.span.data(), AS(int, image.extent.width), AS(int, image.extent.height), AS(int, image.pitch), fmt); + SDL_Surface *icon = + SDL_CreateSurfaceFrom((void *) image.span.data(), AS(int, image.extent.width), + AS(int, image.extent.height), AS(int, image.pitch), fmt); ASH_SDL_CHECK(icon != nullptr); ASH_SDL_CHECK(SDL_SetWindowIcon(window, icon) == 0); SDL_DestroySurface(icon); @@ -219,7 +224,8 @@ struct Window void request_attention(bool briefly) { - ASH_SDL_CHECK(SDL_FlashWindow(window, briefly ? SDL_FLASH_BRIEFLY : SDL_FLASH_UNTIL_FOCUSED) == 0); + ASH_SDL_CHECK(SDL_FlashWindow(window, briefly ? SDL_FLASH_BRIEFLY : SDL_FLASH_UNTIL_FOCUSED) == + 0); } void make_fullscreen() @@ -275,9 +281,13 @@ struct Window VkSurfaceKHR surface; - ASH_SDL_CHECK(SDL_Vulkan_CreateSurface(window, instance->instance, &surface) == SDL_TRUE, "unable to create surface for window"); + ASH_SDL_CHECK(SDL_Vulkan_CreateSurface(window, instance->instance, &surface) == SDL_TRUE, + "unable to create surface for window"); - this->surface = stx::Some(stx::rc::make_unique(stx::os_allocator, vk::Surface{.surface = surface, .instance = instance->instance}).unwrap()); + this->surface = stx::Some( + stx::rc::make_unique(stx::os_allocator, + vk::Surface{.surface = surface, .instance = instance->instance}) + .unwrap()); } void recreate_swapchain(stx::Rc const &queue, u32 max_nframes_in_flight) @@ -290,9 +300,10 @@ struct Window int surface_width, surface_height; ASH_SDL_CHECK(SDL_GetWindowSizeInPixels(window, &surface_width, &surface_height) == 0); - VkSurfaceFormatKHR preferred_formats[] = {{VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}; + VkSurfaceFormatKHR preferred_formats[] = { + {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, + {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, + {VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}; // VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; // VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT; @@ -306,21 +317,24 @@ struct Window // VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT; // VK_COLOR_SPACE_DCI_P3_LINEAR_EXT; - VkPresentModeKHR preferred_present_modes[] = {VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_FIFO_RELAXED_KHR, - VK_PRESENT_MODE_MAILBOX_KHR}; + VkPresentModeKHR preferred_present_modes[] = { + VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_FIFO_RELAXED_KHR, + VK_PRESENT_MODE_MAILBOX_KHR}; VkSampleCountFlagBits max_msaa_sample_count = queue->device->phy_dev->get_max_sample_count(); - surface.value()->change_swapchain(queue, max_nframes_in_flight, preferred_formats, preferred_present_modes, - VkExtent2D{.width = AS(u32, surface_width), .height = AS(u32, surface_height)}, - VkExtent2D{.width = AS(u32, width), .height = AS(u32, height)}, max_msaa_sample_count, - VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR); + surface.value()->change_swapchain( + queue, max_nframes_in_flight, preferred_formats, preferred_present_modes, + VkExtent2D{.width = AS(u32, surface_width), .height = AS(u32, surface_height)}, + VkExtent2D{.width = AS(u32, width), .height = AS(u32, height)}, max_msaa_sample_count, + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR); } std::pair acquire_image() { ASH_CHECK(surface.is_some(), "trying to present to swapchain without having surface attached"); - ASH_CHECK(surface.value()->swapchain.is_some(), "trying to present to swapchain without having one"); + ASH_CHECK(surface.value()->swapchain.is_some(), + "trying to present to swapchain without having one"); vk::SwapChain &swapchain = surface.value()->swapchain.value(); @@ -330,9 +344,12 @@ struct Window u32 swapchain_image_index = 0; - VkResult result = vkAcquireNextImageKHR(swapchain.dev, swapchain.swapchain, VULKAN_TIMEOUT, semaphore, fence, &swapchain_image_index); + VkResult result = vkAcquireNextImageKHR(swapchain.dev, swapchain.swapchain, VULKAN_TIMEOUT, + semaphore, fence, &swapchain_image_index); - ASH_CHECK(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR, "failed to acquire swapchain image"); + ASH_CHECK(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR || + result == VK_ERROR_OUT_OF_DATE_KHR, + "failed to acquire swapchain image"); if (result == VK_SUBOPTIMAL_KHR) { @@ -355,7 +372,8 @@ struct Window SwapChainState present(VkQueue queue, u32 swapchain_image_index) { ASH_CHECK(surface.is_some(), "trying to present to swapchain without having surface attached"); - ASH_CHECK(surface.value()->swapchain.is_some(), "trying to present to swapchain without having one"); + ASH_CHECK(surface.value()->swapchain.is_some(), + "trying to present to swapchain without having one"); // we submit multiple render commands (operating on the swapchain images) to // the GPU to prevent having to force a sync with the GPU (await_fence) when @@ -371,15 +389,17 @@ struct Window VkPresentInfoKHR present_info{.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .pNext = nullptr, .waitSemaphoreCount = 1, - .pWaitSemaphores = &swapchain.render_semaphores[swapchain.frame], - .swapchainCount = 1, - .pSwapchains = &swapchain.swapchain, - .pImageIndices = &swapchain_image_index, - .pResults = nullptr}; + .pWaitSemaphores = &swapchain.render_semaphores[swapchain.frame], + .swapchainCount = 1, + .pSwapchains = &swapchain.swapchain, + .pImageIndices = &swapchain_image_index, + .pResults = nullptr}; VkResult result = vkQueuePresentKHR(queue, &present_info); - ASH_CHECK(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR || result == VK_ERROR_OUT_OF_DATE_KHR, "failed to present swapchain image"); + ASH_CHECK(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR || + result == VK_ERROR_OUT_OF_DATE_KHR, + "failed to present swapchain image"); if (result == VK_SUBOPTIMAL_KHR) { diff --git a/ashura/include/ashura/window_manager.h b/ashura/include/ashura/window_manager.h index d7d7c5091..21dd21e08 100644 --- a/ashura/include/ashura/window_manager.h +++ b/ashura/include/ashura/window_manager.h @@ -11,7 +11,8 @@ namespace ash struct WindowManager { - stx::Rc create_window(char const *title, WindowType type, WindowCreateFlags flags, ash::Extent extent) + stx::Rc create_window(char const *title, WindowType type, WindowCreateFlags flags, + ash::Extent extent) { // width and height here refer to the screen coordinates and not the // actual pixel coordinates (SEE: Device Pixel Ratio) @@ -61,7 +62,8 @@ struct WindowManager window_flags |= SDL_WINDOW_ALWAYS_ON_TOP; } - SDL_Window *window = SDL_CreateWindow(title, AS(i32, extent.width), AS(i32, extent.height), window_flags); + SDL_Window *window = + SDL_CreateWindow(title, AS(i32, extent.width), AS(i32, extent.height), window_flags); // window creation shouldn't fail reliably, if it fails, there's no point in the program proceeding ASH_SDL_CHECK(window != nullptr, "unable to create window"); diff --git a/ashura/src/ashura.cc b/ashura/src/ashura.cc index 62f99b3ae..d94d6e8e4 100644 --- a/ashura/src/ashura.cc +++ b/ashura/src/ashura.cc @@ -29,14 +29,24 @@ int main(int argc, char **argv) { ASH_CHECK(SDL_Init(SDL_INIT_EVERYTHING) == 0); - FontSpec fonts[] = {{.name = "Roboto", .path = R"(C:\Users\Basit\Documents\workspace\oss\ashura\ashura\assets\fonts\Roboto\Roboto-Regular.ttf)"}, - {.name = "RobotoMono", .path = R"(C:\Users\Basit\Desktop\JetBrainsMono-2.304\fonts\ttf\JetBrainsMono-Regular.ttf)"}, - {.name = "MaterialIcons", .path = R"(C:\Users\Basit\Documents\workspace\oss\ashura\ashura\assets\fonts\MaterialIcons\MaterialIcons-Regular.ttf)"}, - {.name = "NotoSans", .path = R"(C:\Users\Basit\Desktop\Noto_Sans_Arabic\static\NotoSansArabic-Regular.ttf)"}}; + FontSpec fonts[] = { + {.name = "Roboto", + .path = + R"(C:\Users\Basit\Documents\workspace\oss\ashura\ashura\assets\fonts\Roboto\Roboto-Regular.ttf)"}, + {.name = "RobotoMono", + .path = R"(C:\Users\Basit\Desktop\JetBrainsMono-2.304\fonts\ttf\JetBrainsMono-Regular.ttf)"}, + {.name = "MaterialIcons", + .path = + R"(C:\Users\Basit\Documents\workspace\oss\ashura\ashura\assets\fonts\MaterialIcons\MaterialIcons-Regular.ttf)"}, + {.name = "NotoSans", + .path = R"(C:\Users\Basit\Desktop\Noto_Sans_Arabic\static\NotoSansArabic-Regular.ttf)"}}; - CanvasPipelineSpec pipelines[] = { - {.name = DEFAULT_SHAPE_PIPELINE, .vertex_shader = gfx::vertex_shader_code, .fragment_shader = gfx::fragment_shader_code}, - {.name = DEFAULT_GLYPH_PIPELINE, .vertex_shader = gfx::glyph_vertex_shader_code, .fragment_shader = gfx::glyph_fragment_shader_code}}; + CanvasPipelineSpec pipelines[] = {{.name = DEFAULT_SHAPE_PIPELINE, + .vertex_shader = gfx::vertex_shader_code, + .fragment_shader = gfx::fragment_shader_code}, + {.name = DEFAULT_GLYPH_PIPELINE, + .vertex_shader = gfx::glyph_vertex_shader_code, + .fragment_shader = gfx::glyph_fragment_shader_code}}; AppConfig cfg{.enable_validation_layers = true, .fonts = fonts, .pipelines = pipelines}; @@ -53,106 +63,128 @@ int main(int argc, char **argv) stx::Vec items; gui::GridItem items_tmp[] = { - {.column = 0, .column_span = 2, .row = 0, .row_span = 2}, - {.column = 2, .column_span = 1, .row = 0, .row_span = 1}, - {.column = 2, .column_span = 1, .row = 1, .row_span = 1}, + {.column = 0, .column_span = 2, .row = 0, .row_span = 2}, + {.column = 2, .column_span = 1, .row = 0, .row_span = 1}, + {.column = 2, .column_span = 1, .row = 1, .row_span = 1}, }; items.extend(items_tmp).unwrap(); gui::RadioCtx state{8}; - App app{std::move(cfg), - gui::Flex{ - gui::FlexProps{}, - gui::Image{ - gui::ImageProps{.source = gui::FileImageSource{.path = R"(C:\Users\Basit\Desktop\pimping.png)"}, - .aspect_ratio = stx::Some(1.0f), - .resize_on_load = true}}, - gui::Text{"cruelty_free", gui::TextProps{.style = TextStyle{.font = "MaterialIcons", - .font_height = 25, - .foreground_color = material::BLACK, - .background_color = colors::WHITE, - .line_height = 1.0f}}}, - gui::Text{std::string_view{(char *) greeting, sizeof(greeting)}, gui::TextProps{.style = TextStyle{.font = "NotoSans", - .font_height = 20, - .foreground_color = material::BLACK, - .background_color = colors::WHITE}}}, - gui::CheckBox{}, - gui::Slider{stx::fn::rc::make_unique_static([](gui::Slider &slider, Context &ctx, f32 value) { - ctx.text_scale_factor = value * 5; - })}, - gui::Switch{}, - gui::StatsWidget{}, - gui::ProgressBar{}, - gui::Grid{gui::GridProps{.columns = 3, .rows = 2, .column_gap = 10, .row_gap = 10, .alignment = ALIGN_CENTER, .items = std::move(items), .frame = Constraint2D::absolute(600, 400)}, - gui::Image{ - gui::ImageProps{.source = gui::FileImageSource{.path = R"(C:\Users\Basit\Desktop\26050398.jpg)"}, - .aspect_ratio = stx::Some(1.0f), - .resize_on_load = true}}, - gui::Image{ - gui::ImageProps{.source = gui::FileImageSource{.path = R"(C:\Users\Basit\Desktop\26050398.jpg)"}, - .aspect_ratio = stx::Some(1.0f), - .resize_on_load = true}}, - gui::Image{ - gui::ImageProps{.source = gui::FileImageSource{.path = R"(C:\Users\Basit\Desktop\26050398.jpg)"}, - .aspect_ratio = stx::Some(1.0f), - .resize_on_load = true}}}, - gui::Stack{ - gui::StackProps{.alignment = ALIGN_BOTTOM_CENTER}, - gui::Box{ - gui::BoxProps{.padding = EdgeInsets::all(2.5f), - .border_thickness = 2.5f, - .border_color = material::CYAN_500, - .border_radius = BorderRadius::relative(1)}, - gui::Image{ - gui::ImageProps{.source = gui::FileImageSource{.path = R"(C:\Users\Basit\Desktop\profile.png)"}, - .border_radius = BorderRadius::relative(1, 1, 1, 1), - .aspect_ratio = stx::Some(1.0f), - .resize_on_load = true}}}, - gui::Box{ - gui::BoxProps{.background_color = material::RED_500, - .padding = EdgeInsets::horizontal(5), - .border_thickness = 5, - .border_color = colors::BLACK, - .border_radius = BorderRadius::absolute(7.5f)}, - gui::Text{"LIVE", gui::TextProps{.style = TextStyle{.font_height = 15, - .foreground_color = colors::WHITE}}}}}, - gui::Stack{ - gui::StackProps{.alignment = ALIGN_CENTER}, - gui::Box{ - gui::BoxProps{ - .background_gradient = LinearColorGradient{.begin = material::GREEN_500, .end = material::GREEN_500.with_alpha(10), .angle = 0}, - .padding = EdgeInsets::all(50), - .border_radius = BorderRadius::absolute(7.5f)}, - gui::Text{"FE!N FE!N FE!N FE!N FE!N", gui::TextProps{.style = TextStyle{.foreground_color = colors::WHITE}, - .frame = Constraint2D::relative(1, 1)}}}, - gui::Padding{ - EdgeInsets::all(20), - gui::Box{gui::BoxProps{.background_color = material::RED_500.with_alpha(0xCC), - .padding = EdgeInsets::all(5), - .border_thickness = 5, - .border_color = colors::BLACK, - .border_radius = BorderRadius::absolute(7.5f), - .corner_shape = gui::BoxCornerShape::Bevel}, - gui::Text{"For You", gui::TextProps{.style = TextStyle{.foreground_color = colors::WHITE}}}}}}, - gui::Box{gui::BoxProps{.background_color = Color::from_rgb(0x33, 0x33, 0x33), - .padding = EdgeInsets::all(5), - .border_thickness = 1, - .border_color = Color::from_rgb(0xFF, 0xFF, 0xFF), + App app{ + std::move(cfg), + gui::Flex{ + gui::FlexProps{}, + gui::Image{gui::ImageProps{ + .source = gui::FileImageSource{.path = R"(C:\Users\Basit\Desktop\pimping.png)"}, + .aspect_ratio = stx::Some(1.0f), + .resize_on_load = true}}, + gui::Text{"cruelty_free", + gui::TextProps{.style = TextStyle{.font = "MaterialIcons", + .font_height = 25, + .foreground_color = material::BLACK, + .background_color = colors::WHITE, + .line_height = 1.0f}}}, + gui::Text{std::string_view{(char *) greeting, sizeof(greeting)}, + gui::TextProps{.style = TextStyle{.font = "NotoSans", + .font_height = 20, + .foreground_color = material::BLACK, + .background_color = colors::WHITE}}}, + gui::CheckBox{}, + gui::Slider{stx::fn::rc::make_unique_static( + [](gui::Slider &slider, Context &ctx, + f32 value) { ctx.text_scale_factor = value * 5; })}, + gui::Switch{}, + gui::StatsWidget{}, + gui::ProgressBar{}, + gui::Grid{gui::GridProps{.columns = 3, + .rows = 2, + .column_gap = 10, + .row_gap = 10, + .alignment = ALIGN_CENTER, + .items = std::move(items), + .frame = Constraint2D::absolute(600, 400)}, + gui::Image{gui::ImageProps{ + .source = + gui::FileImageSource{.path = R"(C:\Users\Basit\Desktop\26050398.jpg)"}, + .aspect_ratio = stx::Some(1.0f), + .resize_on_load = true}}, + gui::Image{gui::ImageProps{ + .source = + gui::FileImageSource{.path = R"(C:\Users\Basit\Desktop\26050398.jpg)"}, + .aspect_ratio = stx::Some(1.0f), + .resize_on_load = true}}, + gui::Image{gui::ImageProps{ + .source = + gui::FileImageSource{.path = R"(C:\Users\Basit\Desktop\26050398.jpg)"}, + .aspect_ratio = stx::Some(1.0f), + .resize_on_load = true}}}, + gui::Stack{ + gui::StackProps{.alignment = ALIGN_BOTTOM_CENTER}, + gui::Box{gui::BoxProps{.padding = EdgeInsets::all(2.5f), + .border_thickness = 2.5f, + .border_color = material::CYAN_500, + .border_radius = BorderRadius::relative(1)}, + gui::Image{gui::ImageProps{ + .source = + gui::FileImageSource{.path = + R"(C:\Users\Basit\Desktop\profile.png)"}, + .border_radius = BorderRadius::relative(1, 1, 1, 1), + .aspect_ratio = stx::Some(1.0f), + .resize_on_load = true}}}, + gui::Box{gui::BoxProps{.background_color = material::RED_500, + .padding = EdgeInsets::horizontal(5), + .border_thickness = 5, + .border_color = colors::BLACK, .border_radius = BorderRadius::absolute(7.5f)}, - gui::Text{"For You", gui::TextProps{.style = TextStyle{.foreground_color = colors::WHITE}}}}, - gui::Radio(5, state), - gui::Radio(6, state), - gui::Radio(8, state), - gui::Text{"verified", gui::TextProps{.style = TextStyle{.font = "MaterialIcons", .foreground_color = colors::YELLOW}}}, - gui::Text{R"(I didn't wanna say anything, but this game seems lame)", gui::TextProps{.style = TextStyle{.font = "Roboto", - .font_height = 30, - .foreground_color = material::WHITE, - .shadow_color = colors::BLACK, - .shadow_scale = 1, - .shadow_offset = 2, - .background_color = material::GRAY_100}}}, - gui::Text{R"([2023-07-31 13:26:08.632] [Init] [info] WINDOW RESIZED + gui::Text{"LIVE", gui::TextProps{.style = TextStyle{.font_height = 15, + .foreground_color = + colors::WHITE}}}}}, + gui::Stack{ + gui::StackProps{.alignment = ALIGN_CENTER}, + gui::Box{ + gui::BoxProps{.background_gradient = + LinearColorGradient{.begin = material::GREEN_500, + .end = material::GREEN_500.with_alpha(10), + .angle = 0}, + .padding = EdgeInsets::all(50), + .border_radius = BorderRadius::absolute(7.5f)}, + gui::Text{"FE!N FE!N FE!N FE!N FE!N", + gui::TextProps{.style = TextStyle{.foreground_color = colors::WHITE}, + .frame = Constraint2D::relative(1, 1)}}}, + gui::Padding{ + EdgeInsets::all(20), + gui::Box{gui::BoxProps{.background_color = material::RED_500.with_alpha(0xCC), + .padding = EdgeInsets::all(5), + .border_thickness = 5, + .border_color = colors::BLACK, + .border_radius = BorderRadius::absolute(7.5f), + .corner_shape = gui::BoxCornerShape::Bevel}, + gui::Text{"For You", + gui::TextProps{ + .style = TextStyle{.foreground_color = colors::WHITE}}}}}}, + gui::Box{gui::BoxProps{.background_color = Color::from_rgb(0x33, 0x33, 0x33), + .padding = EdgeInsets::all(5), + .border_thickness = 1, + .border_color = Color::from_rgb(0xFF, 0xFF, 0xFF), + .border_radius = BorderRadius::absolute(7.5f)}, + gui::Text{"For You", gui::TextProps{.style = TextStyle{.foreground_color = + colors::WHITE}}}}, + gui::Radio(5, state), + gui::Radio(6, state), + gui::Radio(8, state), + gui::Text{"verified", + gui::TextProps{.style = TextStyle{.font = "MaterialIcons", + .foreground_color = colors::YELLOW}}}, + gui::Text{R"(I didn't wanna say anything, but this game seems lame)", + gui::TextProps{.style = TextStyle{.font = "Roboto", + .font_height = 30, + .foreground_color = material::WHITE, + .shadow_color = colors::BLACK, + .shadow_scale = 1, + .shadow_offset = 2, + .background_color = material::GRAY_100}}}, + gui::Text{R"([2023-07-31 13:26:08.632] [Init] [info] WINDOW RESIZED [2023-07-31 13:26:08.632] [ImageLoader] [info] Loading image from path: C:\Users\Basit\Desktop\pimping.png [2023-07-31 13:26:08.632] [ImageLoader] [info] Loading image from path: C:\Users\Basit\Desktop\profile.png [2023-07-31 13:26:08.633] [ImageLoader] [info] Loading image from path: C:\Users\Basit\Desktop\wallpaperflare.com_wallpaper.jpg @@ -219,17 +251,24 @@ int main(int argc, char **argv) [2023-07-31 13:26:08.691] [Vulkan_RenderResourceManager] [info] Created non-real-time 1920x1080 Image #11 with format=VK_FORMAT_R8G8B8A8_UNORM and size=8847360 bytes [2023-07-31 13:26:08.695] [Vulkan_RenderResourceManager] [info] Uploaded pending image )", - gui::TextProps{.style = TextStyle{.font = "Roboto", - .font_height = 30, - .foreground_color = material::BLUE_500, - .background_color = material::GRAY_100}}}, - gui::Text{"explicit", gui::TextProps{.style = TextStyle{.font = "MaterialIcons", - .foreground_color = colors::GREEN}}}, - gui::ScrollBox{gui::ScrollBoxProps{}, gui::Image{gui::ImageProps{.source = gui::FileImageSource{.path = R"(C:\Users\Basit\Desktop\wallpaperflare.com_wallpaper.jpg)"}, - .size = Constraint2D::absolute(2000, 2000).with_maxr(INF, INF), - .border_radius = BorderRadius::relative(.25f, .25f, .25f, .25f), - .aspect_ratio = stx::Some(2.0f), - .resize_on_load = false}}}}}; + gui::TextProps{.style = TextStyle{.font = "Roboto", + .font_height = 30, + .foreground_color = material::BLUE_500, + .background_color = material::GRAY_100}}}, + gui::Text{"explicit", + gui::TextProps{.style = TextStyle{.font = "MaterialIcons", + .foreground_color = colors::GREEN}}}, + gui::ScrollBox{ + gui::ScrollBoxProps{}, + gui::Image{ + gui::ImageProps{ + .source = + gui::FileImageSource{ + .path = R"(C:\Users\Basit\Desktop\wallpaperflare.com_wallpaper.jpg)"}, + .size = Constraint2D::absolute(2000, 2000).with_maxr(INF, INF), + .border_radius = BorderRadius::relative(.25f, .25f, .25f, .25f), + .aspect_ratio = stx::Some(2.0f), + .resize_on_load = false}}}}}; timepoint last_tick = Clock::now(); while (true) diff --git a/ashura/src/engine.cc b/ashura/src/engine.cc index 82f83f405..4e50c0ee8 100644 --- a/ashura/src/engine.cc +++ b/ashura/src/engine.cc @@ -20,15 +20,19 @@ #include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/stdout_color_sinks.h" -#define TIMER_BEGIN(name) ::std::chrono::steady_clock::time_point name##_TIMER_Begin = ::std::chrono::steady_clock::now() -#define TIMER_END(name, str) ASH_LOG_INFO(FontRenderer, "Timer: {}, Task: {}, took: {}ms", #name, str, (::std::chrono::steady_clock::now() - name##_TIMER_Begin).count() / 1'000'000.0f) +#define TIMER_BEGIN(name) \ + ::std::chrono::steady_clock::time_point name##_TIMER_Begin = ::std::chrono::steady_clock::now() +#define TIMER_END(name, str) \ + ASH_LOG_INFO(FontRenderer, "Timer: {}, Task: {}, took: {}ms", #name, str, \ + (::std::chrono::steady_clock::now() - name##_TIMER_Begin).count() / 1'000'000.0f) namespace ash { inline stx::Option> select_device(stx::Span const phy_devices, - stx::Span preferred_device_types, vk::Surface const &target_surface) + stx::Span preferred_device_types, + vk::Surface const &target_surface) { for (VkPhysicalDeviceType type : preferred_device_types) { @@ -42,8 +46,8 @@ inline stx::Option> // or data dev.has_transfer_command_queue_family() && // can be used for presenting to a specific surface - !vk::get_surface_presentation_command_queue_support(dev.phy_device, dev.family_properties, - target_surface.surface) + !vk::get_surface_presentation_command_queue_support( + dev.phy_device, dev.family_properties, target_surface.surface) .span() .find(true) .is_empty(); @@ -80,40 +84,48 @@ Engine::Engine(AppConfig const &cfg, Widget *iroot_widget) : ASH_LOG_INFO(Init, "Initializing Window API"); - root_window = stx::Some(window_manager.create_window(cfg.name.c_str(), cfg.root_window_type, cfg.root_window_create_flags, cfg.root_window_extent)); + root_window = + stx::Some(window_manager.create_window(cfg.name.c_str(), cfg.root_window_type, + cfg.root_window_create_flags, cfg.root_window_extent)); ASH_LOG_INFO(Init, "Creating root window"); stx::Vec required_window_instance_extensions = Window::get_required_instance_extensions(); - stx::Rc vk_instance = vk::create_instance(cfg.name.c_str(), VK_MAKE_VERSION(0, 0, 1), cfg.name.c_str(), - VK_MAKE_VERSION(cfg.version.major, cfg.version.minor, cfg.version.patch), - required_window_instance_extensions, required_validation_layers); + stx::Rc vk_instance = + vk::create_instance(cfg.name.c_str(), VK_MAKE_VERSION(0, 0, 1), cfg.name.c_str(), + VK_MAKE_VERSION(cfg.version.major, cfg.version.minor, cfg.version.patch), + required_window_instance_extensions, required_validation_layers); root_window.value()->attach_surface(vk_instance.share()); stx::Vec phy_devices = vk::get_all_devices(vk_instance); - VkPhysicalDeviceType const device_preference[] = {VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, - VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU, - VK_PHYSICAL_DEVICE_TYPE_CPU}; + VkPhysicalDeviceType const device_preference[] = { + VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, + VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU, VK_PHYSICAL_DEVICE_TYPE_CPU}; ASH_LOG_INFO(Init, "Available Physical Devices:"); for (vk::PhyDeviceInfo const &device : phy_devices) { // TODO(lamarrr): log graphics families on devices and other properties - ASH_LOG_INFO(Init, "Device(name: '{}', ID: {}, type: {})", device.properties.deviceName, device.properties.deviceID, string_VkPhysicalDeviceType(device.properties.deviceType)); + ASH_LOG_INFO(Init, "Device(name: '{}', ID: {}, type: {})", device.properties.deviceName, + device.properties.deviceID, + string_VkPhysicalDeviceType(device.properties.deviceType)); } stx::Rc phy_device = - stx::rc::make(stx::os_allocator, select_device(phy_devices, device_preference, *root_window.value()->surface.value()) - .expect("Unable to find any suitable rendering device")[0] - .copy()) + stx::rc::make( + stx::os_allocator, + select_device(phy_devices, device_preference, *root_window.value()->surface.value()) + .expect("Unable to find any suitable rendering device")[0] + .copy()) .unwrap(); - ASH_LOG_INFO(Init, "Selected Physical Device: Device(name: '{}', ID: {}, type: {})", phy_device->properties.deviceName, - phy_device->properties.deviceID, string_VkPhysicalDeviceType(phy_device->properties.deviceType)); + ASH_LOG_INFO(Init, "Selected Physical Device: Device(name: '{}', ID: {}, type: {})", + phy_device->properties.deviceName, phy_device->properties.deviceID, + string_VkPhysicalDeviceType(phy_device->properties.deviceType)); // we might need multiple command queues, one for data transfer and one for // rendering @@ -122,32 +134,35 @@ Engine::Engine(AppConfig const &cfg, Widget *iroot_widget) : 1}; stx::Rc graphics_command_queue_family = - stx::rc::make(stx::os_allocator, - vk::get_graphics_command_queue(phy_device).expect("Unable to get graphics command queue")) + stx::rc::make( + stx::os_allocator, + vk::get_graphics_command_queue(phy_device).expect("Unable to get graphics command queue")) .unwrap(); // we can accept queue family struct here instead and thus not have to // perform extra manual checks // the user shouldn't have to touch handles - VkDeviceQueueCreateInfo command_queue_create_infos[] = {{.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .queueFamilyIndex = graphics_command_queue_family->index, - .queueCount = AS(u32, std::size(queue_priorities)), - .pQueuePriorities = queue_priorities}}; + VkDeviceQueueCreateInfo command_queue_create_infos[] = { + {.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .queueFamilyIndex = graphics_command_queue_family->index, + .queueCount = AS(u32, std::size(queue_priorities)), + .pQueuePriorities = queue_priorities}}; VkPhysicalDeviceFeatures required_features{}; required_features.samplerAnisotropy = VK_TRUE; required_features.pipelineStatisticsQuery = VK_TRUE; - stx::Rc device = vk::create_device(phy_device, command_queue_create_infos, required_device_extensions, - required_validation_layers, required_features); + stx::Rc device = + vk::create_device(phy_device, command_queue_create_infos, required_device_extensions, + required_validation_layers, required_features); stx::Rc xqueue = - stx::rc::make_inplace(stx::os_allocator, - vk::get_command_queue(device, *graphics_command_queue_family.handle, 0) - .expect("Failed to create graphics command queue")) + stx::rc::make_inplace( + stx::os_allocator, vk::get_command_queue(device, *graphics_command_queue_family.handle, 0) + .expect("Failed to create graphics command queue")) .unwrap(); queue = stx::Some(xqueue.share()); @@ -155,8 +170,11 @@ Engine::Engine(AppConfig const &cfg, Widget *iroot_widget) : root_window.value()->recreate_swapchain(xqueue, DEFAULT_MAX_FRAMES_IN_FLIGHT); auto &swp = root_window.value()->surface.value()->swapchain.value(); - ASH_LOG_INFO(Init, "recreated swapchain for logical/window/viewport extent: [{}, {}], physical/surface extent: [{}, {}]", - swp.window_extent.width, swp.window_extent.height, swp.image_extent.width, swp.image_extent.height); + ASH_LOG_INFO(Init, + "recreated swapchain for logical/window/viewport extent: [{}, {}], physical/surface " + "extent: [{}, {}]", + swp.window_extent.width, swp.window_extent.height, swp.image_extent.width, + swp.image_extent.height); render_resource_manager.init(xqueue.share()); pipeline_manager.init(xqueue->device->dev); @@ -172,10 +190,15 @@ Engine::Engine(AppConfig const &cfg, Widget *iroot_widget) : pipeline_manager.rebuild_for_renderpass(swp.render_pass, swp.msaa_sample_count); - root_window.value()->on(WindowEvents::CloseRequested, stx::fn::rc::make_unique_functor(stx::os_allocator, [](WindowEvents) { std::exit(0); }).unwrap()); + root_window.value()->on(WindowEvents::CloseRequested, + stx::fn::rc::make_unique_functor(stx::os_allocator, [](WindowEvents) { + std::exit(0); + }).unwrap()); root_window.value()->on(WindowEvents::Resized | WindowEvents::PixelSizeChanged, - stx::fn::rc::make_unique_functor(stx::os_allocator, [](WindowEvents) { ASH_LOG_INFO(Init, "WINDOW RESIZED"); }).unwrap()); + stx::fn::rc::make_unique_functor(stx::os_allocator, [](WindowEvents) { + ASH_LOG_INFO(Init, "WINDOW RESIZED"); + }).unwrap()); root_window.value()->on_mouse_click(stx::fn::rc::make_unique_static([](MouseClickEvent event) { if (event.action == KeyAction::Press && event.button == MouseButton::A2) { @@ -184,26 +207,43 @@ Engine::Engine(AppConfig const &cfg, Widget *iroot_widget) : })); u8 transparent_image_data[] = {0xFF, 0xFF, 0xFF, 0xFF}; - gfx::image transparent_image = render_resource_manager.add_image(ImageView{.span = transparent_image_data, .extent = {1, 1}, .pitch = 4, .format = ImageFormat::Rgba8888}, false); + gfx::image transparent_image = + render_resource_manager.add_image(ImageView{.span = transparent_image_data, + .extent = {1, 1}, + .pitch = 4, + .format = ImageFormat::Rgba8888}, + false); ASH_CHECK(transparent_image == 0); u8 icon[] = {0xFF, 0xFF, 0xFF, 0xFF}; - root_window.value()->set_icon(ImageView{.span = icon, .extent = {1, 1}, .pitch = 4, .format = ImageFormat::Rgba8888}); + root_window.value()->set_icon(ImageView{ + .span = icon, .extent = {1, 1}, .pitch = 4, .format = ImageFormat::Rgba8888}); ctx.register_subsystem(new VulkanImageManager{render_resource_manager}); ctx.register_subsystem(new ImageLoader{}); - root_window.value()->on_mouse_click(stx::fn::rc::make_unique_functor(stx::os_allocator, [this](MouseClickEvent event) { widget_system.events.push_inplace(event).unwrap(); }).unwrap()); - root_window.value()->on_mouse_motion(stx::fn::rc::make_unique_functor(stx::os_allocator, [this](MouseMotionEvent event) { widget_system.events.push_inplace(event).unwrap(); }).unwrap()); - root_window.value()->on_key(stx::fn::rc::make_unique_functor(stx::os_allocator, [this](KeyEvent event) { ctx.key_events.push_inplace(event).unwrap(); }).unwrap()); - root_window.value()->on(WindowEvents::All, stx::fn::rc::make_unique_functor(stx::os_allocator, [this](WindowEvents events) { - if ((events & WindowEvents::MouseLeave) != WindowEvents::None) - { - widget_system.events.push_inplace(events).unwrap(); - } - }).unwrap()); + root_window.value()->on_mouse_click( + stx::fn::rc::make_unique_functor(stx::os_allocator, [this](MouseClickEvent event) { + widget_system.events.push_inplace(event).unwrap(); + }).unwrap()); + root_window.value()->on_mouse_motion( + stx::fn::rc::make_unique_functor(stx::os_allocator, [this](MouseMotionEvent event) { + widget_system.events.push_inplace(event).unwrap(); + }).unwrap()); + root_window.value()->on_key( + stx::fn::rc::make_unique_functor(stx::os_allocator, [this](KeyEvent event) { + ctx.key_events.push_inplace(event).unwrap(); + }).unwrap()); + root_window.value()->on( + WindowEvents::All, + stx::fn::rc::make_unique_functor(stx::os_allocator, [this](WindowEvents events) { + if ((events & WindowEvents::MouseLeave) != WindowEvents::None) + { + widget_system.events.push_inplace(events).unwrap(); + } + }).unwrap()); TIMER_BEGIN(AllFontLoad); for (FontSpec const &spec : cfg.fonts) @@ -222,11 +262,16 @@ Engine::Engine(AppConfig const &cfg, Widget *iroot_widget) : { atlas.bins[i].texture = render_resource_manager.add_image(image_buffers[i], false); } - font_bundle.push(BundledFont{.name = spec.name.copy(stx::os_allocator).unwrap(), .font = std::move(result.value()), .atlas = std::move(atlas)}).unwrap(); + font_bundle + .push(BundledFont{.name = spec.name.copy(stx::os_allocator).unwrap(), + .font = std::move(result.value()), + .atlas = std::move(atlas)}) + .unwrap(); } else { - ASH_LOG_ERR(Init, "Failed to load font: {} from file: {}, error: {}", spec.name.view(), spec.path.view(), AS(i64, result.err())); + ASH_LOG_ERR(Init, "Failed to load font: {} from file: {}, error: {}", spec.name.view(), + spec.path.view(), AS(i64, result.err())); } } @@ -239,11 +284,14 @@ Engine::Engine(AppConfig const &cfg, Widget *iroot_widget) : for (Subsystem *subsystem : ctx.subsystems) { subsystem->on_startup(ctx); - ASH_LOG_INFO(Context, "Initialized subsystem: {} (type: {})", subsystem->get_name(), typeid(*subsystem).name()); + ASH_LOG_INFO(Context, "Initialized subsystem: {} (type: {})", subsystem->get_name(), + typeid(*subsystem).name()); } VkImageFormatProperties image_format_properties; - ASH_VK_CHECK(vkGetPhysicalDeviceImageFormatProperties(queue.value()->device->phy_dev->phy_device, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT, 0, &image_format_properties)); + ASH_VK_CHECK(vkGetPhysicalDeviceImageFormatProperties( + queue.value()->device->phy_dev->phy_device, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_TYPE_2D, + VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_SAMPLED_BIT, 0, &image_format_properties)); fmt::println("max: {}", image_format_properties.maxMipLevels); } @@ -305,8 +353,9 @@ void Engine::tick(std::chrono::nanoseconds interval) if (!root_window.value()->surface.value()->is_zero_sized_swapchain) { auto &swp = root_window.value()->surface.value()->swapchain.value(); - ASH_LOG_INFO(Window, "recreated swapchain for logical/window/viewport extent: [{}, {}], " - "physical/surface extent: [{}, {}]", + ASH_LOG_INFO(Window, + "recreated swapchain for logical/window/viewport extent: [{}, {}], " + "physical/surface extent: [{}, {}]", swp.window_extent.width, swp.window_extent.height, swp.image_extent.width, swp.image_extent.height); pipeline_manager.rebuild_for_renderpass(swp.render_pass, swp.msaa_sample_count); @@ -330,13 +379,15 @@ void Engine::tick(std::chrono::nanoseconds interval) gfx::DrawList const &draw_list = canvas.draw_list; renderer.submit(swapchain.window_extent, swapchain.image_extent, swapchain.frame, - swapchain.render_fences[swapchain.frame], swapchain.image_acquisition_semaphores[swapchain.frame], + swapchain.render_fences[swapchain.frame], + swapchain.image_acquisition_semaphores[swapchain.frame], swapchain.render_semaphores[swapchain.frame], swapchain.render_pass, - swapchain.framebuffers[swapchain_image_index], draw_list.commands, draw_list.vertices, draw_list.indices, - pipeline_manager, + swapchain.framebuffers[swapchain_image_index], draw_list.commands, + draw_list.vertices, draw_list.indices, pipeline_manager, render_resource_manager, ctx.frame_stats); - swapchain_state = root_window.value()->present(queue.value()->info.queue, swapchain_image_index); + swapchain_state = + root_window.value()->present(queue.value()->info.queue, swapchain_image_index); // the frame semaphores and synchronization primitives are still used even // if an error is returned diff --git a/ashura/src/gfx.cc b/ashura/src/gfx.cc index 66138ee7d..bd89f60b5 100644 --- a/ashura/src/gfx.cc +++ b/ashura/src/gfx.cc @@ -8,22 +8,20 @@ namespace gfx bool BufferSyncScope::sync(MemoryAccess memory_access, PipelineStages stages, QueueBufferMemoryBarrier &barrier) - - -// TODO(lamarrr): index buffer can be used from a generated compute stage, will our graph handle -// this? we need to check for read/write compatibility -// TODO(lamarrr): on every device idle, we can reset resource states. -// TODO(lamarrr): for each renderpass execution we need to get accessed resources and generate -// barriers for them -// -// requirements: -// - allow simultaneous reads -// - prevent simultaneous writes -// - prevent simultaneous writes and reads -// -// Also, See: -// https://stackoverflow.com/questions/60339191/synchronizing-a-render-pass-layout-transition-with-a-semaphore-in-acquire-pres -bool BufferState::sync(BufferAccess request, QueueBufferMemoryBarrier &barrier) + // TODO(lamarrr): index buffer can be used from a generated compute stage, will our graph handle + // this? we need to check for read/write compatibility + // TODO(lamarrr): on every device idle, we can reset resource states. + // TODO(lamarrr): for each renderpass execution we need to get accessed resources and generate + // barriers for them + // + // requirements: + // - allow simultaneous reads + // - prevent simultaneous writes + // - prevent simultaneous writes and reads + // + // Also, See: + // https://stackoverflow.com/questions/60339191/synchronizing-a-render-pass-layout-transition-with-a-semaphore-in-acquire-pres + bool BufferState::sync(BufferAccess request, QueueBufferMemoryBarrier &barrier) { MemoryOps const ops = get_memory_ops(request.access); diff --git a/ashura/src/media.cc b/ashura/src/media.cc index febd59fc0..02569e036 100644 --- a/ashura/src/media.cc +++ b/ashura/src/media.cc @@ -46,24 +46,28 @@ enum class MediaProperties }; -#define ASH_LOG_FFMPEG_ERR(err_ctx, err_exp) \ - do \ - { \ - char ASH_LOG_FFMPEG_ERR_error_buffer[256]; \ - int ASH_LOG_FFMPEG_ERR_error = (err_exp); \ - if (av_strerror(ASH_LOG_FFMPEG_ERR_error, ASH_LOG_FFMPEG_ERR_error_buffer, sizeof(ASH_LOG_FFMPEG_ERR_error_buffer)) == 0) \ - { \ - ASH_LOG_ERR(MediaPlayer, "FFMPEG returned error: {}={} while {}", ASH_LOG_FFMPEG_ERR_error, ASH_LOG_FFMPEG_ERR_error_buffer, err_ctx); \ - } \ - else \ - { \ - ASH_LOG_ERR(MediaPlayer, "FFMPEG returned error: {} while {}", ASH_LOG_FFMPEG_ERR_error, err_ctx); \ - } \ +#define ASH_LOG_FFMPEG_ERR(err_ctx, err_exp) \ + do \ + { \ + char ASH_LOG_FFMPEG_ERR_error_buffer[256]; \ + int ASH_LOG_FFMPEG_ERR_error = (err_exp); \ + if (av_strerror(ASH_LOG_FFMPEG_ERR_error, ASH_LOG_FFMPEG_ERR_error_buffer, \ + sizeof(ASH_LOG_FFMPEG_ERR_error_buffer)) == 0) \ + { \ + ASH_LOG_ERR(MediaPlayer, "FFMPEG returned error: {}={} while {}", ASH_LOG_FFMPEG_ERR_error, \ + ASH_LOG_FFMPEG_ERR_error_buffer, err_ctx); \ + } \ + else \ + { \ + ASH_LOG_ERR(MediaPlayer, "FFMPEG returned error: {} while {}", ASH_LOG_FFMPEG_ERR_error, \ + err_ctx); \ + } \ } while (0) constexpr nanoseconds timebase_to_ns(AVRational timebase) { - return nanoseconds{AS(nanoseconds::rep, 1'000'000'000LL * AS(f32, timebase.num) / AS(f32, timebase.den))}; + return nanoseconds{ + AS(nanoseconds::rep, 1'000'000'000LL * AS(f32, timebase.num) / AS(f32, timebase.den))}; } constexpr i64 timebase_scale(AVRational timebase, nanoseconds duration) @@ -113,16 +117,17 @@ enum class MediaSeekType Backward }; -enum class MediaSeekValueType{ +enum class MediaSeekValueType +{ Time, Frame }; struct MediaSeekRequest { - MediaSeekType type = MediaSeekType::Exact; + MediaSeekType type = MediaSeekType::Exact; MediaSeekValueType value_type = MediaSeekValueType::Time; - i64 value = 0; + i64 value = 0; }; struct ResamplerConfig @@ -136,8 +141,10 @@ struct ResamplerConfig bool operator==(ResamplerConfig const &other) const { - return fmt == other.fmt && dst_fmt == other.dst_fmt && sample_rate == other.sample_rate && dst_sample_rate == other.dst_sample_rate && - (av_channel_layout_compare(&channel_layout, &other.channel_layout) == 0) && (av_channel_layout_compare(&dst_channel_layout, &other.dst_channel_layout) == 0); + return fmt == other.fmt && dst_fmt == other.dst_fmt && sample_rate == other.sample_rate && + dst_sample_rate == other.dst_sample_rate && + (av_channel_layout_compare(&channel_layout, &other.channel_layout) == 0) && + (av_channel_layout_compare(&dst_channel_layout, &other.dst_channel_layout) == 0); } bool operator!=(ResamplerConfig const &other) const @@ -153,7 +160,8 @@ struct RgbVideoFrame nanoseconds pts{0}; RgbVideoFrame() - {} + { + } STX_MAKE_PINNED(RgbVideoFrame) @@ -177,7 +185,8 @@ struct RgbVideoFrame /// NOTE: FFMPEG will segfault if you use a custom allocated memory that doesn't meet its alignment/size requirements /// as it will perform some aligned/packed SIMD operations (if supported, as in most cases) - int nbytes = av_image_alloc(planes, linesizes, new_extent.width, new_extent.height, AV_PIX_FMT_RGB24, 1); + int nbytes = av_image_alloc(planes, linesizes, new_extent.width, new_extent.height, + AV_PIX_FMT_RGB24, 1); ASH_CHECK(nbytes >= 0); pixels = planes[0]; extent = new_extent; @@ -189,26 +198,36 @@ struct RgbVideoFrame // TODO(lamarrr): add a flush packet and/or is_completed marker struct AudioDecodeContext { - AVCodecContext *codec = nullptr; // accessed only on the audio thread - AVStream *stream = nullptr; // accessed only on demuxer thread - stx::SpinLock packets_lock; // lock for packets - stx::Vec packets; // accessed on decoder and demuxer thread - AVFrame *frame = nullptr; // accessed only on decoder thread - std::atomic clock = 0; // accessed on main/presentation and decoder thread - stx::Vec samples; // usually in the target device's sample format, accessed on decoder thread - usize bytes_consumed = 0; // portion of samples consumed, accessed only on decoder thread - SwrContext *resampler = nullptr; // accessed only on decoder thread - ResamplerConfig resampler_cfg; // accessed only on decoder thread - timepoint begin_timepoint; + AVCodecContext *codec = nullptr; // accessed only on the audio thread + AVStream *stream = nullptr; // accessed only on demuxer thread + stx::SpinLock packets_lock; // lock for packets + stx::Vec packets; // accessed on decoder and demuxer thread + AVFrame *frame = nullptr; // accessed only on decoder thread + std::atomic clock = + 0; // accessed on main/presentation and decoder thread + stx::Vec + samples; // usually in the target device's sample format, accessed on decoder thread + usize bytes_consumed = 0; // portion of samples consumed, accessed only on decoder thread + SwrContext *resampler = nullptr; // accessed only on decoder thread + ResamplerConfig resampler_cfg; // accessed only on decoder thread + timepoint begin_timepoint; /// COMMANDS - stx::SpinLock cmd_lock; - bool pause_requested = false; + stx::SpinLock cmd_lock; + bool pause_requested = false; stx::Option seek_request; - AudioDecodeContext(AVCodecContext *icodec, AVStream *istream, AVFrame *iframe, SwrContext *iresampler, ResamplerConfig iresampler_cfg, timepoint ibegin_timepoint) : - codec{icodec}, stream{istream}, frame{iframe}, resampler{iresampler}, resampler_cfg{iresampler_cfg}, begin_timepoint{ibegin_timepoint} - {} + AudioDecodeContext(AVCodecContext *icodec, AVStream *istream, AVFrame *iframe, + SwrContext *iresampler, ResamplerConfig iresampler_cfg, + timepoint ibegin_timepoint) : + codec{icodec}, + stream{istream}, + frame{iframe}, + resampler{iresampler}, + resampler_cfg{iresampler_cfg}, + begin_timepoint{ibegin_timepoint} + { + } STX_MAKE_PINNED(AudioDecodeContext) @@ -238,28 +257,35 @@ struct AudioDecodeContext struct VideoDecodeContext { - AVCodecContext *codec = nullptr; // accessed only on decoder thread - AVStream *stream = nullptr; // accessed only on demuxer thread - stx::SpinLock packets_lock; // locks the packets - stx::Vec packets; // accessed on demuxer and decoder thread - AVFrame *frame = nullptr; // accessed only on decoder thread - stx::SpinLock rgb_frame_lock; // locks rgb_frame - RgbVideoFrame rgb_frame; // accessed on decoder thread and main/presentation thread - SwsContext *rescaler = nullptr; // only accessed on decoder thread - nanoseconds timebase{0}; // only accessed on main/presentation, only written to once - nanoseconds last_frame_pts{0}; // accessed only on main/presentation - nanoseconds last_frame_pts_interval{0}; // accessed only on main/presentation - std::atomic last_frame_pts_timepoint{0}; // duration from begin_timepoint, accessed on audio thread, and main/presentation - nanoseconds frame_timer{0}; // accessed only on main/presentation - timepoint begin_timepoint; // accessed only on main/presentation, immutable + AVCodecContext *codec = nullptr; // accessed only on decoder thread + AVStream *stream = nullptr; // accessed only on demuxer thread + stx::SpinLock packets_lock; // locks the packets + stx::Vec packets; // accessed on demuxer and decoder thread + AVFrame *frame = nullptr; // accessed only on decoder thread + stx::SpinLock rgb_frame_lock; // locks rgb_frame + RgbVideoFrame rgb_frame; // accessed on decoder thread and main/presentation thread + SwsContext *rescaler = nullptr; // only accessed on decoder thread + nanoseconds timebase{0}; // only accessed on main/presentation, only written to once + nanoseconds last_frame_pts{0}; // accessed only on main/presentation + nanoseconds last_frame_pts_interval{0}; // accessed only on main/presentation + std::atomic last_frame_pts_timepoint{ + 0}; // duration from begin_timepoint, accessed on audio thread, and main/presentation + nanoseconds frame_timer{0}; // accessed only on main/presentation + timepoint begin_timepoint; // accessed only on main/presentation, immutable /// COMMANDS stx::SpinLock cmd_lock; bool pause_requested = false; - VideoDecodeContext(AVCodecContext *icodec, AVStream *istream, AVFrame *iframe, timepoint ibegin_timepoint) : - codec{icodec}, stream{istream}, frame{iframe}, timebase{timebase_to_ns(istream->time_base)}, begin_timepoint{ibegin_timepoint} - {} + VideoDecodeContext(AVCodecContext *icodec, AVStream *istream, AVFrame *iframe, + timepoint ibegin_timepoint) : + codec{icodec}, + stream{istream}, + frame{iframe}, + timebase{timebase_to_ns(istream->time_base)}, + begin_timepoint{ibegin_timepoint} + { + } STX_MAKE_PINNED(VideoDecodeContext) @@ -291,8 +317,10 @@ struct VideoDecodeContext // repeated and span over multiple cycles. nanoseconds get_clock_time() const { - timepoint last_frame_pts_timepoint = begin_timepoint + nanoseconds{this->last_frame_pts_timepoint.load(std::memory_order_relaxed)}; - timepoint now = Clock::now(); + timepoint last_frame_pts_timepoint = + begin_timepoint + + nanoseconds{this->last_frame_pts_timepoint.load(std::memory_order_relaxed)}; + timepoint now = Clock::now(); return last_frame_pts + (now - last_frame_pts_timepoint); } @@ -300,7 +328,9 @@ struct VideoDecodeContext void update_rgb_frame() { ASH_CHECK(frame->pts != AV_NOPTS_VALUE); - rescaler = sws_getCachedContext(rescaler, frame->width, frame->height, AS(AVPixelFormat, frame->format), frame->width, frame->height, AV_PIX_FMT_RGB24, 0, nullptr, nullptr, nullptr); + rescaler = sws_getCachedContext(rescaler, frame->width, frame->height, + AS(AVPixelFormat, frame->format), frame->width, frame->height, + AV_PIX_FMT_RGB24, 0, nullptr, nullptr, nullptr); ASH_CHECK(rescaler != nullptr); rgb_frame_lock.lock(); @@ -341,8 +371,13 @@ struct VideoDecodeContext if (audio_pts.is_some()) { - nanoseconds diff = frame_pts - audio_pts.value(); // time difference between present audio and video frames - nanoseconds sync_threshold = pts_interval > SYNC_THRESHOLD ? pts_interval : SYNC_THRESHOLD; // skip or repeat the frame. Take delay into account we still doesn't "know if this is the best guess." + nanoseconds diff = + frame_pts - + audio_pts.value(); // time difference between present audio and video frames + nanoseconds sync_threshold = + pts_interval > SYNC_THRESHOLD ? + pts_interval : + SYNC_THRESHOLD; // skip or repeat the frame. Take delay into account we still doesn't "know if this is the best guess." if (std::chrono::abs(diff) < NO_SYNC_THRESHOLD) { @@ -350,7 +385,8 @@ struct VideoDecodeContext { delay = nanoseconds{0}; } - else if (diff >= sync_threshold) // audio frame is lagging behind video frame, slow down + else if (diff >= + sync_threshold) // audio frame is lagging behind video frame, slow down { delay = 2 * delay; } @@ -369,7 +405,8 @@ struct VideoDecodeContext actual_delay = SYNC_THRESHOLD; } - last_frame_pts_timepoint.store((current_timepoint - begin_timepoint).count(), std::memory_order_relaxed); + last_frame_pts_timepoint.store((current_timepoint - begin_timepoint).count(), + std::memory_order_relaxed); return actual_delay; } @@ -397,9 +434,11 @@ struct VideoDemuxer FILE *file = nullptr; stx::String path; - VideoDemuxer(AVIOContext *iio_ctx, AVFormatContext *ictx, AVPacket *ipacket, FILE *ifile, stx::String ipath) : + VideoDemuxer(AVIOContext *iio_ctx, AVFormatContext *ictx, AVPacket *ipacket, FILE *ifile, + stx::String ipath) : io_ctx{iio_ctx}, fmt_ctx{ictx}, packet{ipacket}, file{ifile}, path{std::move(ipath)} - {} + { + } STX_MAKE_PINNED(VideoDemuxer) @@ -480,12 +519,18 @@ struct VideoDemuxer std::FILE *file = std::fopen(path.c_str(), "rb"); ASH_CHECK(file != nullptr); - void *avio_buffer = av_malloc(AVIO_BUFFER_SIZE); // TODO(lamarrr): this memory is presently being leaked + void *avio_buffer = + av_malloc(AVIO_BUFFER_SIZE); // TODO(lamarrr): this memory is presently being leaked ASH_CHECK(avio_buffer != nullptr); - stx::Rc demuxer = stx::rc::make_inplace(stx::os_allocator, nullptr, nullptr, nullptr, nullptr, stx::string::make(stx::os_allocator, path).unwrap()).unwrap(); + stx::Rc demuxer = + stx::rc::make_inplace(stx::os_allocator, nullptr, nullptr, nullptr, nullptr, + stx::string::make(stx::os_allocator, path).unwrap()) + .unwrap(); - AVIOContext *io_ctx = avio_alloc_context(AS(uchar *, avio_buffer), AVIO_BUFFER_SIZE, 0, demuxer.handle, packet_file_read_callback, nullptr, packet_file_seek_callback); + AVIOContext *io_ctx = + avio_alloc_context(AS(uchar *, avio_buffer), AVIO_BUFFER_SIZE, 0, demuxer.handle, + packet_file_read_callback, nullptr, packet_file_seek_callback); ASH_CHECK(io_ctx != nullptr); AVFormatContext *fmt_ctx = avformat_alloc_context(); @@ -507,7 +552,8 @@ struct VideoDemuxer return stx::Err(DemuxError::StreamNotFound); } - ASH_LOG_INFO(MediaPlayer, "Found Stream(s) in Media File {}. Dumping Metadata.", std::string_view{path}); + ASH_LOG_INFO(MediaPlayer, "Found Stream(s) in Media File {}. Dumping Metadata.", + std::string_view{path}); AVDictionaryEntry *prev = nullptr; do @@ -522,7 +568,10 @@ struct VideoDemuxer for (uint i = 0; i < fmt_ctx->nb_streams; i++) { AVStream *stream = fmt_ctx->streams[i]; - ASH_LOG_INFO(MediaPlayer, "Dumping Metadata for Media File -> {} Stream: {}, type={}, codec={}", std::string_view{path}, i, av_get_media_type_string(stream->codecpar->codec_type), avcodec_get_name(stream->codecpar->codec_id)); + ASH_LOG_INFO( + MediaPlayer, "Dumping Metadata for Media File -> {} Stream: {}, type={}, codec={}", + std::string_view{path}, i, av_get_media_type_string(stream->codecpar->codec_type), + avcodec_get_name(stream->codecpar->codec_id)); AVDictionaryEntry *prev = nullptr; do { @@ -537,7 +586,8 @@ struct VideoDemuxer return stx::Ok(std::move(demuxer)); } - static stx::Result make_decoder_for_stream(std::string_view source, AVStream *stream) + static stx::Result make_decoder_for_stream(std::string_view source, + AVStream *stream) { AVCodec const *codec = avcodec_find_decoder(stream->codecpar->codec_id); @@ -584,7 +634,13 @@ struct VideoDemuxer struct AudioDeviceInfo { stx::String name; - SDL_AudioSpec spec{.freq = 48000, .format = SDL_AUDIO_S16SYS, .channels = 2, .samples = 4800, .size = 4800 * 2, .callback = nullptr, .userdata = nullptr}; + SDL_AudioSpec spec{.freq = 48000, + .format = SDL_AUDIO_S16SYS, + .channels = 2, + .samples = 4800, + .size = 4800 * 2, + .callback = nullptr, + .userdata = nullptr}; static stx::Vec enumerate() { @@ -601,7 +657,10 @@ struct AudioDeviceInfo continue; } - devices.push(AudioDeviceInfo{.name = stx::string::make(stx::os_allocator, device_name).unwrap(), .spec = spec}).unwrap(); + devices + .push(AudioDeviceInfo{.name = stx::string::make(stx::os_allocator, device_name).unwrap(), + .spec = spec}) + .unwrap(); } return devices; @@ -618,7 +677,10 @@ struct AudioDeviceInfo return stx::None; } - AudioDeviceInfo info{.name = name == nullptr ? stx::string::make_static("") : stx::string::make(stx::os_allocator, name).unwrap(), .spec = spec}; + AudioDeviceInfo info{.name = name == nullptr ? + stx::string::make_static("") : + stx::string::make(stx::os_allocator, name).unwrap(), + .spec = spec}; SDL_free(name); @@ -647,9 +709,9 @@ struct AudioDevice stx::SpinLock audio_sources_lock; stx::Vec audio_sources; - AudioDevice(SDL_AudioDeviceID iid, AudioDeviceInfo iinfo) : - id{iid}, info{std::move(iinfo)} - {} + AudioDevice(SDL_AudioDeviceID iid, AudioDeviceInfo iinfo) : id{iid}, info{std::move(iinfo)} + { + } STX_MAKE_PINNED(AudioDevice) @@ -679,12 +741,14 @@ struct AudioDevice info.is_open = info.source->mix(stream, This->info.spec); } - auto [open, closed] = This->audio_sources.span().partition([](AudioSourceEntry const &src) { return src.is_open; }); + auto [open, closed] = This->audio_sources.span().partition( + [](AudioSourceEntry const &src) { return src.is_open; }); (void) open; if (!closed.is_empty()) { - ASH_LOG_INFO(MediaPlayer, "Closing {} Audio Sources For Audio Device -> {} (id = {})", closed.size(), This->info.name.view(), This->id); + ASH_LOG_INFO(MediaPlayer, "Closing {} Audio Sources For Audio Device -> {} (id = {})", + closed.size(), This->info.name.view(), This->id); } This->audio_sources.erase(closed); @@ -750,7 +814,10 @@ struct AudioDevice default: { out.channels = 2; - ASH_LOG_WARN(MediaPlayer, "Unrecognized number of channels = {} on audio device: {}, attempting to use: {}", spec.channels, name, out.channels); + ASH_LOG_WARN( + MediaPlayer, + "Unrecognized number of channels = {} on audio device: {}, attempting to use: {}", + spec.channels, name, out.channels); break; } } @@ -767,17 +834,21 @@ struct AudioDevice static stx::Option> open_default() { TRY_SOME(info, AudioDeviceInfo::get_default()); - stx::Rc dev = stx::rc::make_inplace(stx::os_allocator, AS(SDL_AudioDeviceID, 0), AudioDeviceInfo{}).unwrap(); + stx::Rc dev = stx::rc::make_inplace(stx::os_allocator, AS(SDL_AudioDeviceID, 0), + AudioDeviceInfo{}) + .unwrap(); SDL_AudioSpec desired_spec = make_spec(info.spec, "default", dev.handle); // .size is modified to the hardware buffer size // we are flexible with number of samples and frequency change - SDL_AudioDeviceID id = SDL_OpenAudioDevice(nullptr, 0, &desired_spec, &dev->info.spec, SDL_AUDIO_ALLOW_ANY_CHANGE); + SDL_AudioDeviceID id = + SDL_OpenAudioDevice(nullptr, 0, &desired_spec, &dev->info.spec, SDL_AUDIO_ALLOW_ANY_CHANGE); if (id == 0) { - ASH_LOG_ERR(MediaPlayer, "Failed To Open Default Audio Device, SDL Error: {}", SDL_GetError()); + ASH_LOG_ERR(MediaPlayer, "Failed To Open Default Audio Device, SDL Error: {}", + SDL_GetError()); return stx::None; } else @@ -793,17 +864,21 @@ struct AudioDevice static stx::Option> open(AudioDeviceInfo const &info) { - stx::Rc dev = stx::rc::make_inplace(stx::os_allocator, AS(SDL_AudioDeviceID, 0), AudioDeviceInfo{}).unwrap(); + stx::Rc dev = stx::rc::make_inplace(stx::os_allocator, AS(SDL_AudioDeviceID, 0), + AudioDeviceInfo{}) + .unwrap(); SDL_AudioSpec desired_spec = make_spec(info.spec, info.name, dev.handle); // .size is modified to the hardware buffer size // we are flexible with number of samples and frequency change - SDL_AudioDeviceID id = SDL_OpenAudioDevice(info.name.c_str(), 0, &desired_spec, &dev->info.spec, SDL_AUDIO_ALLOW_ANY_CHANGE); + SDL_AudioDeviceID id = SDL_OpenAudioDevice(info.name.c_str(), 0, &desired_spec, &dev->info.spec, + SDL_AUDIO_ALLOW_ANY_CHANGE); if (id == 0) { - ASH_LOG_ERR(MediaPlayer, "Failed To Open Audio Device: {}, SDL Error: {}", info.name.c_str(), SDL_GetError()); + ASH_LOG_ERR(MediaPlayer, "Failed To Open Audio Device: {}, SDL Error: {}", info.name.c_str(), + SDL_GetError()); return stx::None; } else @@ -826,14 +901,15 @@ struct MediaPlayerAudioSource : public AudioSource MediaPlayerAudioSource(stx::Promise ipromise, stx::Rc ictx) : promise{std::move(ipromise)}, ctx{std::move(ictx)} - {} + { + } virtual ~MediaPlayerAudioSource() override = default; virtual bool mix(stx::Span stream, SDL_AudioSpec spec) override { - int volume = AS(int, this->volume.load(std::memory_order_relaxed)); - volume = (volume * 128) / 255; // convert to SDL volume range 0-128 + int volume = AS(int, this->volume.load(std::memory_order_relaxed)); + volume = (volume * 128) / 255; // convert to SDL volume range 0-128 usize bytes_written = 0; AVSampleFormat sample_fmt = AV_SAMPLE_FMT_NONE; AVChannelLayout channel_layout = AV_CHANNEL_LAYOUT_MONO; @@ -864,7 +940,8 @@ struct MediaPlayerAudioSource : public AudioSource break; default: { - ASH_LOG_ERR(MediaPlayer, "encountered unsupported SDL Device audio format: {}", AS(int, spec.format)); + ASH_LOG_ERR(MediaPlayer, "encountered unsupported SDL Device audio format: {}", + AS(int, spec.format)); is_open = false; } break; @@ -938,20 +1015,27 @@ struct MediaPlayerAudioSource : public AudioSource ctx->cmd_lock.unlock(); if (!needs_pause) - while (is_open && bytes_written < stream.size() && (expected_state = promise.fetch_cancel_request()) != stx::CancelState::Canceled) + while (is_open && bytes_written < stream.size() && + (expected_state = promise.fetch_cancel_request()) != stx::CancelState::Canceled) { if (ctx->bytes_consumed != ctx->samples.size()) { - usize bytes_left = stream.size() - bytes_written; - usize bytes_to_write = std::min(bytes_left, AS(usize, ctx->samples.size() - ctx->bytes_consumed)); + usize bytes_left = stream.size() - bytes_written; + usize bytes_to_write = + std::min(bytes_left, AS(usize, ctx->samples.size() - ctx->bytes_consumed)); - ASH_SDL_CHECK(SDL_MixAudioFormat(stream.data() + bytes_written, ctx->samples.data() + ctx->bytes_consumed, spec.format, bytes_to_write, volume) == 0); + ASH_SDL_CHECK(SDL_MixAudioFormat(stream.data() + bytes_written, + ctx->samples.data() + ctx->bytes_consumed, spec.format, + bytes_to_write, volume) == 0); bytes_written += bytes_to_write; ctx->bytes_consumed += bytes_to_write; - usize nsamples_written = (bytes_to_write / spec.channels) / av_get_bytes_per_sample(sample_fmt); + usize nsamples_written = + (bytes_to_write / spec.channels) / av_get_bytes_per_sample(sample_fmt); - ctx->clock.fetch_add(AS(nanoseconds::rep, 1'000'000'000LL * AS(f32, nsamples_written) / AS(f32, spec.freq)), std::memory_order_relaxed); + ctx->clock.fetch_add(AS(nanoseconds::rep, + 1'000'000'000LL * AS(f32, nsamples_written) / AS(f32, spec.freq)), + std::memory_order_relaxed); } else { @@ -1003,13 +1087,12 @@ struct MediaPlayerAudioSource : public AudioSource } } - ResamplerConfig target_cfg{ - .fmt = AS(AVSampleFormat, ctx->frame->format), - .dst_fmt = sample_fmt, - .sample_rate = ctx->frame->sample_rate, - .dst_sample_rate = spec.freq, - .channel_layout = ctx->frame->ch_layout, - .dst_channel_layout = channel_layout}; + ResamplerConfig target_cfg{.fmt = AS(AVSampleFormat, ctx->frame->format), + .dst_fmt = sample_fmt, + .sample_rate = ctx->frame->sample_rate, + .dst_sample_rate = spec.freq, + .channel_layout = ctx->frame->ch_layout, + .dst_channel_layout = channel_layout}; if (ctx->resampler_cfg != target_cfg || ctx->resampler == nullptr) { @@ -1018,8 +1101,10 @@ struct MediaPlayerAudioSource : public AudioSource swr_free(&ctx->resampler); } - error = swr_alloc_set_opts2(&ctx->resampler, &target_cfg.dst_channel_layout, target_cfg.dst_fmt, target_cfg.dst_sample_rate, - &ctx->frame->ch_layout, target_cfg.fmt, target_cfg.sample_rate, 0, nullptr); + error = swr_alloc_set_opts2(&ctx->resampler, &target_cfg.dst_channel_layout, + target_cfg.dst_fmt, target_cfg.dst_sample_rate, + &ctx->frame->ch_layout, target_cfg.fmt, + target_cfg.sample_rate, 0, nullptr); if (error != 0) { @@ -1047,7 +1132,8 @@ struct MediaPlayerAudioSource : public AudioSource break; } - int max_buffer_size = av_samples_get_buffer_size(nullptr, spec.channels, max_nsamples, target_cfg.dst_fmt, 1); + int max_buffer_size = av_samples_get_buffer_size(nullptr, spec.channels, max_nsamples, + target_cfg.dst_fmt, 1); if (max_buffer_size < 0) { @@ -1060,7 +1146,8 @@ struct MediaPlayerAudioSource : public AudioSource u8 *out = ctx->samples.data(); - int nsamples = swr_convert(ctx->resampler, &out, max_nsamples, (u8 const **) ctx->frame->data, ctx->frame->nb_samples); + int nsamples = swr_convert(ctx->resampler, &out, max_nsamples, + (u8 const **) ctx->frame->data, ctx->frame->nb_samples); av_frame_unref(ctx->frame); @@ -1071,7 +1158,8 @@ struct MediaPlayerAudioSource : public AudioSource break; } - int buffer_size = av_samples_get_buffer_size(nullptr, spec.channels, nsamples, target_cfg.dst_fmt, 1); + int buffer_size = + av_samples_get_buffer_size(nullptr, spec.channels, nsamples, target_cfg.dst_fmt, 1); if (buffer_size < 0) { @@ -1085,15 +1173,20 @@ struct MediaPlayerAudioSource : public AudioSource usize bytes_left = stream.size() - bytes_written; usize bytes_to_write = std::min(bytes_left, AS(usize, buffer_size)); - ASH_SDL_CHECK(SDL_MixAudioFormat(stream.data() + bytes_written, ctx->samples.data(), spec.format, bytes_to_write, volume) == 0); + ASH_SDL_CHECK(SDL_MixAudioFormat(stream.data() + bytes_written, ctx->samples.data(), + spec.format, bytes_to_write, volume) == 0); ctx->bytes_consumed = bytes_to_write; bytes_written += bytes_to_write; - usize nsamples_written = (bytes_to_write / spec.channels) / av_get_bytes_per_sample(target_cfg.dst_fmt); + usize nsamples_written = + (bytes_to_write / spec.channels) / av_get_bytes_per_sample(target_cfg.dst_fmt); - ctx->clock.store(pts.count() + AS(nanoseconds::rep, 1'000'000'000LL * AS(f32, nsamples_written) / AS(f32, spec.freq)), std::memory_order_relaxed); + ctx->clock.store(pts.count() + + AS(nanoseconds::rep, + 1'000'000'000LL * AS(f32, nsamples_written) / AS(f32, spec.freq)), + std::memory_order_relaxed); } } @@ -1157,13 +1250,9 @@ struct MediaState std::atomic buffering_state{MediaBufferingState::NotBuffered}; }; - - - // ExactVideoFrame, - // AdvanceVideoFrame, - // BackwardVideoFrame - - +// ExactVideoFrame, +// AdvanceVideoFrame, +// BackwardVideoFrame struct MediaSession { @@ -1228,7 +1317,8 @@ struct MediaPlayer : public Subsystem codec = av_codec_iterate(&iter); if (codec != nullptr) { - ASH_LOG_INFO(MediaPlayer, "codec -> name: {}, long name: {}, media type: {}", codec->name, codec->long_name, codec->type); + ASH_LOG_INFO(MediaPlayer, "codec -> name: {}, long name: {}, media type: {}", codec->name, + codec->long_name, codec->type); } } while (codec != nullptr); } @@ -1247,7 +1337,8 @@ struct MediaPlayer : public Subsystem } virtual void on_exit(Context &ctx) - {} + { + } virtual std::string_view get_name() { @@ -1284,15 +1375,17 @@ struct MediaPlayer : public Subsystem switch (request.type) { - case MediaSeekType::ExactVideoFrame:{ - if(video.is_ok()){ - video.value()->l + case MediaSeekType::ExactVideoFrame: + { + if (video.is_ok()) + { + video.value()->l + } } - } - break; - - default: break; + + default: + break; } if (audio.is_ok()) @@ -1302,12 +1395,15 @@ struct MediaPlayer : public Subsystem audio.value()->packets.clear(); audio.value()->packets_lock.unlock(); audio.value()->clock(); - timebase_convert(request.value, session->audio_decode_ctx.value().value()->stream->time_base, session->video_decode_ctx.value().value()->stream->time_base); + timebase_convert(request.value, + session->audio_decode_ctx.value().value()->stream->time_base, + session->video_decode_ctx.value().value()->stream->time_base); if () { i64 video_frame = 0; - av_seek_frame(session->demuxer.value().value()->fmt_ctx, session->audio_decode_ctx.value().value()->stream->index, ); + av_seek_frame(session->demuxer.value().value()->fmt_ctx, + session->audio_decode_ctx.value().value()->stream->index, ); } } @@ -1325,7 +1421,8 @@ struct MediaPlayer : public Subsystem void __create_demux_thread(MediaSession &session, std::string_view path) { - session.demux_thread = std::thread{[path_s = stx::string::make(stx::os_allocator, path).unwrap(), + session.demux_thread = std::thread{[path_s = + stx::string::make(stx::os_allocator, path).unwrap(), session = &session]() { ASH_LOG_INFO(MediaPlayer, "Demux Thread Running"); @@ -1357,7 +1454,10 @@ struct MediaPlayer : public Subsystem { ASH_LOG_INFO(MediaPlayer, "Found Video Stream in Media file: {}", path_s.view()); auto ctx = video.value(); - session->video_decode_ctx = stx::Some(VideoResult(stx::Ok(stx::rc::make_inplace(stx::os_allocator, ctx.codec, ctx.stream, ctx.frame, begin_timepoint).unwrap()))); + session->video_decode_ctx = stx::Some(VideoResult( + stx::Ok(stx::rc::make_inplace( + stx::os_allocator, ctx.codec, ctx.stream, ctx.frame, begin_timepoint) + .unwrap()))); } if (audio.is_err()) @@ -1368,8 +1468,12 @@ struct MediaPlayer : public Subsystem else { ASH_LOG_INFO(MediaPlayer, "Found Audio Stream in Media file: {}", path_s.view()); - auto ctx = audio.value(); - session->audio_decode_ctx = stx::Some(AudioResult(stx::Ok(stx::rc::make_inplace(stx::os_allocator, ctx.codec, ctx.stream, ctx.frame, nullptr, ResamplerConfig{}, begin_timepoint).unwrap()))); + auto ctx = audio.value(); + session->audio_decode_ctx = + stx::Some(AudioResult(stx::Ok(stx::rc::make_inplace( + stx::os_allocator, ctx.codec, ctx.stream, ctx.frame, + nullptr, ResamplerConfig{}, begin_timepoint) + .unwrap()))); } session->demux_lock.unlock(); @@ -1386,23 +1490,25 @@ struct MediaPlayer : public Subsystem // TODO(lamarrr): video decode thread? // TODO(lamarrr): what about playing, seeking, etc - while (!session->exit_requested.load(std::memory_order_relaxed) && - error >= 0 && + while (!session->exit_requested.load(std::memory_order_relaxed) && error >= 0 && session->demux_promise.fetch_cancel_request() == stx::CancelState::Executing) { - error = av_read_frame(session->demuxer.value().value()->fmt_ctx, session->demuxer.value().value()->packet); + error = av_read_frame(session->demuxer.value().value()->fmt_ctx, + session->demuxer.value().value()->packet); if (error >= 0) { AVPacket *packet = av_packet_alloc(); ASH_CHECK(packet != nullptr); av_packet_move_ref(packet, session->demuxer.value().value()->packet); - if (session->video_decode_ctx.value().is_ok() && packet->stream_index == session->video_decode_ctx.value().value()->stream->index) + if (session->video_decode_ctx.value().is_ok() && + packet->stream_index == session->video_decode_ctx.value().value()->stream->index) { session->video_decode_ctx.value().value()->packets_lock.lock(); session->video_decode_ctx.value().value()->packets.push_inplace(packet).unwrap(); session->video_decode_ctx.value().value()->packets_lock.unlock(); } - else if (session->audio_decode_ctx.value().is_ok() && packet->stream_index == session->audio_decode_ctx.value().value()->stream->index) + else if (session->audio_decode_ctx.value().is_ok() && + packet->stream_index == session->audio_decode_ctx.value().value()->stream->index) { session->audio_decode_ctx.value().value()->packets_lock.lock(); session->audio_decode_ctx.value().value()->packets.push_inplace(packet).unwrap(); @@ -1443,9 +1549,10 @@ struct MediaPlayer : public Subsystem { media_session session_id = next_session_id; next_session_id++; - auto it = sessions.emplace(session_id, stx::rc::make_inplace(stx::os_allocator, - stx::string::make(stx::os_allocator, path).unwrap(), - stx::make_promise(stx::os_allocator).unwrap()) + auto it = sessions.emplace(session_id, stx::rc::make_inplace( + stx::os_allocator, + stx::string::make(stx::os_allocator, path).unwrap(), + stx::make_promise(stx::os_allocator).unwrap()) .unwrap()); __create_demux_thread(*it.first->second, path); @@ -1520,25 +1627,33 @@ struct MediaPlayer : public Subsystem return stx::Ok(stx::Void{}); } - Result seek_time(media_session session, nanoseconds timepoint, MediaSeek seek = MediaSeek::Exact) + Result seek_time(media_session session, nanoseconds timepoint, + MediaSeek seek = MediaSeek::Exact) { auto pos = sessions.find(session); ASH_CHECK(pos != sessions.end()); - nanoseconds diff = timepoint - nanoseconds{pos->second.audio_decode_ctx.value()->clock.load(std::memory_order_relaxed)}; + nanoseconds diff = + timepoint - + nanoseconds{pos->second.audio_decode_ctx.value()->clock.load(std::memory_order_relaxed)}; // TODO(lamarrr): lock format contexts and decode contexts, also update the docs if (pos->second.audio_decode_ctx.is_some()) { - int error = av_seek_frame(pos->second.demuxer->fmt_ctx, pos->second.audio_decode_ctx.value()->stream->index, 0, diff.count() < 0 ? AVSEEK_FLAG_BACKWARD : 0); + int error = av_seek_frame(pos->second.demuxer->fmt_ctx, + pos->second.audio_decode_ctx.value()->stream->index, 0, + diff.count() < 0 ? AVSEEK_FLAG_BACKWARD : 0); } else if (pos->second.video_decode_ctx.is_some()) { - int error = av_seek_frame(pos->second.demuxer->fmt_ctx, pos->second.video_decode_ctx.value()->stream->index, 0, diff.count() < 0 ? AVSEEK_FLAG_BACKWARD : 0); + int error = av_seek_frame(pos->second.demuxer->fmt_ctx, + pos->second.video_decode_ctx.value()->stream->index, 0, + diff.count() < 0 ? AVSEEK_FLAG_BACKWARD : 0); } } - Result seek_frame(media_session session, usize frame, MediaSeek seek = MediaSeek::Exact); + Result seek_frame(media_session session, usize frame, + MediaSeek seek = MediaSeek::Exact); Result seek_preview_at_time(media_session session, nanoseconds timepoint); @@ -1611,13 +1726,16 @@ struct MediaPlayer : public Subsystem struct Video : public Widget { Video() - {} + { + } explicit Video(std::string_view source) - {} + { + } virtual ~Video() override - {} + { + } virtual WidgetInfo get_info(Context &ctx) override { @@ -1641,19 +1759,20 @@ struct Video : public Widget bool show_controls = true; }; -void media(){ - - spdlog::info("System theme: {}", (int) SDL_GetSystemTheme()); +void media() +{ + spdlog::info("System theme: {}", (int) SDL_GetSystemTheme()); stx::Vec devices = AudioDeviceInfo::enumerate(); for (AudioDeviceInfo const &dev : devices) { spdlog::info("name: {}, channels: {}, format: {}, samplerate: {}, nsamples: {}", - dev.name.c_str(), - dev.spec.channels, dev.spec.format, dev.spec.freq, dev.spec.samples); + dev.name.c_str(), dev.spec.channels, dev.spec.format, dev.spec.freq, + dev.spec.samples); } AudioDeviceInfo dev_info = AudioDeviceInfo::get_default().unwrap(); - spdlog::info("default device: {}, channels: {}, format: {}, samplerate: {}, nsamples: {}", dev_info.name.c_str(), dev_info.spec.channels, dev_info.spec.format, + spdlog::info("default device: {}, channels: {}, format: {}, samplerate: {}, nsamples: {}", + dev_info.name.c_str(), dev_info.spec.channels, dev_info.spec.format, dev_info.spec.freq, dev_info.spec.samples); MediaPlayer::dump_supported_codecs(); @@ -1664,79 +1783,87 @@ void media(){ stx::Promise promise = stx::make_promise(stx::os_allocator).unwrap(); stx::Rc audio_dev = AudioDevice::open(dev_info).unwrap(); - spdlog::info("opened device: {}, channels: {}, format: {}, samplerate: {}, nsamples: {}, size: {}, silence: {}", dev_info.name.c_str(), dev_info.spec.channels, - dev_info.spec.format, dev_info.spec.freq, dev_info.spec.samples, audio_dev->info.spec.size, (int) audio_dev->info.spec.silence); + spdlog::info("opened device: {}, channels: {}, format: {}, samplerate: {}, nsamples: {}, size: " + "{}, silence: {}", + dev_info.name.c_str(), dev_info.spec.channels, dev_info.spec.format, + dev_info.spec.freq, dev_info.spec.samples, audio_dev->info.spec.size, + (int) audio_dev->info.spec.silence); - auto audio_src = stx::rc::make_inplace(stx::os_allocator, stx::make_promise(stx::os_allocator).unwrap(), - audio_decode_ctx.share()) + auto audio_src = stx::rc::make_inplace( + stx::os_allocator, stx::make_promise(stx::os_allocator).unwrap(), + audio_decode_ctx.share()) .unwrap(); - audio_dev->add_source(stx::transmute(static_cast(audio_src.handle), audio_src.share())); + audio_dev->add_source( + stx::transmute(static_cast(audio_src.handle), audio_src.share())); audio_dev->play(); audio_src->volume.store(25, std::memory_order_relaxed); - std::thread video_decode_thread{ - [video_decode_ctx = video_decode_ctx.share(), - audio_ctx = media_ctx.share(), - promise = promise.share(), ctx = stx::rc::make_inplace(stx::os_allocator, Clock::now(), timebase_to_ns(video_decode_ctx->stream->time_base)).unwrap()]() { - int error = 0; + std::thread video_decode_thread{[video_decode_ctx = video_decode_ctx.share(), + audio_ctx = media_ctx.share(), promise = promise.share(), + ctx = stx::rc::make_inplace( + stx::os_allocator, Clock::now(), + timebase_to_ns(video_decode_ctx->stream->time_base)) + .unwrap()]() { + int error = 0; - while (error >= 0 && promise.fetch_cancel_request() == stx::CancelState::Executing) - { - video_decode_ctx->lock.lock(); - if (video_decode_ctx->packets.is_empty()) - { - video_decode_ctx->lock.unlock(); - continue; - } + while (error >= 0 && promise.fetch_cancel_request() == stx::CancelState::Executing) + { + video_decode_ctx->lock.lock(); + if (video_decode_ctx->packets.is_empty()) + { + video_decode_ctx->lock.unlock(); + continue; + } - AVPacket *packet = video_decode_ctx->packets[0]; - video_decode_ctx->packets.erase(video_decode_ctx->packets.span().slice(0, 1)); - video_decode_ctx->lock.unlock(); + AVPacket *packet = video_decode_ctx->packets[0]; + video_decode_ctx->packets.erase(video_decode_ctx->packets.span().slice(0, 1)); + video_decode_ctx->lock.unlock(); - error = avcodec_send_packet(video_decode_ctx->codec, packet); + error = avcodec_send_packet(video_decode_ctx->codec, packet); - av_packet_free(&packet); + av_packet_free(&packet); - if (error != 0) - { - // handle error - } + if (error != 0) + { + // handle error + } - while ((error = avcodec_receive_frame(video_decode_ctx->codec, video_decode_ctx->frame)) == 0) - { - ctx->load_frame(video_decode_ctx->frame); - nanoseconds delay = ctx->refresh(nanoseconds{audio_ctx->decode_ctx.clock.load(std::memory_order_relaxed)}, Clock::now()); - spdlog::info("sleeping for: {}ms", delay.count() / 1'000'000LL); - auto begin = Clock::now(); - while ((Clock::now() - begin) < delay) - std::this_thread::yield(); - } + while ((error = avcodec_receive_frame(video_decode_ctx->codec, video_decode_ctx->frame)) == 0) + { + ctx->load_frame(video_decode_ctx->frame); + nanoseconds delay = ctx->refresh( + nanoseconds{audio_ctx->decode_ctx.clock.load(std::memory_order_relaxed)}, Clock::now()); + spdlog::info("sleeping for: {}ms", delay.count() / 1'000'000LL); + auto begin = Clock::now(); + while ((Clock::now() - begin) < delay) + std::this_thread::yield(); + } - if (error == AVERROR(EAGAIN)) - { - error = 0; - } - else if (error == AVERROR(EOF)) - { - } - else - { - ASH_LOG_FFMPEG_ERR("Receiving Video Frame", error); - break; - } - } + if (error == AVERROR(EAGAIN)) + { + error = 0; + } + else if (error == AVERROR(EOF)) + { + } + else + { + ASH_LOG_FFMPEG_ERR("Receiving Video Frame", error); + break; + } + } - if (promise.fetch_cancel_request() == stx::CancelState::Canceled) - { - promise.notify_canceled(); - spdlog::info("video decode thread canceled"); - } - else - { - promise.notify_completed(); - spdlog::info("video decode thread completed"); - } - }}; + if (promise.fetch_cancel_request() == stx::CancelState::Canceled) + { + promise.notify_canceled(); + spdlog::info("video decode thread canceled"); + } + else + { + promise.notify_completed(); + spdlog::info("video decode thread completed"); + } + }}; // fmt_ctx->chapters; // fmt_ctx->metadata; // AV_DISPOSITION_ATTACHED_PIC contains album art diff --git a/ashura/src/rcg.cc b/ashura/src/rcg.cc index c47b8c8e0..f0f12a9f9 100644 --- a/ashura/src/rcg.cc +++ b/ashura/src/rcg.cc @@ -91,6 +91,29 @@ void CommandBuffer::update_buffer(stx::Span src, u64 dst_offset, gfx:: graph->driver->cmd_update_buffer(handle, src, dst_offset, dst); } +void gen_transfer_barriers(gfx::BufferUsageScope scope, gfx::QueueBufferMemoryBarrier[]) +{ + // pre- + // all ops that have side-effects + // post- +} + +void gen_transfer_barriers(gfx::ImageUsageScope scope, gfx::QueueImageMemoryBarrier[]) +{ +} + +void gen_compute_barriers(gfx::BufferUsageScope scope, gfx::QueueBufferMemoryBarrier[]) +{ +} + +void gen_compute_barriers(gfx::ImageUsageScope scope, gfx::QueueImageMemoryBarrier[]) +{ +} + +void gen_present_barriers(gfx::ImageUsageScope scope, gfx::QueueImageMemoryBarrier[]) +{ +} + void CommandBuffer::copy_image(gfx::Image src, gfx::Image dst, stx::Span copies) { @@ -131,12 +154,12 @@ void CommandBuffer::copy_image(gfx::Image src, gfx::Image dst, } } + graph->driver->cmd_copy_image(handle, src, dst, copies); + if (num_barriers > 0) { graph->driver->cmd_insert_barriers(handle, {}, stx::Span{barriers, num_barriers}); } - - graph->driver->cmd_copy_image(handle, src, dst, copies); } void CommandBuffer::copy_buffer_to_image(gfx::Buffer src, gfx::Image dst, diff --git a/ashura/src/subsystems/http_client.cc b/ashura/src/subsystems/http_client.cc index 8a1112332..f9f5130d1 100644 --- a/ashura/src/subsystems/http_client.cc +++ b/ashura/src/subsystems/http_client.cc @@ -7,9 +7,9 @@ struct HttpCurlMultiHandleImpl { CURLM *multi; // Pointer to the multi-handle - HttpCurlMultiHandleImpl(CURLM *init_multi) : - multi{init_multi} - {} + HttpCurlMultiHandleImpl(CURLM *init_multi) : multi{init_multi} + { + } ~HttpCurlMultiHandleImpl() { @@ -17,8 +17,7 @@ struct HttpCurlMultiHandleImpl } }; -HttpCurlMultiHandle::HttpCurlMultiHandle(CURLM *init_multi) : - impl(nullptr) +HttpCurlMultiHandle::HttpCurlMultiHandle(CURLM *init_multi) : impl(nullptr) { if (init_multi) impl = new HttpCurlMultiHandleImpl(init_multi); @@ -34,13 +33,15 @@ HttpCurlMultiHandle::~HttpCurlMultiHandle() // Implementation class definition struct HttpCurlEasyHandleImpl { - CURL *easy; - curl_slist *header; + CURL *easy; + curl_slist *header; stx::Rc parent; - HttpCurlEasyHandleImpl(CURL *easy_easy, curl_slist *easy_header, stx::Rc easy_parent) : + HttpCurlEasyHandleImpl(CURL *easy_easy, curl_slist *easy_header, + stx::Rc easy_parent) : easy(easy_easy), header(easy_header), parent(std::move(easy_parent)) - {} + { + } ~HttpCurlEasyHandleImpl() { @@ -50,15 +51,16 @@ struct HttpCurlEasyHandleImpl } }; -HttpCurlEasyHandle::HttpCurlEasyHandle(CURL *easy, curl_slist *header, stx::Rc parent) : +HttpCurlEasyHandle::HttpCurlEasyHandle(CURL *easy, curl_slist *header, + stx::Rc parent) : impl(new HttpCurlEasyHandleImpl(easy, header, std::move(parent))) { if (!impl->easy) stx::panic("Failed to initialize HttpCurlEasyHandle"); } -inline size_t curl_header_write_function(u8 const *bytes, size_t unit_size, - size_t nmemb, HttpTaskInfo *task_info) +inline size_t curl_header_write_function(u8 const *bytes, size_t unit_size, size_t nmemb, + HttpTaskInfo *task_info) { size_t total_size = nmemb * unit_size; @@ -70,8 +72,8 @@ inline size_t curl_header_write_function(u8 const *bytes, size_t unit_size, return total_size; } -inline size_t curl_content_write_function(u8 const *bytes, size_t unit_size, - size_t nmemb, HttpTaskInfo *task_info) +inline size_t curl_content_write_function(u8 const *bytes, size_t unit_size, size_t nmemb, + HttpTaskInfo *task_info) { size_t total_size = nmemb * unit_size; @@ -101,14 +103,16 @@ inline size_t curl_content_write_function(u8 const *bytes, size_t unit_size, return total_size; } -stx::Result, stx::AllocError> HttpTask::prepare_request( - stx::Allocator allocator, stx::Rc const &parent, HttpRequest const &request) +stx::Result, stx::AllocError> + HttpTask::prepare_request(stx::Allocator allocator, + stx::Rc const &parent, + HttpRequest const &request) { CURL *easy = curl_easy_init(); if (easy == nullptr) stx::panic("unexpected error from CURL"); - TRY_OK(easy_handle_rc, stx::rc::make_inplace( - allocator, easy, nullptr, parent.share())); + TRY_OK(easy_handle_rc, + stx::rc::make_inplace(allocator, easy, nullptr, parent.share())); HttpCurlEasyHandle *easy_handle = easy_handle_rc.handle; @@ -138,8 +142,8 @@ stx::Result, stx::AllocError> HttpTask::prepare_re easy_handle->impl->header = new_header; } - ASH_CURLE_CHECK(curl_easy_setopt(easy_handle->impl->easy, CURLOPT_HTTPHEADER, - easy_handle->impl->header)); + ASH_CURLE_CHECK( + curl_easy_setopt(easy_handle->impl->easy, CURLOPT_HTTPHEADER, easy_handle->impl->header)); ASH_CURLE_CHECK(curl_easy_setopt(easy_handle->impl->easy, CURLOPT_VERBOSE, 1L)); @@ -152,11 +156,9 @@ stx::Result, stx::AllocError> HttpTask::prepare_re void HttpTask::begin_request(CURL *easy, CURLM *multi, HttpTaskInfo *info_addr) { ASH_CURLE_CHECK(curl_easy_setopt(easy, CURLOPT_WRITEDATA, info_addr)); - ASH_CURLE_CHECK(curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, - curl_content_write_function)); + ASH_CURLE_CHECK(curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, curl_content_write_function)); ASH_CURLE_CHECK(curl_easy_setopt(easy, CURLOPT_HEADERDATA, info_addr)); - ASH_CURLE_CHECK(curl_easy_setopt(easy, CURLOPT_HEADERFUNCTION, - curl_header_write_function)); + ASH_CURLE_CHECK(curl_easy_setopt(easy, CURLOPT_HEADERFUNCTION, curl_header_write_function)); ASH_CURLM_CHECK(curl_multi_add_handle(multi, easy)); } @@ -183,23 +185,24 @@ void HttpTask::retrieve_optional_progress_info(CURL *easy, CURLINFO info, stx::O void HttpTask::update_progress() { - HttpProgress progress; - CURL *easyr = info.handle->easy.handle->impl->easy; + HttpProgress progress; + CURL *easyr = info.handle->easy.handle->impl->easy; retrieve_progress_info(easyr, CURLINFO_SIZE_UPLOAD_T, progress.bytesSent); retrieve_progress_info(easyr, CURLINFO_SIZE_DOWNLOAD_T, progress.bytesReceived); retrieve_progress_info(easyr, CURLINFO_SPEED_UPLOAD_T, progress.uploadSpeed); retrieve_progress_info(easyr, CURLINFO_SPEED_DOWNLOAD_T, progress.downloadSpeed); - retrieve_optional_progress_info(easyr, CURLINFO_CONTENT_LENGTH_UPLOAD_T, progress.contentUploadSize); - retrieve_optional_progress_info(easyr, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, progress.contentDownloadSize); + retrieve_optional_progress_info(easyr, CURLINFO_CONTENT_LENGTH_UPLOAD_T, + progress.contentUploadSize); + retrieve_optional_progress_info(easyr, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, + progress.contentDownloadSize); info.handle->updater.update(progress); } -stx::Result>, - stx::AllocError> +stx::Result>, stx::AllocError> HttpTask::launch(stx::Allocator allocator, HttpRequest const &request, - stx::Rc const &parent) + stx::Rc const &parent) { TRY_OK(easy, prepare_request(allocator, parent, request)); TRY_OK(updater, makeProgressMonitor(allocator)); @@ -207,19 +210,15 @@ stx::Result> auto future = promise.get_future(); - TRY_OK(task_info, - stx::rc::make_unique_inplace( - allocator, std::move(easy), stx::Vec{allocator}, - stx::Vec{allocator}, std::move(promise), - std::move(updater.second))); + TRY_OK(task_info, stx::rc::make_unique_inplace( + allocator, std::move(easy), stx::Vec{allocator}, + stx::Vec{allocator}, std::move(promise), std::move(updater.second))); begin_request(task_info.handle->easy.handle->impl->easy, - task_info.handle->easy.handle->impl->parent.handle->impl->multi, - task_info.handle); + task_info.handle->easy.handle->impl->parent.handle->impl->multi, task_info.handle); - return stx::Ok(std::make_tuple(HttpTask{std::move(task_info)}, - std::move(updater.first), - std::move(future))); + return stx::Ok( + std::make_tuple(HttpTask{std::move(task_info)}, std::move(updater.first), std::move(future))); } void HttpTask::finish(stx::Allocator allocator) @@ -230,34 +229,28 @@ void HttpTask::finish(stx::Allocator allocator) // get status and more completion info char const *effective_url = nullptr; - ASH_CURLE_CHECK( - curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &effective_url)); + ASH_CURLE_CHECK(curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &effective_url)); if (effective_url != nullptr) { - response.effectiveUrl = - stx::string::make(allocator, effective_url).unwrap(); + response.effectiveUrl = stx::string::make(allocator, effective_url).unwrap(); } curl_off_t total_time = 0; - ASH_CURLE_CHECK( - curl_easy_getinfo(easy, CURLINFO_TOTAL_TIME_T, &total_time)); + ASH_CURLE_CHECK(curl_easy_getinfo(easy, CURLINFO_TOTAL_TIME_T, &total_time)); response.totalTime = std::chrono::microseconds(total_time); curl_off_t total_downloaded = 0; curl_off_t total_uploaded = 0; - ASH_CURLE_CHECK(curl_easy_getinfo(easy, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, - &total_downloaded)); - ASH_CURLE_CHECK(curl_easy_getinfo(easy, CURLINFO_CONTENT_LENGTH_UPLOAD_T, - &total_uploaded)); + ASH_CURLE_CHECK(curl_easy_getinfo(easy, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &total_downloaded)); + ASH_CURLE_CHECK(curl_easy_getinfo(easy, CURLINFO_CONTENT_LENGTH_UPLOAD_T, &total_uploaded)); response.downloaded = total_downloaded; response.uploaded = total_uploaded; long response_code = 0; - ASH_CURLE_CHECK( - curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &response_code)); + ASH_CURLE_CHECK(curl_easy_getinfo(easy, CURLINFO_RESPONSE_CODE, &response_code)); response.code = u64{static_cast(response_code)}; @@ -275,18 +268,16 @@ void HttpClient::tick(Context &ctx, std::chrono::nanoseconds interval) { // poll statuses tasks_.span().for_each([](HttpTask &task) { - task.info.handle->last_status_poll = - task.info.handle->promise.fetch_status(); + task.info.handle->last_status_poll = task.info.handle->promise.fetch_status(); }); - auto to_erase = tasks_.span() - .partition([](HttpTask const &task) { - return task.info.handle->last_status_poll != - stx::FutureStatus::Canceled || - task.info.handle->last_status_poll == - stx::FutureStatus::Completed; - }) - .second; + auto to_erase = + tasks_.span() + .partition([](HttpTask const &task) { + return task.info.handle->last_status_poll != stx::FutureStatus::Canceled || + task.info.handle->last_status_poll == stx::FutureStatus::Completed; + }) + .second; tasks_.erase(to_erase); } @@ -295,18 +286,15 @@ void HttpClient::tick(Context &ctx, std::chrono::nanoseconds interval) task.update_progress(); if (task.info.handle->last_status_poll == stx::FutureStatus::Suspended && - task.info.handle->promise.fetch_suspend_request() == - stx::SuspendState::Executing) + task.info.handle->promise.fetch_suspend_request() == stx::SuspendState::Executing) { - ASH_CURLE_CHECK(curl_easy_pause(task.info.handle->easy.handle->impl->easy, - CURLPAUSE_CONT)); + ASH_CURLE_CHECK(curl_easy_pause(task.info.handle->easy.handle->impl->easy, CURLPAUSE_CONT)); task.info.handle->promise.notify_executing(); } } int num_running_handles = 0; - ASH_CURLM_CHECK( - curl_multi_perform(multi_.handle->impl->multi, &num_running_handles)); + ASH_CURLM_CHECK(curl_multi_perform(multi_.handle->impl->multi, &num_running_handles)); int num_messages_in_queue = 0; CURLMsg const *messages = @@ -319,15 +307,13 @@ void HttpClient::tick(Context &ctx, std::chrono::nanoseconds interval) num_messages_in_queue = 1; } - for (CURLMsg const &msg : - stx::Span(messages, num_messages_in_queue)) + for (CURLMsg const &msg : stx::Span(messages, num_messages_in_queue)) { if (msg.msg == CURLMSG_DONE) { - stx::Span task = tasks_.span().which( - [easy = msg.easy_handle](HttpTask const &task) { - return task.info.handle->easy.handle->impl->easy == easy; - }); + stx::Span task = tasks_.span().which([easy = msg.easy_handle](HttpTask const &task) { + return task.info.handle->easy.handle->impl->easy == easy; + }); ASH_CHECK(!task.is_empty()); diff --git a/ashura/tests/gfx.cc b/ashura/tests/gfx.cc index c5be178c4..afe3639d3 100644 --- a/ashura/tests/gfx.cc +++ b/ashura/tests/gfx.cc @@ -8,9 +8,9 @@ TEST(GFX, ReadAfterWrite) { QueueImageMemoryBarrier barrier; ImageState state; - ImageAccess shader_access{.stages = PipelineStages::FragmentShader | PipelineStages::VertexShader, - .access = Access::ShaderRead, - .layout = ImageLayout::ShaderReadOnlyOptimal}; + ImageAccess shader_access{.stages = PipelineStages::FragmentShader | PipelineStages::VertexShader, + .access = Access::ShaderRead, + .layout = ImageLayout::ShaderReadOnlyOptimal}; EXPECT_TRUE(state.sync(shader_access, barrier)); EXPECT_EQ(barrier.old_layout, ImageLayout::Undefined); @@ -52,10 +52,9 @@ TEST(GFX, ReadAfterWrite) TEST(GFX, WriteAfterRead) { QueueImageMemoryBarrier barrier; - ImageState state{.access = {ImageAccess{ - .stages = PipelineStages::None, - .access = Access::None, - .layout = ImageLayout::General}}, + ImageState state{.access = {ImageAccess{.stages = PipelineStages::None, + .access = Access::None, + .layout = ImageLayout::General}}, .sequence = AccessSequence::None}; ImageAccess shader_read1{.stages = PipelineStages::FragmentShader, diff --git a/ashura/tests/http.cc b/ashura/tests/http.cc index ae0dd84d2..79fb760c5 100644 --- a/ashura/tests/http.cc +++ b/ashura/tests/http.cc @@ -1,22 +1,20 @@ -#include "ashura/subsystems/http_client.h" #include "ashura/context.h" -#include "gtest/gtest.h" +#include "ashura/subsystems/http_client.h" #include "stx/scheduler.h" #include "stx/scheduler/scheduling/await.h" - +#include "gtest/gtest.h" using namespace ash; -Context _ctx; - +Context _ctx; TEST(Http, HttpClient) { - HttpClient client{stx::os_allocator}; + HttpClient client{stx::os_allocator}; stx::TaskScheduler scheduler{stx::os_allocator, std::chrono::steady_clock::now()}; auto [response, monitor] = client.get(stx::string::make_static("https://github.com")); - stx::Future a = stx::sched::await( + stx::Future a = stx::sched::await( scheduler, [](stx::Future response) { auto httpResponse = response.ref().unwrap(); @@ -35,5 +33,4 @@ TEST(Http, HttpClient) client.tick(_ctx, interval); scheduler.tick(interval); } - } \ No newline at end of file diff --git a/ashura/tests/layout.cc b/ashura/tests/layout.cc index 6a9530274..99a51b5f0 100644 --- a/ashura/tests/layout.cc +++ b/ashura/tests/layout.cc @@ -16,10 +16,9 @@ WidgetTree tree; TEST(FlexLayout, Start) { - gui::Flex flex{ - gui::FlexProps{}, Widget{}, - gui::Image{gui::ImageProps{.size = Constraint2D::absolute(100, 100)}}, - gui::Image{gui::ImageProps{.size = Constraint2D::absolute(200, 200)}}}; + gui::Flex flex{gui::FlexProps{}, Widget{}, + gui::Image{gui::ImageProps{.size = Constraint2D::absolute(100, 100)}}, + gui::Image{gui::ImageProps{.size = Constraint2D::absolute(200, 200)}}}; tree.build(ctx, flex); tree.layout(ctx, Vec2{1920, 1080}); @@ -49,13 +48,11 @@ TEST(FlexLayout, Start) TEST(FlexLayout, SpaceAround) { - gui::Flex flex{ - gui::FlexProps{ - .direction = Direction::H, - .main_align = MainAlign::SpaceAround, - .cross_align = CrossAlign::Center}, - Widget{}, gui::Image{gui::ImageProps{.size = Constraint2D::absolute(100, 100)}}, - gui::Image{gui::ImageProps{.size = Constraint2D::absolute(200, 200)}}}; + gui::Flex flex{gui::FlexProps{.direction = Direction::H, + .main_align = MainAlign::SpaceAround, + .cross_align = CrossAlign::Center}, + Widget{}, gui::Image{gui::ImageProps{.size = Constraint2D::absolute(100, 100)}}, + gui::Image{gui::ImageProps{.size = Constraint2D::absolute(200, 200)}}}; tree.build(ctx, flex); tree.layout(ctx, Vec2{1920, 1080}); @@ -91,12 +88,11 @@ TEST(FlexLayout, SpaceAround) TEST(FlexLayout, SpaceEvenly) { - gui::Flex flex{ - gui::FlexProps{.direction = Direction::H, - .main_align = MainAlign::SpaceEvenly, - .cross_align = CrossAlign::Center}, - Widget{}, gui::Image{gui::ImageProps{.size = Constraint2D::absolute(100, 100)}}, - gui::Image{gui::ImageProps{.size = Constraint2D::absolute(200, 200)}}}; + gui::Flex flex{gui::FlexProps{.direction = Direction::H, + .main_align = MainAlign::SpaceEvenly, + .cross_align = CrossAlign::Center}, + Widget{}, gui::Image{gui::ImageProps{.size = Constraint2D::absolute(100, 100)}}, + gui::Image{gui::ImageProps{.size = Constraint2D::absolute(200, 200)}}}; tree.build(ctx, flex); tree.layout(ctx, Vec2{1920, 1080}); @@ -132,12 +128,11 @@ TEST(FlexLayout, SpaceEvenly) TEST(FlexLayout, SpaceBetween) { - gui::Flex flex{ - gui::FlexProps{.direction = Direction::H, - .main_align = MainAlign::SpaceBetween, - .cross_align = CrossAlign::Center}, - Widget{}, gui::Image{gui::ImageProps{.size = Constraint2D::absolute(100, 100)}}, - gui::Image{gui::ImageProps{.size = Constraint2D::absolute(200, 200)}}}; + gui::Flex flex{gui::FlexProps{.direction = Direction::H, + .main_align = MainAlign::SpaceBetween, + .cross_align = CrossAlign::Center}, + Widget{}, gui::Image{gui::ImageProps{.size = Constraint2D::absolute(100, 100)}}, + gui::Image{gui::ImageProps{.size = Constraint2D::absolute(200, 200)}}}; tree.build(ctx, flex); tree.layout(ctx, Vec2{1920, 1080});