17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-07 18:04:47 +01:00

fix(vulkan): read back the editor cursor depth and correct the depth pass bias

This commit is contained in:
2026-10-30 01:33:41 +01:00
parent 765153ad91
commit 7078edf138
2 changed files with 83 additions and 48 deletions

View File

@@ -2228,14 +2228,21 @@ void vulkan14_renderer::begin_colour_rendering(std::size_t const Index, bool con
remember_rendering(rendering, colour_target_uses(targets)); 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 // 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 begun again where it was // 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() void vulkan14_renderer::walk_editor_cursor()
{ {
auto &ctx{*m_context}; auto &ctx{*m_context};
auto &scene{ctx.scene}; auto &scene{ctx.scene};
auto &draw{*m_draw}; 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; return;
} }
@@ -2244,52 +2251,43 @@ void vulkan14_renderer::walk_editor_cursor()
glm::vec2 mousepos = Global.cursor_pos; glm::vec2 mousepos = Global.cursor_pos;
// cursor coordinates are flipped compared to opengl, whose rows the images keep // cursor coordinates are flipped compared to opengl, whose rows the images keep
mousepos.y = Global.window_size.y - mousepos.y; 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)))}; glm::ivec2 const extent{static_cast<int>(targets.extent.width), static_cast<int>(targets.extent.height)};
bufferpos = glm::clamp(bufferpos, glm::ivec2(0, 0), glm::ivec2(targets.extent.width - 1, targets.extent.height - 1)); 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<vk14::buffer>()}; auto readback{std::make_shared<vk14::buffer>()};
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)}); return;
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 = &region;
commands->copy_image_to_buffer2(&copy);
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)});
} }
// what the rendering stored before is loaded again suspend_rendering();
vk14::barriers(commands, colour_target_uses(targets)); 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)});
begin_colour_rendering(0, false); VkBufferImageCopy2 region{};
// the dynamic state starts anew with the rendering region.sType = VK_STRUCTURE_TYPE_BUFFER_IMAGE_COPY_2;
auto const set{draw.set}; region.imageSubresource = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, 0, 1};
auto const scene_offset{draw.scene_offset}; region.imageOffset = {blockcorner.x, blockcorner.y, 0};
auto const light_offset{draw.light_offset}; region.imageExtent = {static_cast<std::uint32_t>(blocksize.x), static_cast<std::uint32_t>(blocksize.y), 1};
auto const cluster_offset{draw.cluster_offset}; VkCopyImageToBufferInfo2 copy{};
auto const blending{draw.blending}; copy.sType = VK_STRUCTURE_TYPE_COPY_IMAGE_TO_BUFFER_INFO_2;
auto *program{draw.program}; copy.srcImage = targets.depth->handle();
auto const textures{draw.textures}; copy.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
start_draws(targets.formats, static_cast<int>(targets.extent.width), static_cast<int>(targets.extent.height), set, false); copy.dstBuffer = readback->handle();
draw.scene_offset = scene_offset; copy.regionCount = 1;
draw.light_offset = light_offset; copy.pRegions = &region;
draw.cluster_offset = cluster_offset; commands->copy_image_to_buffer2(&copy);
draw.blending = blending; host_read_barrier(commands);
draw.program = program; scene.cursor.data = readback;
draw.textures = textures; 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 // 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())) if (cursor.pending && (ctx.completed() >= cursor.value) && (false == ctx.scene.targets.empty()))
{ {
cursor.pending = false; cursor.pending = false;
float pointdepth{0.f}; std::array<float, 9> depths{};
cursor.data->invalidate(0, sizeof(float)); auto const count{static_cast<std::size_t>(std::clamp(m_cursorblocksize.x * m_cursorblocksize.y, 0, static_cast<int>(depths.size())))};
std::memcpy(&pointdepth, cursor.data->mapped(), sizeof(float)); cursor.data->invalidate(0, sizeof(float) * count);
std::memcpy(depths.data(), cursor.data->mapped(), sizeof(float) * count);
cursor.data.reset(); 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<std::size_t>(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}; auto const extent{ctx.scene.targets.front().extent};
m_mousehit = pointdepth > 0.0f; m_mousehit = pointdepth > 0.0f;
if (pointdepth > 0.0f) if (pointdepth > 0.0f)

View File

@@ -589,6 +589,11 @@ class vulkan14_renderer : public gfx_renderer, public gfx::scene_walk
glm::ivec2 m_pickposition{0}; glm::ivec2 m_pickposition{0};
glm::vec3 m_worldmousecoordinates{0.f}; glm::vec3 m_worldmousecoordinates{0.f};
bool m_mousehit{false}; 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 // the state of the draws of the walk being recorded
struct draw_state; struct draw_state;
std::unique_ptr<draw_state> m_draw; std::unique_ptr<draw_state> m_draw;