mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-03-16 13:20:52 +00:00
Dynamic capture format selection (IDXGIOutput5) (#654)
Co-authored-by: Conn O'Griofa <connogriofa@gmail.com>
This commit is contained in:
parent
03b62730ae
commit
0c6d0edacf
@ -31,6 +31,7 @@ using device_ctx_t = util::safe_ptr<ID3D11DeviceContext, Release<ID3D11
|
||||
using adapter_t = util::safe_ptr<IDXGIAdapter1, Release<IDXGIAdapter1>>;
|
||||
using output_t = util::safe_ptr<IDXGIOutput, Release<IDXGIOutput>>;
|
||||
using output1_t = util::safe_ptr<IDXGIOutput1, Release<IDXGIOutput1>>;
|
||||
using output5_t = util::safe_ptr<IDXGIOutput5, Release<IDXGIOutput5>>;
|
||||
using dup_t = util::safe_ptr<IDXGIOutputDuplication, Release<IDXGIOutputDuplication>>;
|
||||
using texture2d_t = util::safe_ptr<ID3D11Texture2D, Release<ID3D11Texture2D>>;
|
||||
using texture1d_t = util::safe_ptr<ID3D11Texture1D, Release<ID3D11Texture1D>>;
|
||||
@ -118,7 +119,7 @@ public:
|
||||
device_ctx_t device_ctx;
|
||||
duplication_t dup;
|
||||
|
||||
DXGI_FORMAT format;
|
||||
DXGI_FORMAT capture_format;
|
||||
D3D_FEATURE_LEVEL feature_level;
|
||||
|
||||
typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS {
|
||||
@ -134,8 +135,13 @@ public:
|
||||
|
||||
protected:
|
||||
int get_pixel_pitch() {
|
||||
return (format == DXGI_FORMAT_R16G16B16A16_FLOAT) ? 8 : 4;
|
||||
return (capture_format == DXGI_FORMAT_R16G16B16A16_FLOAT) ? 8 : 4;
|
||||
}
|
||||
|
||||
const char *dxgi_format_to_string(DXGI_FORMAT format);
|
||||
|
||||
virtual int complete_img(img_t *img, bool dummy) = 0;
|
||||
virtual std::vector<DXGI_FORMAT> get_supported_sdr_capture_formats() = 0;
|
||||
};
|
||||
|
||||
class display_ram_t : public display_base_t {
|
||||
@ -146,6 +152,8 @@ public:
|
||||
|
||||
std::shared_ptr<img_t> alloc_img() override;
|
||||
int dummy_img(img_t *img) override;
|
||||
int complete_img(img_t *img, bool dummy) override;
|
||||
std::vector<DXGI_FORMAT> get_supported_sdr_capture_formats() override;
|
||||
|
||||
int init(int framerate, const std::string &display_name);
|
||||
|
||||
@ -161,6 +169,8 @@ public:
|
||||
|
||||
std::shared_ptr<img_t> alloc_img() override;
|
||||
int dummy_img(img_t *img_base) override;
|
||||
int complete_img(img_t *img_base, bool dummy) override;
|
||||
std::vector<DXGI_FORMAT> get_supported_sdr_capture_formats() override;
|
||||
|
||||
int init(int framerate, const std::string &display_name);
|
||||
|
||||
@ -174,7 +184,6 @@ public:
|
||||
ps_t scene_ps;
|
||||
vs_t scene_vs;
|
||||
|
||||
texture2d_t src;
|
||||
gpu_cursor_t cursor;
|
||||
};
|
||||
} // namespace platf::dxgi
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <cmath>
|
||||
#include <codecvt>
|
||||
#include <initguid.h>
|
||||
|
||||
#include "display.h"
|
||||
#include "misc.h"
|
||||
@ -79,22 +80,21 @@ duplication_t::~duplication_t() {
|
||||
}
|
||||
|
||||
int display_base_t::init(int framerate, const std::string &display_name) {
|
||||
/* Uncomment when use of IDXGIOutput5 is implemented
|
||||
std::once_flag windows_cpp_once_flag;
|
||||
|
||||
std::call_once(windows_cpp_once_flag, []() {
|
||||
DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
|
||||
const auto DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = ((DPI_AWARENESS_CONTEXT)-4);
|
||||
|
||||
typedef BOOL (*User32_SetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT value);
|
||||
|
||||
auto user32 = LoadLibraryA("user32.dll");
|
||||
auto f = (User32_SetProcessDpiAwarenessContext)GetProcAddress(user32, "SetProcessDpiAwarenessContext");
|
||||
auto f = (User32_SetProcessDpiAwarenessContext)GetProcAddress(user32, "SetProcessDpiAwarenessContext");
|
||||
if(f) {
|
||||
f(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
}
|
||||
|
||||
FreeLibrary(user32);
|
||||
});
|
||||
*/
|
||||
|
||||
// Ensure we can duplicate the current display
|
||||
syncThreadDesktop();
|
||||
@ -291,27 +291,57 @@ int display_base_t::init(int framerate, const std::string &display_name) {
|
||||
}
|
||||
|
||||
//FIXME: Duplicate output on RX580 in combination with DOOM (2016) --> BSOD
|
||||
//TODO: Use IDXGIOutput5 for improved performance
|
||||
{
|
||||
dxgi::output1_t output1 {};
|
||||
dxgi::output5_t output5 {};
|
||||
|
||||
// IDXGIOutput5 is optional, but can provide improved performance and wide color support
|
||||
status = output->QueryInterface(IID_IDXGIOutput5, (void **)&output5);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to query IDXGIOutput5 from the output"sv;
|
||||
}
|
||||
|
||||
status = output->QueryInterface(IID_IDXGIOutput1, (void **)&output1);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We try this twice, in case we still get an error on reinitialization
|
||||
for(int x = 0; x < 2; ++x) {
|
||||
status = output1->DuplicateOutput((IUnknown *)device.get(), &dup.dup);
|
||||
if(SUCCEEDED(status)) {
|
||||
break;
|
||||
if(output5) {
|
||||
// Ask the display implementation which formats it supports
|
||||
auto supported_formats = get_supported_sdr_capture_formats();
|
||||
if(supported_formats.empty()) {
|
||||
BOOST_LOG(warning) << "No compatible capture formats for this encoder"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We try this twice, in case we still get an error on reinitialization
|
||||
for(int x = 0; x < 2; ++x) {
|
||||
status = output5->DuplicateOutput1((IUnknown *)device.get(), 0, supported_formats.size(), supported_formats.data(), &dup.dup);
|
||||
if(SUCCEEDED(status)) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(200ms);
|
||||
}
|
||||
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(warning) << "DuplicateOutput1 Failed [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
}
|
||||
std::this_thread::sleep_for(200ms);
|
||||
}
|
||||
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "DuplicateOutput Failed [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
if(!output5 || FAILED(status)) {
|
||||
for(int x = 0; x < 2; ++x) {
|
||||
status = output1->DuplicateOutput((IUnknown *)device.get(), &dup.dup);
|
||||
if(SUCCEEDED(status)) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(200ms);
|
||||
}
|
||||
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "DuplicateOutput Failed [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,16 +349,10 @@ int display_base_t::init(int framerate, const std::string &display_name) {
|
||||
dup.dup->GetDesc(&dup_desc);
|
||||
|
||||
BOOST_LOG(info) << "Desktop resolution ["sv << dup_desc.ModeDesc.Width << 'x' << dup_desc.ModeDesc.Height << ']';
|
||||
BOOST_LOG(info) << "Desktop format ["sv << format_str[dup_desc.ModeDesc.Format] << ']';
|
||||
BOOST_LOG(info) << "Desktop format ["sv << dxgi_format_to_string(dup_desc.ModeDesc.Format) << ']';
|
||||
|
||||
// For IDXGIOutput1::DuplicateOutput(), the format of the desktop image we receive from AcquireNextFrame() is
|
||||
// converted to DXGI_FORMAT_B8G8R8A8_UNORM, even if the current mode (as returned in dup_desc) differs.
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/desktop-dup-api for details.
|
||||
//
|
||||
// TODO: When we implement IDXGIOutput5, we will need to actually call AcquireNextFrame(), then call GetDesc()
|
||||
// on the the texture we receive to determine which format in our list that it has decided to use.
|
||||
format = DXGI_FORMAT_B8G8R8A8_UNORM;
|
||||
BOOST_LOG(info) << "Capture format ["sv << format_str[format] << ']';
|
||||
// Capture format will be determined from the first call to AcquireNextFrame()
|
||||
capture_format = DXGI_FORMAT_UNKNOWN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -458,6 +482,10 @@ const char *format_str[] = {
|
||||
"DXGI_FORMAT_V408"
|
||||
};
|
||||
|
||||
const char *display_base_t::dxgi_format_to_string(DXGI_FORMAT format) {
|
||||
return format_str[format];
|
||||
}
|
||||
|
||||
} // namespace platf::dxgi
|
||||
|
||||
namespace platf {
|
||||
|
@ -210,6 +210,14 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
||||
return capture_status;
|
||||
}
|
||||
|
||||
const bool mouse_update_flag = frame_info.LastMouseUpdateTime.QuadPart != 0 || frame_info.PointerShapeBufferSize > 0;
|
||||
const bool frame_update_flag = frame_info.AccumulatedFrames != 0 || frame_info.LastPresentTime.QuadPart != 0;
|
||||
const bool update_flag = mouse_update_flag || frame_update_flag;
|
||||
|
||||
if(!update_flag) {
|
||||
return capture_e::timeout;
|
||||
}
|
||||
|
||||
if(frame_info.PointerShapeBufferSize > 0) {
|
||||
auto &img_data = cursor.img_data;
|
||||
|
||||
@ -230,8 +238,7 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
||||
cursor.visible = frame_info.PointerPosition.Visible;
|
||||
}
|
||||
|
||||
// If frame has been updated
|
||||
if(frame_info.LastPresentTime.QuadPart != 0) {
|
||||
if(frame_update_flag) {
|
||||
{
|
||||
texture2d_t src {};
|
||||
status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src);
|
||||
@ -241,35 +248,82 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
src->GetDesc(&desc);
|
||||
|
||||
// If we don't know the capture format yet, grab it from this texture and create the staging texture
|
||||
if(capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
capture_format = desc.Format;
|
||||
BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']';
|
||||
|
||||
D3D11_TEXTURE2D_DESC t {};
|
||||
t.Width = width;
|
||||
t.Height = height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.SampleDesc.Count = 1;
|
||||
t.Usage = D3D11_USAGE_STAGING;
|
||||
t.Format = capture_format;
|
||||
t.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
|
||||
auto status = device->CreateTexture2D(&t, nullptr, &texture);
|
||||
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create staging texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
|
||||
// It's possible for our display enumeration to race with mode changes and result in
|
||||
// mismatched image pool and desktop texture sizes. If this happens, just reinit again.
|
||||
if(desc.Width != width || desc.Height != height) {
|
||||
BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
// It's also possible for the capture format to change on the fly. If that happens,
|
||||
// reinitialize capture to try format detection again and create new images.
|
||||
if(capture_format != desc.Format) {
|
||||
BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
//Copy from GPU to CPU
|
||||
device_ctx->CopyResource(texture.get(), src.get());
|
||||
}
|
||||
}
|
||||
|
||||
if(img_info.pData) {
|
||||
device_ctx->Unmap(texture.get(), 0);
|
||||
img_info.pData = nullptr;
|
||||
// If we don't know the final capture format yet, encode a dummy image
|
||||
if(capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
BOOST_LOG(debug) << "Capture format is still unknown. Encoding a blank image"sv;
|
||||
|
||||
if(dummy_img(img)) {
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// Map the staging texture for CPU access (making it inaccessible for the GPU)
|
||||
status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
// Now that we know the capture format, we can finish creating the image
|
||||
if(complete_img(img, false)) {
|
||||
device_ctx->Unmap(texture.get(), 0);
|
||||
img_info.pData = nullptr;
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
std::copy_n((std::uint8_t *)img_info.pData, height * img_info.RowPitch, (std::uint8_t *)img->data);
|
||||
|
||||
// Unmap the staging texture to allow GPU access again
|
||||
device_ctx->Unmap(texture.get(), 0);
|
||||
img_info.pData = nullptr;
|
||||
}
|
||||
|
||||
const bool mouse_update =
|
||||
(frame_info.LastMouseUpdateTime.QuadPart || frame_info.PointerShapeBufferSize > 0) &&
|
||||
(cursor_visible && cursor.visible);
|
||||
|
||||
const bool update_flag = frame_info.LastPresentTime.QuadPart != 0 || mouse_update;
|
||||
|
||||
if(!update_flag) {
|
||||
return capture_e::timeout;
|
||||
}
|
||||
|
||||
std::copy_n((std::uint8_t *)img_info.pData, height * img_info.RowPitch, (std::uint8_t *)img->data);
|
||||
|
||||
if(cursor_visible && cursor.visible) {
|
||||
blend_cursor(cursor, *img);
|
||||
}
|
||||
@ -280,48 +334,59 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise
|
||||
std::shared_ptr<platf::img_t> display_ram_t::alloc_img() {
|
||||
auto img = std::make_shared<img_t>();
|
||||
|
||||
img->pixel_pitch = get_pixel_pitch();
|
||||
img->row_pitch = img_info.RowPitch;
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
img->data = new std::uint8_t[img->row_pitch * height];
|
||||
// Initialize fields that are format-independent
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
int display_ram_t::dummy_img(platf::img_t *img) {
|
||||
int display_ram_t::complete_img(platf::img_t *img, bool dummy) {
|
||||
// If this is not a dummy image, we must know the format by now
|
||||
if(!dummy && capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
BOOST_LOG(error) << "display_ram_t::complete_img() called with unknown capture format!";
|
||||
return -1;
|
||||
}
|
||||
|
||||
img->pixel_pitch = get_pixel_pitch();
|
||||
|
||||
if(dummy && !img->row_pitch) {
|
||||
// Assume our dummy image will have no padding
|
||||
img->row_pitch = img->pixel_pitch * img->width;
|
||||
}
|
||||
|
||||
// Reallocate the image buffer if the pitch changes
|
||||
if(!dummy && img->row_pitch != img_info.RowPitch) {
|
||||
img->row_pitch = img_info.RowPitch;
|
||||
delete img->data;
|
||||
img->data = nullptr;
|
||||
}
|
||||
|
||||
if(!img->data) {
|
||||
img->data = new std::uint8_t[img->row_pitch * height];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int display_ram_t::dummy_img(platf::img_t *img) {
|
||||
if(complete_img(img, true)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::fill_n((std::uint8_t *)img->data, height * img->row_pitch, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<DXGI_FORMAT> display_ram_t::get_supported_sdr_capture_formats() {
|
||||
return std::vector { DXGI_FORMAT_B8G8R8A8_UNORM };
|
||||
}
|
||||
|
||||
int display_ram_t::init(int framerate, const std::string &display_name) {
|
||||
if(display_base_t::init(framerate, display_name)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC t {};
|
||||
t.Width = width;
|
||||
t.Height = height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.SampleDesc.Count = 1;
|
||||
t.Usage = D3D11_USAGE_STAGING;
|
||||
t.Format = format;
|
||||
t.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
|
||||
auto status = device->CreateTexture2D(&t, nullptr, &texture);
|
||||
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
// map the texture simply to get the pitch and stride
|
||||
status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to map the texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace platf::dxgi
|
||||
|
@ -92,6 +92,7 @@ struct img_d3d_t : public platf::img_t {
|
||||
render_target_t scene_rt;
|
||||
|
||||
texture2d_t texture;
|
||||
bool dummy = false;
|
||||
|
||||
~img_d3d_t() override = default;
|
||||
};
|
||||
@ -215,25 +216,14 @@ blob_t compile_vertex_shader(LPCSTR file) {
|
||||
return compile_shader(file, "main_vs", "vs_5_0");
|
||||
}
|
||||
|
||||
int init_rt(device_t::pointer device, shader_res_t &shader_res, render_target_t &render_target, int width, int height, DXGI_FORMAT format, texture2d_t::pointer tex) {
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC shader_resource_desc {
|
||||
format,
|
||||
D3D11_SRV_DIMENSION_TEXTURE2D
|
||||
};
|
||||
shader_resource_desc.Texture2D.MipLevels = 1;
|
||||
|
||||
auto status = device->CreateShaderResourceView(tex, &shader_resource_desc, &shader_res);
|
||||
int init_rt(device_t::pointer device, shader_res_t &shader_res, render_target_t &render_target, int width, int height, texture2d_t::pointer tex) {
|
||||
auto status = device->CreateShaderResourceView(tex, nullptr, &shader_res);
|
||||
if(status) {
|
||||
BOOST_LOG(error) << "Failed to create render target texture for luma [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
BOOST_LOG(error) << "Failed to create shader resource view for luma [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
D3D11_RENDER_TARGET_VIEW_DESC render_target_desc {
|
||||
format,
|
||||
D3D11_RTV_DIMENSION_TEXTURE2D
|
||||
};
|
||||
|
||||
status = device->CreateRenderTargetView(tex, &render_target_desc, &render_target);
|
||||
status = device->CreateRenderTargetView(tex, nullptr, &render_target);
|
||||
if(status) {
|
||||
BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
@ -242,28 +232,6 @@ int init_rt(device_t::pointer device, shader_res_t &shader_res, render_target_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_rt(device_t::pointer device, shader_res_t &shader_res, render_target_t &render_target, int width, int height, DXGI_FORMAT format) {
|
||||
D3D11_TEXTURE2D_DESC desc {};
|
||||
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.Format = format;
|
||||
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.SampleDesc.Count = 1;
|
||||
|
||||
texture2d_t tex;
|
||||
auto status = device->CreateTexture2D(&desc, nullptr, &tex);
|
||||
if(status) {
|
||||
BOOST_LOG(error) << "Failed to create render target texture for luma [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
return init_rt(device, shader_res, render_target, width, height, format, tex.get());
|
||||
}
|
||||
|
||||
class hwdevice_t : public platf::hwdevice_t {
|
||||
public:
|
||||
int convert(platf::img_t &img_base) override {
|
||||
@ -275,7 +243,7 @@ public:
|
||||
device_ctx_p->OMSetRenderTargets(1, &nv12_Y_rt, nullptr);
|
||||
device_ctx_p->VSSetShader(scene_vs.get(), nullptr, 0);
|
||||
device_ctx_p->PSSetShader(convert_Y_ps.get(), nullptr, 0);
|
||||
device_ctx_p->PSSetShaderResources(0, 1, &back_img.input_res);
|
||||
device_ctx_p->PSSetShaderResources(0, 1, &((img_d3d_t *)back_img.get())->input_res);
|
||||
device_ctx_p->Draw(3, 0);
|
||||
|
||||
device_ctx_p->RSSetViewports(1, &outY_view);
|
||||
@ -290,7 +258,7 @@ public:
|
||||
device_ctx_p->OMSetRenderTargets(1, &nv12_UV_rt, nullptr);
|
||||
device_ctx_p->VSSetShader(convert_UV_vs.get(), nullptr, 0);
|
||||
device_ctx_p->PSSetShader(convert_UV_ps.get(), nullptr, 0);
|
||||
device_ctx_p->PSSetShaderResources(0, 1, &back_img.input_res);
|
||||
device_ctx_p->PSSetShaderResources(0, 1, &((img_d3d_t *)back_img.get())->input_res);
|
||||
device_ctx_p->Draw(3, 0);
|
||||
|
||||
device_ctx_p->RSSetViewports(1, &outUV_view);
|
||||
@ -386,7 +354,7 @@ public:
|
||||
}
|
||||
|
||||
D3D11_RENDER_TARGET_VIEW_DESC nv12_rt_desc {
|
||||
DXGI_FORMAT_R8_UNORM,
|
||||
format == DXGI_FORMAT_P010 ? DXGI_FORMAT_R16_UNORM : DXGI_FORMAT_R8_UNORM,
|
||||
D3D11_RTV_DIMENSION_TEXTURE2D
|
||||
};
|
||||
|
||||
@ -396,7 +364,7 @@ public:
|
||||
return -1;
|
||||
}
|
||||
|
||||
nv12_rt_desc.Format = DXGI_FORMAT_R8G8_UNORM;
|
||||
nv12_rt_desc.Format = (format == DXGI_FORMAT_P010) ? DXGI_FORMAT_R16G16_UNORM : DXGI_FORMAT_R8G8_UNORM;
|
||||
|
||||
status = device_p->CreateRenderTargetView(img.texture.get(), &nv12_rt_desc, &nv12_UV_rt);
|
||||
if(FAILED(status)) {
|
||||
@ -485,23 +453,12 @@ public:
|
||||
|
||||
// Color the background black, so that the padding for keeping the aspect ratio
|
||||
// is black
|
||||
if(img.display->dummy_img(&back_img)) {
|
||||
back_img = img.display->alloc_img();
|
||||
if(img.display->dummy_img(back_img.get())) {
|
||||
BOOST_LOG(warning) << "Couldn't create an image to set background color to black"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC desc {
|
||||
DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
D3D11_SRV_DIMENSION_TEXTURE2D
|
||||
};
|
||||
desc.Texture2D.MipLevels = 1;
|
||||
|
||||
status = device_p->CreateShaderResourceView(back_img.texture.get(), &desc, &back_img.input_res);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create input shader resource view [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
device_ctx_p->IASetInputLayout(input_layout.get());
|
||||
device_ctx_p->PSSetConstantBuffers(0, 1, &color_matrix);
|
||||
device_ctx_p->VSSetConstantBuffers(0, 1, &info_scene);
|
||||
@ -548,7 +505,7 @@ public:
|
||||
img_d3d_t img;
|
||||
|
||||
// Clear nv12 render target to black
|
||||
img_d3d_t back_img;
|
||||
std::shared_ptr<img_t> back_img;
|
||||
|
||||
vs_t convert_UV_vs;
|
||||
ps_t convert_UV_ps;
|
||||
@ -656,15 +613,9 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC desc {
|
||||
DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
D3D11_SRV_DIMENSION_TEXTURE2D
|
||||
};
|
||||
desc.Texture2D.MipLevels = 1;
|
||||
|
||||
// Free resources before allocating on the next line.
|
||||
cursor.input_res.reset();
|
||||
status = device->CreateShaderResourceView(texture.get(), &desc, &cursor.input_res);
|
||||
status = device->CreateShaderResourceView(texture.get(), nullptr, &cursor.input_res);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create cursor shader resource view [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
@ -674,21 +625,60 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec
|
||||
}
|
||||
|
||||
if(frame_info.LastMouseUpdateTime.QuadPart) {
|
||||
cursor.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, frame_info.PointerPosition.Visible && cursor_visible);
|
||||
cursor.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, frame_info.PointerPosition.Visible);
|
||||
}
|
||||
|
||||
if(frame_update_flag) {
|
||||
src.reset();
|
||||
status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src);
|
||||
if(capture_format != DXGI_FORMAT_UNKNOWN || frame_update_flag) {
|
||||
texture2d_t src {};
|
||||
|
||||
// Get the texture object from this frame
|
||||
status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
src->GetDesc(&desc);
|
||||
|
||||
// If we don't know the capture format yet, grab it from this texture
|
||||
if(capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
capture_format = desc.Format;
|
||||
BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']';
|
||||
}
|
||||
|
||||
// It's possible for our display enumeration to race with mode changes and result in
|
||||
// mismatched image pool and desktop texture sizes. If this happens, just reinit again.
|
||||
if(desc.Width != width || desc.Height != height) {
|
||||
BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
// It's also possible for the capture format to change on the fly. If that happens,
|
||||
// reinitialize capture to try format detection again and create new images.
|
||||
if(capture_format != desc.Format) {
|
||||
BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
// Now that we know the capture format, we can finish creating the image
|
||||
if(complete_img(img, false)) {
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
// Copy the texture into this image
|
||||
device_ctx->CopyResource(img->texture.get(), src.get());
|
||||
}
|
||||
else {
|
||||
// We don't know the final capture format yet, so we will encode a dummy image
|
||||
BOOST_LOG(debug) << "Capture format is still unknown. Encoding a blank image"sv;
|
||||
|
||||
if(dummy_img(img)) {
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
|
||||
device_ctx->CopyResource(img->texture.get(), src.get());
|
||||
if(cursor.visible) {
|
||||
if(cursor.visible && cursor_visible) {
|
||||
D3D11_VIEWPORT view {
|
||||
0.0f, 0.0f,
|
||||
(float)width, (float)height,
|
||||
@ -698,8 +688,8 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec
|
||||
device_ctx->VSSetShader(scene_vs.get(), nullptr, 0);
|
||||
device_ctx->PSSetShader(scene_ps.get(), nullptr, 0);
|
||||
device_ctx->RSSetViewports(1, &view);
|
||||
device_ctx->OMSetRenderTargets(1, &img->scene_rt, nullptr);
|
||||
device_ctx->PSSetShaderResources(0, 1, &cursor.input_res);
|
||||
device_ctx->OMSetRenderTargets(1, &img->scene_rt, nullptr);
|
||||
device_ctx->OMSetBlendState(blend_enable.get(), nullptr, 0xFFFFFFFFu);
|
||||
device_ctx->RSSetViewports(1, &cursor.cursor_view);
|
||||
device_ctx->Draw(3, 0);
|
||||
@ -758,85 +748,84 @@ int display_vram_t::init(int framerate, const std::string &display_name) {
|
||||
std::shared_ptr<platf::img_t> display_vram_t::alloc_img() {
|
||||
auto img = std::make_shared<img_d3d_t>();
|
||||
|
||||
img->pixel_pitch = get_pixel_pitch();
|
||||
img->row_pitch = img->pixel_pitch * width;
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
img->display = shared_from_this();
|
||||
// Initialize format-independent fields
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
img->display = shared_from_this();
|
||||
|
||||
auto dummy_data = std::make_unique<std::uint8_t[]>(img->row_pitch * height);
|
||||
D3D11_SUBRESOURCE_DATA data {
|
||||
dummy_data.get(),
|
||||
(UINT)img->row_pitch
|
||||
};
|
||||
std::fill_n(dummy_data.get(), img->row_pitch * height, 0);
|
||||
return img;
|
||||
}
|
||||
|
||||
int display_vram_t::complete_img(platf::img_t *img_base, bool dummy) {
|
||||
auto img = (img_d3d_t *)img_base;
|
||||
|
||||
// If this already has a texture and it's not switching dummy state, nothing to do
|
||||
if(img->texture && img->dummy == dummy) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If this is not a dummy image, we must know the format by now
|
||||
if(!dummy && capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
BOOST_LOG(error) << "display_vram_t::complete_img() called with unknown capture format!";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Reset the image (in case this was previously a dummy)
|
||||
img->texture.reset();
|
||||
img->input_res.reset();
|
||||
img->scene_rt.reset();
|
||||
img->data = nullptr;
|
||||
|
||||
// Initialize format-dependent fields
|
||||
img->pixel_pitch = get_pixel_pitch();
|
||||
img->row_pitch = img->pixel_pitch * img->width;
|
||||
img->dummy = dummy;
|
||||
|
||||
D3D11_TEXTURE2D_DESC t {};
|
||||
t.Width = width;
|
||||
t.Height = height;
|
||||
t.Width = img->width;
|
||||
t.Height = img->height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.SampleDesc.Count = 1;
|
||||
t.Usage = D3D11_USAGE_DEFAULT;
|
||||
t.Format = format;
|
||||
t.Format = (capture_format == DXGI_FORMAT_UNKNOWN) ? DXGI_FORMAT_B8G8R8A8_UNORM : capture_format;
|
||||
t.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
|
||||
|
||||
auto status = device->CreateTexture2D(&t, &data, &img->texture);
|
||||
auto status = device->CreateTexture2D(&t, nullptr, &img->texture);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create img buf texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return nullptr;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(init_rt(device.get(), img->input_res, img->scene_rt, width, height, format, img->texture.get())) {
|
||||
return nullptr;
|
||||
if(init_rt(device.get(), img->input_res, img->scene_rt, img->width, img->height, img->texture.get())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
img->data = (std::uint8_t *)img->texture.get();
|
||||
|
||||
return img;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int display_vram_t::dummy_img(platf::img_t *img_base) {
|
||||
auto img = (img_d3d_t *)img_base;
|
||||
|
||||
if(img->texture) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
img->pixel_pitch = get_pixel_pitch();
|
||||
img->row_pitch = img->pixel_pitch * width;
|
||||
auto dummy_data = std::make_unique<uint8_t[]>(img->row_pitch * height);
|
||||
D3D11_SUBRESOURCE_DATA data {
|
||||
dummy_data.get(),
|
||||
(UINT)img->row_pitch
|
||||
};
|
||||
std::fill_n(dummy_data.get(), img->row_pitch * height, 0);
|
||||
|
||||
D3D11_TEXTURE2D_DESC t {};
|
||||
t.Width = width;
|
||||
t.Height = height;
|
||||
t.MipLevels = 1;
|
||||
t.ArraySize = 1;
|
||||
t.SampleDesc.Count = 1;
|
||||
t.Usage = D3D11_USAGE_DEFAULT;
|
||||
t.Format = format;
|
||||
t.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
|
||||
dxgi::texture2d_t tex;
|
||||
auto status = device->CreateTexture2D(&t, &data, &tex);
|
||||
if(FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create dummy texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
if(complete_img(img, true)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
img->texture = std::move(tex);
|
||||
img->data = (std::uint8_t *)img->texture.get();
|
||||
auto dummy_data = std::make_unique<std::uint8_t[]>(img->row_pitch * img->height);
|
||||
std::fill_n(dummy_data.get(), img->row_pitch * img->height, 0);
|
||||
|
||||
device_ctx->UpdateSubresource(img->texture.get(), 0, nullptr, dummy_data.get(), img->row_pitch, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<DXGI_FORMAT> display_vram_t::get_supported_sdr_capture_formats() {
|
||||
return std::vector { DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM };
|
||||
}
|
||||
|
||||
std::shared_ptr<platf::hwdevice_t> display_vram_t::make_hwdevice(pix_fmt_e pix_fmt) {
|
||||
if(pix_fmt != platf::pix_fmt_e::nv12) {
|
||||
if(pix_fmt != platf::pix_fmt_e::nv12 && pix_fmt != platf::pix_fmt_e::p010) {
|
||||
BOOST_LOG(error) << "display_vram_t doesn't support pixel format ["sv << from_pix_fmt(pix_fmt) << ']';
|
||||
|
||||
return nullptr;
|
||||
|
Loading…
x
Reference in New Issue
Block a user