17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-31 20:29:04 +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));
}
// 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<int>(targets.extent.width), static_cast<int>(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<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)});
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)});
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<int>(targets.extent.width), static_cast<int>(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<std::uint32_t>(blocksize.x), static_cast<std::uint32_t>(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 = &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;
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<float, 9> depths{};
auto const count{static_cast<std::size_t>(std::clamp(m_cursorblocksize.x * m_cursorblocksize.y, 0, static_cast<int>(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<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};
m_mousehit = 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::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<draw_state> m_draw;