graphics: refactor DX9 screenshot (part 2) (#861)

This commit is contained in:
bicarus
2026-08-11 01:11:42 -07:00
committed by GitHub
parent 13a171f199
commit ce2f93d234
@@ -2,7 +2,9 @@
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <external/robin_hood.h>
@@ -38,6 +40,33 @@ typedef HRESULT (WINAPI *D3DXSaveSurfaceToFileA_t)(
static bool ATTEMPTED_D3DX9_LOAD_LIBRARY = false;
namespace {
enum class ImageRequestKind {
Screenshot,
Capture,
};
struct ImageRequest {
ImageRequestKind kind;
int screen;
};
struct SurfaceReleaser {
void operator()(IDirect3DSurface9 *surface) const {
surface->Release();
}
};
using SurfacePtr = std::unique_ptr<IDirect3DSurface9, SurfaceReleaser>;
struct BackbufferCopy {
D3DSURFACE_DESC desc {};
SurfacePtr surface;
};
} // namespace
static void save_capture(
int screen,
D3DFORMAT format,
@@ -226,34 +255,24 @@ void graphics_d3d9_poll_screenshot_hotkey() {
}
}
void graphics_d3d9_process_screenshot_and_capture(
IDirect3DDevice9 *device,
IDirect3DSwapChain9 *sub_swap_chain) {
// process pending screenshot
bool screenshot = false;
bool capture = false;
int capture_screen = 0;
if ((screenshot = graphics_screenshot_consume())
|| ((capture = graphics_capture_consume(&capture_screen)))) {
static std::optional<BackbufferCopy> acquire_backbuffer_copy(
IDirect3DDevice9 *device, IDirect3DSwapChain9 *sub_swap_chain, int screen) {
HRESULT hr = S_OK;
// TODO: verify capture_screen is a valid swapchain
// TODO: verify screen is a valid swapchain
// get back buffer
IDirect3DSurface9 *buffer = nullptr;
if (sub_swap_chain != nullptr && capture_screen & 1) {
if (sub_swap_chain != nullptr && screen & 1) {
hr = sub_swap_chain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &buffer);
} else {
hr = device->GetBackBuffer(capture_screen, 0, D3DBACKBUFFER_TYPE_MONO, &buffer);
hr = device->GetBackBuffer(screen, 0, D3DBACKBUFFER_TYPE_MONO, &buffer);
}
if (FAILED(hr) || buffer == nullptr) {
log_warning("graphics::d3d9",
"failed to get back buffer, hr={}",
FMT_HRESULT(hr));
if (capture) {
graphics_capture_skip(capture_screen);
}
return;
return std::nullopt;
}
D3DSURFACE_DESC desc {};
@@ -263,10 +282,7 @@ void graphics_d3d9_process_screenshot_and_capture(
"failed to acquire back buffer descriptor, hr={}",
FMT_HRESULT(hr));
buffer->Release();
if (capture) {
graphics_capture_skip(capture_screen);
}
return;
return std::nullopt;
}
// TODO: cache render targets
@@ -279,10 +295,7 @@ void graphics_d3d9_process_screenshot_and_capture(
"failed to acquire temporary surface, hr={}",
FMT_HRESULT(hr));
buffer->Release();
if (capture) {
graphics_capture_skip(capture_screen);
}
return;
return std::nullopt;
}
hr = device->StretchRect(buffer, nullptr, temp_surface, nullptr, D3DTEXF_NONE);
@@ -292,42 +305,45 @@ void graphics_d3d9_process_screenshot_and_capture(
FMT_HRESULT(hr));
temp_surface->Release();
buffer->Release();
if (capture) {
graphics_capture_skip(capture_screen);
}
return;
return std::nullopt;
}
// release original back buffer reference
buffer->Release();
// function for storing the surface
auto surface_process = [=]() {
return BackbufferCopy {
.desc = desc,
.surface = SurfacePtr(temp_surface),
};
}
// capture
if (capture) {
save_capture(capture_screen, desc.Format, desc.Width, desc.Height, temp_surface);
}
static void dispatch_surface_save(
const ImageRequest &request,
BackbufferCopy copy) {
auto surface_process = [request, copy = std::move(copy)]() {
switch (request.kind) {
case ImageRequestKind::Capture:
save_capture(
request.screen,
copy.desc.Format,
copy.desc.Width,
copy.desc.Height,
copy.surface.get());
break;
// screenshot
if (screenshot) {
// check where we can save it
case ImageRequestKind::Screenshot: {
auto file_path = graphics_screenshot_genpath();
if (!file_path.empty()) {
// write to file
save_screenshot(
file_path,
desc.Format,
desc.Width,
desc.Height,
temp_surface);
copy.desc.Format,
copy.desc.Width,
copy.desc.Height,
copy.surface.get());
}
break;
}
}
// release surface
temp_surface->Release();
};
// list of games that crash when running the screenshot processor on another thread
@@ -347,7 +363,47 @@ void graphics_d3d9_process_screenshot_and_capture(
surface_process();
} else {
static auto pool = ThreadPool(2);
pool.add(surface_process);
}
pool.add(std::move(surface_process));
}
}
static std::optional<ImageRequest> consume_image_request() {
if (graphics_screenshot_consume()) {
return ImageRequest {
.kind = ImageRequestKind::Screenshot,
.screen = 0,
};
}
int capture_screen = 0;
if (graphics_capture_consume(&capture_screen)) {
return ImageRequest {
.kind = ImageRequestKind::Capture,
.screen = capture_screen,
};
}
return std::nullopt;
}
void graphics_d3d9_process_screenshot_and_capture(
IDirect3DDevice9 *device,
IDirect3DSwapChain9 *sub_swap_chain) {
const auto request = consume_image_request();
if (!request.has_value()) {
return;
}
auto copy = acquire_backbuffer_copy(
device,
sub_swap_chain,
request->screen);
if (!copy.has_value()) {
if (request->kind == ImageRequestKind::Capture) {
graphics_capture_skip(request->screen);
}
return;
}
dispatch_surface_save(*request, std::move(*copy));
}