diff --git a/engine/include/vull/ui/painter.hh b/engine/include/vull/ui/painter.hh index e7a8dde7..a38d38d6 100644 --- a/engine/include/vull/ui/painter.hh +++ b/engine/include/vull/ui/painter.hh @@ -61,14 +61,18 @@ private: FontAtlas *m_atlas{nullptr}; void compile(vk::Context &context, vk::CommandBuffer &cmd_buf, Vec2u viewport_extent, - const vk::SampledImage &null_image); + const vk::SampledImage &null_image, const vk::SampledImage &shadow_image); uint32_t get_texture_index(const vk::SampledImage &image); public: - Painter() { m_bound_textures.push({}); } + Painter() { + m_bound_textures.push({}); + m_bound_textures.push({}); + } void bind_atlas(FontAtlas &atlas); void paint_rect(LayoutPoint position, LayoutSize size, const Colour &colour); + void paint_shadow(LayoutPoint p, LayoutSize s); void paint_image(LayoutPoint position, LayoutSize size, const vk::SampledImage &image); void paint_text(Font &font, LayoutPoint position, const Colour &colour, StringView text); void set_scissor(LayoutPoint position, LayoutSize size); diff --git a/engine/include/vull/ui/renderer.hh b/engine/include/vull/ui/renderer.hh index 085ea804..ab758c5d 100644 --- a/engine/include/vull/ui/renderer.hh +++ b/engine/include/vull/ui/renderer.hh @@ -21,6 +21,7 @@ class Renderer { vkb::DescriptorSetLayout m_descriptor_set_layout{nullptr}; vk::Pipeline m_pipeline; vk::Image m_null_image; + vk::Image m_shadow_image; public: explicit Renderer(vk::Context &context); diff --git a/engine/sources/ui/painter.cc b/engine/sources/ui/painter.cc index 57125b0f..3382660a 100644 --- a/engine/sources/ui/painter.cc +++ b/engine/sources/ui/painter.cc @@ -53,6 +53,38 @@ void Painter::paint_rect(LayoutPoint position, LayoutSize size, const Colour &co }); } +void Painter::paint_shadow(LayoutPoint p, LayoutSize s) { + auto push_cmd = [this](LayoutPoint start, LayoutPoint end, Vec2f uv_a, Vec2f uv_c) { + m_commands.push(PaintCommand{ + .position = start.round(), + .size = (end - start).round(), + .variant{TextCommand{ + .colour = Colour::from_rgb(0.0f, 0.0f, 0.0f, 2.1f), + .uv_a = uv_a, + .uv_c = uv_c, + .texture_index = 1, + }}, + }); + }; + + float uv_0 = 0.0f; + float uv_1 = 0.333333f; + float uv_2 = 0.666666f; + float uv_3 = 1.0f; + + auto m = p + s; + auto size = LayoutUnit::from_int_pixels(30); + + push_cmd(LayoutPoint(p.x() - size, p.y() - size), p, {uv_0, uv_0}, {uv_1, uv_1}); + push_cmd(LayoutPoint(p.x(), p.y() - size), LayoutPoint(m.x(), p.y()), {uv_1, uv_0}, {uv_2, uv_1}); + push_cmd(LayoutPoint(m.x(), p.y() - size), LayoutPoint(m.x() + size, p.y()), {uv_2, uv_0}, {uv_3, uv_1}); + push_cmd(LayoutPoint(p.x() - size, p.y()), LayoutPoint(p.x(), m.y()), {uv_0, uv_1}, {uv_1, uv_2}); + push_cmd(LayoutPoint(m.x(), p.y()), LayoutPoint(m.x() + size, m.y()), {uv_2, uv_1}, {uv_3, uv_2}); + push_cmd(LayoutPoint(p.x() - size, m.y()), LayoutPoint(p.x(), m.y() + size), {uv_0, uv_2}, {uv_1, uv_3}); + push_cmd(LayoutPoint(p.x(), m.y()), LayoutPoint(m.x(), m.y() + size), {uv_1, uv_2}, {uv_2, uv_3}); + push_cmd(LayoutPoint(m.x(), m.y()), LayoutPoint(m.x() + size, m.y() + size), {uv_2, uv_2}, {uv_3, uv_3}); +} + void Painter::paint_image(LayoutPoint position, LayoutSize size, const vk::SampledImage &image) { m_commands.push(PaintCommand{ .position = position.floor(), @@ -101,13 +133,14 @@ void Painter::unset_scissor() { } void Painter::compile(vk::Context &context, vk::CommandBuffer &cmd_buf, Vec2u viewport_extent, - const vk::SampledImage &null_image) { + const vk::SampledImage &null_image, const vk::SampledImage &shadow_image) { const auto descriptor_size = context.descriptor_size(vkb::DescriptorType::CombinedImageSampler); auto descriptor_buffer = context.create_buffer(m_bound_textures.size() * descriptor_size, vkb::BufferUsage::SamplerDescriptorBufferEXT, vk::MemoryUsage::HostToDevice); m_bound_textures[0] = BoundTexture{*null_image.view(), null_image.sampler()}; + m_bound_textures[1] = BoundTexture{*shadow_image.view(), shadow_image.sampler()}; auto *descriptor_data = descriptor_buffer.mapped(); for (const auto &texture : m_bound_textures) { diff --git a/engine/sources/ui/renderer.cc b/engine/sources/ui/renderer.cc index 4c0c1398..3b02aae4 100644 --- a/engine/sources/ui/renderer.cc +++ b/engine/sources/ui/renderer.cc @@ -5,8 +5,12 @@ #include #include #include +#include #include #include +#include +#include +#include #include #include #include @@ -103,6 +107,59 @@ Renderer::Renderer(vk::Context &context) : m_context(context) { .subresourceRange = m_null_image.full_view().range(), }); }); + + vkb::ImageCreateInfo shadow_image_ci{ + .sType = vkb::StructureType::ImageCreateInfo, + .imageType = vkb::ImageType::_2D, + .format = vkb::Format::R8G8B8A8Srgb, + .extent = {384, 384, 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = vkb::SampleCount::_1, + .tiling = vkb::ImageTiling::Optimal, + .usage = vkb::ImageUsage::Sampled | vkb::ImageUsage::TransferDst, + .sharingMode = vkb::SharingMode::Exclusive, + .initialLayout = vkb::ImageLayout::Undefined, + }; + m_shadow_image = m_context.create_image(shadow_image_ci, vk::MemoryUsage::DeviceOnly); + + auto staging_buffer = + m_context.create_buffer(384 * 384 * 4, vkb::BufferUsage::TransferSrc, vk::MemoryUsage::HostOnly); + auto stream = vpak::open("/9slice"); + VULL_EXPECT(stream->read({staging_buffer.mapped_raw(), staging_buffer.size()})); + + queue->immediate_submit([&](const vk::CommandBuffer &cmd_buf) { + vkb::ImageMemoryBarrier2 transfer_write_barrier{ + .sType = vkb::StructureType::ImageMemoryBarrier2, + .dstStageMask = vkb::PipelineStage2::Copy, + .dstAccessMask = vkb::Access2::TransferWrite, + .oldLayout = vkb::ImageLayout::Undefined, + .newLayout = vkb::ImageLayout::TransferDstOptimal, + .image = *m_shadow_image, + .subresourceRange = m_shadow_image.full_view().range(), + }; + vkb::BufferImageCopy copy{ + .imageSubresource{ + .aspectMask = vkb::ImageAspect::Color, + .layerCount = 1, + }, + .imageExtent = {384, 384, 1}, + }; + vkb::ImageMemoryBarrier2 image_read_barrier{ + .sType = vkb::StructureType::ImageMemoryBarrier2, + .srcStageMask = vkb::PipelineStage2::Copy, + .srcAccessMask = vkb::Access2::TransferWrite, + .dstStageMask = vkb::PipelineStage2::AllCommands, + .dstAccessMask = vkb::Access2::ShaderRead, + .oldLayout = vkb::ImageLayout::TransferDstOptimal, + .newLayout = vkb::ImageLayout::ReadOnlyOptimal, + .image = *m_shadow_image, + .subresourceRange = m_shadow_image.full_view().range(), + }; + cmd_buf.image_barrier(transfer_write_barrier); + cmd_buf.copy_buffer_to_image(staging_buffer, m_shadow_image, vkb::ImageLayout::TransferDstOptimal, copy); + cmd_buf.image_barrier(image_read_barrier); + }); } Renderer::~Renderer() { @@ -122,7 +179,15 @@ void Renderer::build_pass(vk::RenderGraph &graph, vk::ResourceId &target, Painte .b = vkb::ComponentSwizzle::One, .a = vkb::ComponentSwizzle::One, }) - .sampled(vk::Sampler::Nearest)); + .sampled(vk::Sampler::Nearest), + m_shadow_image + .swizzle_view({ + .r = vkb::ComponentSwizzle::One, + .g = vkb::ComponentSwizzle::One, + .b = vkb::ComponentSwizzle::One, + .a = vkb::ComponentSwizzle::Identity, + }) + .sampled(vk::Sampler::Linear)); }); } diff --git a/engine/sources/ui/window.cc b/engine/sources/ui/window.cc index ce6dfa34..8b42a9cf 100644 --- a/engine/sources/ui/window.cc +++ b/engine/sources/ui/window.cc @@ -43,6 +43,8 @@ void Window::paint(Painter &painter, LayoutPoint position) const { painter.paint_rect(position + m_content_pane->offset_in_parent(), {computed_width(), computed_height() - m_title_pane->computed_height()}, colour); + painter.paint_shadow(position, computed_size()); + // Paint children. Pane::paint(painter, position); }