graphics: refactor DX9 screenshot (part 2) (#861)
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user