diff --git a/rendering/vulkan14/vulkan14frame.cpp b/rendering/vulkan14/vulkan14frame.cpp index 56876c50..f0a0050e 100644 --- a/rendering/vulkan14/vulkan14frame.cpp +++ b/rendering/vulkan14/vulkan14frame.cpp @@ -2228,14 +2228,21 @@ void vulkan14_renderer::begin_colour_rendering(std::size_t const Index, bool con remember_rendering(rendering, colour_target_uses(targets)); } -// the depth of the ground under the cursor of the editor: the rendering is ended (which resolves the depth), the depth under the -// cursor copied, and the rendering begun again where it was +// the depth of the ground under the cursor of the editor: the rendering is ended (which resolves the depth), the depths around the +// cursor copied, and the rendering it was in begun again where it was. it's the colour pass, or the depth pass of the scene drawn +// before the colour (render_scene_depth()), whichever walks the ground first; the depth read before is taken first, once the gpu is +// done with it (opengl46_renderer::walk_editor_cursor()) void vulkan14_renderer::walk_editor_cursor() { auto &ctx{*m_context}; auto &scene{ctx.scene}; auto &draw{*m_draw}; - if ((m_current_viewport == nullptr) || (false == m_current_viewport->main) || (false == draw.rendering) || scene.cursor.pending || scene.targets.empty()) + if ((m_main != nullptr) || (m_current_viewport == nullptr) || (false == m_current_viewport->main) || (false == draw.rendering) || scene.targets.empty()) + { + return; + } + Update_Mouse_Position(); + if (scene.cursor.pending || (false == draw.resume.valid)) { return; } @@ -2244,52 +2251,43 @@ void vulkan14_renderer::walk_editor_cursor() glm::vec2 mousepos = Global.cursor_pos; // cursor coordinates are flipped compared to opengl, whose rows the images keep mousepos.y = Global.window_size.y - mousepos.y; - auto bufferpos{glm::ivec2(mousepos * glm::vec2(targets.extent.width, targets.extent.height) / glm::max(glm::vec2(Global.window_size), glm::vec2(1.f)))}; - bufferpos = glm::clamp(bufferpos, glm::ivec2(0, 0), glm::ivec2(targets.extent.width - 1, targets.extent.height - 1)); + glm::ivec2 const extent{static_cast(targets.extent.width), static_cast(targets.extent.height)}; + auto bufferpos{glm::ivec2(mousepos * glm::vec2(extent) / glm::max(glm::vec2(Global.window_size), glm::vec2(1.f)))}; + bufferpos = glm::clamp(bufferpos, glm::ivec2(0, 0), extent - 1); + // the pixels around the one under the cursor too, for the slope of the depth there (Update_Mouse_Position()) + auto const blocksize{glm::min(glm::ivec2(3), extent)}; + auto const blockcorner{glm::clamp(bufferpos - 1, glm::ivec2(0), extent - blocksize)}; - commands->end_rendering(); auto readback{std::make_shared()}; - if (readback->create(ctx.memory, sizeof(float), VK_BUFFER_USAGE_TRANSFER_DST_BIT, vk14::memory_kind::readback, "cursor depth")) + if (false == readback->create(ctx.memory, sizeof(float) * blocksize.x * blocksize.y, VK_BUFFER_USAGE_TRANSFER_DST_BIT, vk14::memory_kind::readback, "cursor depth")) { - vk14::barriers(commands, {vk14::use_of(*targets.depth, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_2_COPY_BIT, VK_ACCESS_2_TRANSFER_READ_BIT)}); - VkBufferImageCopy2 region{}; - region.sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2; - region.imageSubresource = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0, 1}; - region.imageOffset = {bufferpos.x, bufferpos.y, 0}; - region.imageExtent = {1, 1, 1}; - VkCopyImageToBufferInfo2 copy{}; - copy.sType = VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2; - copy.srcImage = targets.depth->handle(); - copy.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - copy.dstBuffer = readback->handle(); - copy.regionCount = 1; - copy.pRegions = ®ion; - commands->copy_image_to_buffer2(©); - host_read_barrier(commands); - scene.cursor.data = readback; - scene.cursor.value = ctx.timelinevalue + 1; - scene.cursor.pending = true; - scene.cursor.position = bufferpos; - vk14::barriers(commands, {vk14::use_of(*targets.depth, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, resolve_depth_stage, resolve_depth_access)}); + return; } - // what the rendering stored before is loaded again - vk14::barriers(commands, colour_target_uses(targets)); - begin_colour_rendering(0, false); - // the dynamic state starts anew with the rendering - auto const set{draw.set}; - auto const scene_offset{draw.scene_offset}; - auto const light_offset{draw.light_offset}; - auto const cluster_offset{draw.cluster_offset}; - auto const blending{draw.blending}; - auto *program{draw.program}; - auto const textures{draw.textures}; - start_draws(targets.formats, static_cast(targets.extent.width), static_cast(targets.extent.height), set, false); - draw.scene_offset = scene_offset; - draw.light_offset = light_offset; - draw.cluster_offset = cluster_offset; - draw.blending = blending; - draw.program = program; - draw.textures = textures; + suspend_rendering(); + vk14::barriers(commands, {vk14::use_of(*targets.depth, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_2_COPY_BIT, VK_ACCESS_2_TRANSFER_READ_BIT)}); + VkBufferImageCopy2 region{}; + region.sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2; + region.imageSubresource = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0, 1}; + region.imageOffset = {blockcorner.x, blockcorner.y, 0}; + region.imageExtent = {static_cast(blocksize.x), static_cast(blocksize.y), 1}; + VkCopyImageToBufferInfo2 copy{}; + copy.sType = VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2; + copy.srcImage = targets.depth->handle(); + copy.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + copy.dstBuffer = readback->handle(); + copy.regionCount = 1; + copy.pRegions = ®ion; + commands->copy_image_to_buffer2(©); + host_read_barrier(commands); + scene.cursor.data = readback; + scene.cursor.value = ctx.timelinevalue + 1; + scene.cursor.pending = true; + scene.cursor.position = bufferpos; + m_cursorblockcorner = blockcorner; + m_cursorblocksize = blocksize; + m_cursorbiased = draw.depthpass; + // the rendering the walk was in (the colour or the depth of the scene) goes on, its images brought back from the copy + resume_rendering(); } // the cascades of the sun shadows @@ -4340,10 +4338,42 @@ glm::dvec3 vulkan14_renderer::Update_Mouse_Position() if (cursor.pending && (ctx.completed() >= cursor.value) && (false == ctx.scene.targets.empty())) { cursor.pending = false; - float pointdepth{0.f}; - cursor.data->invalidate(0, sizeof(float)); - std::memcpy(&pointdepth, cursor.data->mapped(), sizeof(float)); + std::array depths{}; + auto const count{static_cast(std::clamp(m_cursorblocksize.x * m_cursorblocksize.y, 0, static_cast(depths.size())))}; + cursor.data->invalidate(0, sizeof(float) * count); + std::memcpy(depths.data(), cursor.data->mapped(), sizeof(float) * count); cursor.data.reset(); + auto const centre{cursor.position - m_cursorblockcorner}; + auto const depth_at = [&](glm::ivec2 const Pixel) { + if ((Pixel.x < 0) || (Pixel.y < 0) || (Pixel.x >= m_cursorblocksize.x) || (Pixel.y >= m_cursorblocksize.y)) + { + return 0.f; + } + return depths[static_cast(Pixel.y * m_cursorblocksize.x + Pixel.x)]; + }; + float pointdepth{depth_at(centre)}; + if (m_cursorbiased && (pointdepth > 0.f)) + { + // the depth pass of the scene draws everything a little farther than it lies (depth bias -1, slope -1), so the colour + // pass passes on the same surface. the slope of the depth grows towards the horizon, and the ground seen at a grazing + // angle came out much farther than it lies, or not at all. the slope is taken back: the pixels next to it on the same + // triangle are moved by the same bias, so their difference is the slope; of the two sides the smaller, which is less + // likely to lie on another surface. the larger slope of the two axes, the one implementations use for m + auto const slope = [&](glm::ivec2 const Step) { + auto result{-1.f}; + for (auto const side : {-1, 1}) + { + auto const other{depth_at(centre + Step * side)}; + if (other > 0.f) + { + auto const difference{std::abs(other - pointdepth)}; + result = (result < 0.f ? difference : std::min(result, difference)); + } + } + return std::max(result, 0.f); + }; + pointdepth += std::max(slope({1, 0}), slope({0, 1})) + std::ldexp(1.f, std::ilogb(pointdepth) - 23); + } auto const extent{ctx.scene.targets.front().extent}; m_mousehit = pointdepth > 0.0f; if (pointdepth > 0.0f) diff --git a/rendering/vulkan14/vulkan14renderer.h b/rendering/vulkan14/vulkan14renderer.h index 8e9f59a2..484f06c4 100644 --- a/rendering/vulkan14/vulkan14renderer.h +++ b/rendering/vulkan14/vulkan14renderer.h @@ -589,6 +589,11 @@ class vulkan14_renderer : public gfx_renderer, public gfx::scene_walk glm::ivec2 m_pickposition{0}; glm::vec3 m_worldmousecoordinates{0.f}; bool m_mousehit{false}; + // the depths read around the cursor of the editor (walk_editor_cursor()): where they lie in the image, and whether the depth pass of + // the scene drew them, with its bias + glm::ivec2 m_cursorblockcorner{0}; + glm::ivec2 m_cursorblocksize{1}; + bool m_cursorbiased{false}; // the state of the draws of the walk being recorded struct draw_state; std::unique_ptr m_draw;