graphics: refactor DX9 screenshot (part 2) (#861)
This commit is contained in:
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <external/robin_hood.h>
|
#include <external/robin_hood.h>
|
||||||
|
|
||||||
@@ -38,6 +40,33 @@ typedef HRESULT (WINAPI *D3DXSaveSurfaceToFileA_t)(
|
|||||||
|
|
||||||
static bool ATTEMPTED_D3DX9_LOAD_LIBRARY = false;
|
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(
|
static void save_capture(
|
||||||
int screen,
|
int screen,
|
||||||
D3DFORMAT format,
|
D3DFORMAT format,
|
||||||
@@ -226,34 +255,24 @@ void graphics_d3d9_poll_screenshot_hotkey() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void graphics_d3d9_process_screenshot_and_capture(
|
static std::optional<BackbufferCopy> acquire_backbuffer_copy(
|
||||||
IDirect3DDevice9 *device,
|
IDirect3DDevice9 *device, IDirect3DSwapChain9 *sub_swap_chain, int screen) {
|
||||||
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)))) {
|
|
||||||
HRESULT hr = S_OK;
|
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;
|
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);
|
hr = sub_swap_chain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &buffer);
|
||||||
} else {
|
} 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) {
|
if (FAILED(hr) || buffer == nullptr) {
|
||||||
log_warning("graphics::d3d9",
|
log_warning("graphics::d3d9",
|
||||||
"failed to get back buffer, hr={}",
|
"failed to get back buffer, hr={}",
|
||||||
FMT_HRESULT(hr));
|
FMT_HRESULT(hr));
|
||||||
if (capture) {
|
return std::nullopt;
|
||||||
graphics_capture_skip(capture_screen);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
D3DSURFACE_DESC desc {};
|
D3DSURFACE_DESC desc {};
|
||||||
@@ -263,10 +282,7 @@ void graphics_d3d9_process_screenshot_and_capture(
|
|||||||
"failed to acquire back buffer descriptor, hr={}",
|
"failed to acquire back buffer descriptor, hr={}",
|
||||||
FMT_HRESULT(hr));
|
FMT_HRESULT(hr));
|
||||||
buffer->Release();
|
buffer->Release();
|
||||||
if (capture) {
|
return std::nullopt;
|
||||||
graphics_capture_skip(capture_screen);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: cache render targets
|
// TODO: cache render targets
|
||||||
@@ -279,10 +295,7 @@ void graphics_d3d9_process_screenshot_and_capture(
|
|||||||
"failed to acquire temporary surface, hr={}",
|
"failed to acquire temporary surface, hr={}",
|
||||||
FMT_HRESULT(hr));
|
FMT_HRESULT(hr));
|
||||||
buffer->Release();
|
buffer->Release();
|
||||||
if (capture) {
|
return std::nullopt;
|
||||||
graphics_capture_skip(capture_screen);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = device->StretchRect(buffer, nullptr, temp_surface, nullptr, D3DTEXF_NONE);
|
hr = device->StretchRect(buffer, nullptr, temp_surface, nullptr, D3DTEXF_NONE);
|
||||||
@@ -292,42 +305,45 @@ void graphics_d3d9_process_screenshot_and_capture(
|
|||||||
FMT_HRESULT(hr));
|
FMT_HRESULT(hr));
|
||||||
temp_surface->Release();
|
temp_surface->Release();
|
||||||
buffer->Release();
|
buffer->Release();
|
||||||
if (capture) {
|
return std::nullopt;
|
||||||
graphics_capture_skip(capture_screen);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// release original back buffer reference
|
// release original back buffer reference
|
||||||
buffer->Release();
|
buffer->Release();
|
||||||
|
|
||||||
// function for storing the surface
|
return BackbufferCopy {
|
||||||
auto surface_process = [=]() {
|
.desc = desc,
|
||||||
|
.surface = SurfacePtr(temp_surface),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// capture
|
static void dispatch_surface_save(
|
||||||
if (capture) {
|
const ImageRequest &request,
|
||||||
save_capture(capture_screen, desc.Format, desc.Width, desc.Height, temp_surface);
|
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
|
case ImageRequestKind::Screenshot: {
|
||||||
if (screenshot) {
|
|
||||||
|
|
||||||
// check where we can save it
|
|
||||||
auto file_path = graphics_screenshot_genpath();
|
auto file_path = graphics_screenshot_genpath();
|
||||||
if (!file_path.empty()) {
|
if (!file_path.empty()) {
|
||||||
|
|
||||||
// write to file
|
|
||||||
save_screenshot(
|
save_screenshot(
|
||||||
file_path,
|
file_path,
|
||||||
desc.Format,
|
copy.desc.Format,
|
||||||
desc.Width,
|
copy.desc.Width,
|
||||||
desc.Height,
|
copy.desc.Height,
|
||||||
temp_surface);
|
copy.surface.get());
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// release surface
|
|
||||||
temp_surface->Release();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// list of games that crash when running the screenshot processor on another thread
|
// 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();
|
surface_process();
|
||||||
} else {
|
} else {
|
||||||
static auto pool = ThreadPool(2);
|
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