Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions src/Features/ScreenshotFeature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "Utils/FileSystem.h"
#include <DirectXTex.h>
#include <PCH.h>
#include <algorithm>
#include <cstring>
#include <filesystem>
#include <format>
Expand Down Expand Up @@ -66,6 +67,10 @@ namespace
if (FAILED(context->Map(stagingTexture, 0, D3D11_MAP_READ, 0, &mapped))) {
return false;
}
if (!mapped.pData || mapped.RowPitch == 0) {
context->Unmap(stagingTexture, 0);
return false;
}

const HRESULT initHr = image.Initialize2D(format, width, height, 1, 1);
if (FAILED(initHr)) {
Expand All @@ -79,12 +84,32 @@ namespace
return false;
}

// Driver-mapped region can be smaller than height * mapped.RowPitch
// (alignment quirks, partial mappings). Cap by mapped.DepthPitch and
// clamp each row's copy to whichever of source/dest pitches is smaller -
// stepping past either side hits unmapped memory and the worker crashes
// inside rep movsb (see crash 2026-05-19).
const size_t bytesPerRow = std::min<size_t>(destImage->rowPitch, mapped.RowPitch);
const size_t mappedDepth = mapped.DepthPitch != 0 ? mapped.DepthPitch :
mapped.RowPitch * destImage->height;
const size_t maxRowsBySize = mapped.RowPitch > 0 ? (mappedDepth / mapped.RowPitch) : 0;
const size_t rowsToCopy = std::min<size_t>(destImage->height, maxRowsBySize);
Comment thread
alandtse marked this conversation as resolved.
Comment thread
alandtse marked this conversation as resolved.

auto* destPixels = image.GetPixels();
for (size_t row = 0; row < destImage->height; ++row) {
const auto* srcPixels = static_cast<const uint8_t*>(mapped.pData);

// Initialize2D leaves the pixel buffer uninitialized. If the mapped
// region is short (rowsToCopy < height) or narrow (bytesPerRow <
// destImage->rowPitch), the gaps would otherwise read back as
// undefined memory and SaveToWICFile would encode garbage. Zero-fill
// up front so any uncopied bytes encode as deterministic black.
std::memset(destPixels, 0, image.GetPixelsSize());

for (size_t row = 0; row < rowsToCopy; ++row) {
memcpy(
destPixels + row * destImage->rowPitch,
static_cast<const uint8_t*>(mapped.pData) + row * mapped.RowPitch,
destImage->rowPitch);
srcPixels + row * mapped.RowPitch,
bytesPerRow);
Comment thread
alandtse marked this conversation as resolved.
}

context->Unmap(stagingTexture, 0);
Expand Down
Loading