[host] dxgi: implement downsampling to arbitrary sizes

This commit is contained in:
Geoffrey McRae 2023-11-07 12:58:54 +11:00
parent 30c577beeb
commit 5f613b09d6
4 changed files with 328 additions and 36 deletions

View File

@ -9,6 +9,7 @@ add_library(capture_DXGI STATIC
src/util.c src/util.c
src/com_ref.c src/com_ref.c
src/pp/downsample.c
src/pp/sdrwhitelevel.c src/pp/sdrwhitelevel.c
src/pp/rgb24.c src/pp/rgb24.c
) )

View File

@ -20,7 +20,6 @@
#include "interface/capture.h" #include "interface/capture.h"
#include "interface/platform.h" #include "interface/platform.h"
#include "downsample_parser.h"
#include "common/array.h" #include "common/array.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/windebug.h" #include "common/windebug.h"
@ -32,6 +31,7 @@
#include "common/KVMFR.h" #include "common/KVMFR.h"
#include "common/vector.h" #include "common/vector.h"
#include <math.h>
#include <stdatomic.h> #include <stdatomic.h>
#include <unistd.h> #include <unistd.h>
#include <dxgi.h> #include <dxgi.h>
@ -51,9 +51,17 @@
#define LOCKED(...) INTERLOCKED_SECTION(this->deviceContextLock, __VA_ARGS__) #define LOCKED(...) INTERLOCKED_SECTION(this->deviceContextLock, __VA_ARGS__)
//post processers //post processers
extern const DXGIPostProcess DXGIPP_Downsample;
extern const DXGIPostProcess DXGIPP_SDRWhiteLevel; extern const DXGIPostProcess DXGIPP_SDRWhiteLevel;
extern const DXGIPostProcess DXGIPP_RGB24; extern const DXGIPostProcess DXGIPP_RGB24;
const DXGIPostProcess * postProcessors[] =
{
&DXGIPP_Downsample,
&DXGIPP_SDRWhiteLevel,
&DXGIPP_RGB24
};
typedef struct typedef struct
{ {
ID3D11Texture2D * src; ID3D11Texture2D * src;
@ -81,6 +89,7 @@ static struct DXGICopyBackend * backends[] = {
static bool dxgi_deinit(void); static bool dxgi_deinit(void);
static CaptureResult dxgi_releaseFrame(void); static CaptureResult dxgi_releaseFrame(void);
static void ppEarlyInit(void);
static bool ppInit(const DXGIPostProcess * pp, bool shareable); static bool ppInit(const DXGIPostProcess * pp, bool shareable);
static ID3D11Texture2D * ppRun(Texture * tex, ID3D11Texture2D * src, static ID3D11Texture2D * ppRun(Texture * tex, ID3D11Texture2D * src,
int * width, int * height, int * width, int * height,
@ -119,7 +128,6 @@ static void dxgi_initOptions(void)
.type = OPTION_TYPE_STRING, .type = OPTION_TYPE_STRING,
.value.x_string = NULL .value.x_string = NULL
}, },
DOWNSAMPLE_PARSER("dxgi"),
{ {
.module = "dxgi", .module = "dxgi",
.name = "maxTextures", .name = "maxTextures",
@ -173,6 +181,7 @@ static void dxgi_initOptions(void)
}; };
option_register(options); option_register(options);
ppEarlyInit();
} }
static bool dxgi_create(CaptureGetPointerBuffer getPointerBufferFn, CapturePostPointerBuffer postPointerBufferFn) static bool dxgi_create(CaptureGetPointerBuffer getPointerBufferFn, CapturePostPointerBuffer postPointerBufferFn)
@ -693,14 +702,6 @@ static bool dxgi_init(void)
this->outputWidth = this->width; this->outputWidth = this->width;
this->outputHeight = this->height; this->outputHeight = this->height;
DownsampleRule * rule = downsampleRule_match(this->width, this->height);
if (rule)
{
this->outputWidth = rule->targetX;
this->outputHeight = rule->targetY;
}
DEBUG_INFO("Request Size : %u x %u", this->outputWidth, this->outputHeight);
const char * copyBackend = option_get_string("dxgi", "copyBackend"); const char * copyBackend = option_get_string("dxgi", "copyBackend");
for (int i = 0; i < ARRAY_LENGTH(backends); ++i) for (int i = 0; i < ARRAY_LENGTH(backends); ++i)
@ -738,6 +739,13 @@ static bool dxgi_init(void)
if (!initVertexShader()) if (!initVertexShader())
goto fail; goto fail;
if (!ppInit(&DXGIPP_Downsample,
this->backend != &copyBackendD3D11 && !this->hdr))
{
DEBUG_ERROR("Failed to intiailize the downsample post processor");
goto fail;
}
// if HDR add the SDRWhiteLevel post processor to correct the output // if HDR add the SDRWhiteLevel post processor to correct the output
if (this->hdr) if (this->hdr)
{ {
@ -856,10 +864,10 @@ static void rectToFrameDamageRect(RECT * src, FrameDamageRect * dst)
{ {
*dst = (FrameDamageRect) *dst = (FrameDamageRect)
{ {
.x = src->left , .x = floor((double)src->left * this->scaleX),
.y = src->top , .y = floor((double)src->top * this->scaleY),
.width = (src->right - src->left), .width = ceil ((double)(src->right - src->left) * this->scaleX),
.height = (src->bottom - src->top) .height = ceil ((double)(src->bottom - src->top) * this->scaleY)
}; };
} }
@ -917,10 +925,12 @@ static void computeFrameDamage(Texture * tex)
*texDamageRect++ = (FrameDamageRect) *texDamageRect++ = (FrameDamageRect)
{ {
.x = moveRect->SourcePoint.x, .x = floor((double)moveRect->SourcePoint.x * this->scaleX),
.y = moveRect->SourcePoint.y, .y = floor((double)moveRect->SourcePoint.y * this->scaleY),
.width = moveRect->DestinationRect.right - moveRect->DestinationRect.left, .width = ceil((double)(moveRect->DestinationRect.right -
.height = moveRect->DestinationRect.bottom - moveRect->DestinationRect.top moveRect->DestinationRect.left) * this->scaleX),
.height = ceil((double)(moveRect->DestinationRect.bottom -
moveRect->DestinationRect.top ) * this->scaleY)
}; };
rectToFrameDamageRect(&moveRect->DestinationRect, texDamageRect++); rectToFrameDamageRect(&moveRect->DestinationRect, texDamageRect++);
@ -1038,14 +1048,6 @@ static CaptureResult dxgi_capture(void)
{ {
if (copyFrame) if (copyFrame)
{ {
if (this->useAcquireLock)
{
LOCKED({ computeFrameDamage(tex); });
}
else
computeFrameDamage(tex);
computeTexDamage(tex);
// run any postprocessors // run any postprocessors
int width = this->width; int width = this->width;
int height = this->height; int height = this->height;
@ -1114,8 +1116,20 @@ static CaptureResult dxgi_capture(void)
this->dataHeight = rows; this->dataHeight = rows;
this->pitch = pitch; this->pitch = pitch;
this->stride = pitch / this->bpp; this->stride = pitch / this->bpp;
this->scaleX = (double)width / this->width;
this->scaleY = (double)height / this->height;
} }
// compute the frame damage
if (this->useAcquireLock)
{
LOCKED({ computeFrameDamage(tex); });
}
else
computeFrameDamage(tex);
computeTexDamage(tex);
if (!this->backend->copyFrame(tex, dst)) if (!this->backend->copyFrame(tex, dst))
{ {
result = CAPTURE_RESULT_ERROR; result = CAPTURE_RESULT_ERROR;
@ -1380,6 +1394,13 @@ static CaptureResult dxgi_releaseFrame(void)
return CAPTURE_RESULT_OK; return CAPTURE_RESULT_OK;
} }
static void ppEarlyInit(void)
{
for(int i = 0; i < ARRAY_LENGTH(postProcessors); ++i)
if (postProcessors[i]->earlyInit)
postProcessors[i]->earlyInit();
}
static bool ppInit(const DXGIPostProcess * pp, bool shareable) static bool ppInit(const DXGIPostProcess * pp, bool shareable)
{ {
if (!pp->setup(this->device, this->deviceContext, this->output, shareable)) if (!pp->setup(this->device, this->deviceContext, this->output, shareable))
@ -1448,7 +1469,7 @@ static ID3D11Texture2D * ppRun(Texture * tex, ID3D11Texture2D * src,
format)) format))
{ {
LG_UNLOCK(this->deviceContextLock); LG_UNLOCK(this->deviceContextLock);
DEBUG_ERROR("setFormat failed on a post processor"); DEBUG_ERROR("setFormat failed on a post processor (%s)", inst->pp->name);
return NULL; return NULL;
} }
@ -1476,15 +1497,8 @@ static ID3D11Texture2D * ppRun(Texture * tex, ID3D11Texture2D * src,
// run the post processor // run the post processor
ID3D11Texture2D * out = inst->pp->run(inst->opaque, inst->srv); ID3D11Texture2D * out = inst->pp->run(inst->opaque, inst->srv);
// if the post processor failed // if the post processor does nothing, just continue
if (!out) if (!out)
{
LG_UNLOCK(this->deviceContextLock);
return NULL;
}
// if the post processor did nothing, just continue
if (out == src)
{ {
LG_UNLOCK(this->deviceContextLock); LG_UNLOCK(this->deviceContextLock);
continue; continue;
@ -1495,6 +1509,11 @@ static ID3D11Texture2D * ppRun(Texture * tex, ID3D11Texture2D * src,
*this->deviceContext, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); *this->deviceContext, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
ID3D11DeviceContext_Draw(*this->deviceContext, 4, 0); ID3D11DeviceContext_Draw(*this->deviceContext, 4, 0);
// unset the target render view
static ID3D11RenderTargetView * nullTarget = NULL;
ID3D11DeviceContext_OMSetRenderTargets(
*this->deviceContext, 1, &nullTarget, NULL);
// the output is now the input // the output is now the input
src = out; src = out;
LG_UNLOCK(this->deviceContextLock); LG_UNLOCK(this->deviceContextLock);

View File

@ -48,7 +48,7 @@ typedef struct Texture
uint64_t copyTime; uint64_t copyTime;
uint32_t damageRectsCount; uint32_t damageRectsCount;
FrameDamageRect damageRects[KVMFR_MAX_DAMAGE_RECTS]; FrameDamageRect damageRects[KVMFR_MAX_DAMAGE_RECTS];
int32_t texDamageCount; int texDamageCount;
FrameDamageRect texDamageRects[KVMFR_MAX_DAMAGE_RECTS]; FrameDamageRect texDamageRects[KVMFR_MAX_DAMAGE_RECTS];
// post processing // post processing
@ -109,6 +109,7 @@ struct DXGIInterface
unsigned int stride; unsigned int stride;
unsigned int padding; unsigned int padding;
unsigned int bpp; unsigned int bpp;
double scaleX, scaleY;
CaptureFormat format, outputFormat; CaptureFormat format, outputFormat;
CaptureRotation rotation; CaptureRotation rotation;

View File

@ -0,0 +1,271 @@
/**
* Looking Glass
* Copyright © 2017-2023 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pp.h"
#include "com_ref.h"
#include "util.h"
#include "downsample_parser.h"
#include "common/debug.h"
#include "common/windebug.h"
#include <math.h>
typedef struct Downsample
{
ID3D11Device ** device;
ID3D11DeviceContext ** context;
bool shareable;
bool disabled;
int width , height;
ID3D11SamplerState ** sampler;
ID3D11PixelShader ** pshader;
}
Downsample;
static Downsample this = {0};
typedef struct
{
ID3D11Texture2D ** tex;
ID3D11RenderTargetView ** target;
}
DownsampleInst;
static void downsample_earlyInit(void)
{
struct Option options[] =
{
DOWNSAMPLE_PARSER("dxgi"),
{0}
};
option_register(options);
}
static bool downsample_setup(
ID3D11Device ** device,
ID3D11DeviceContext ** context,
IDXGIOutput ** output,
bool shareable
)
{
this.device = device;
this.context = context;
this.shareable = shareable;
return true;
}
static void downsample_finish(void)
{
memset(&this, 0, sizeof(this));
}
static bool downsample_configure(void * opaque,
int * width, int * height,
int * cols , int * rows ,
CaptureFormat * format)
{
DownsampleInst * inst = (DownsampleInst *)opaque;
if (*format == CAPTURE_FMT_BGR)
this.disabled = true;
if (this.disabled)
return true;
HRESULT status;
comRef_scopePush();
if (!this.pshader)
{
DownsampleRule * rule = downsampleRule_match(*width, *height);
if (!rule || (rule->targetX == *width && rule->targetY == *height))
{
this.disabled = true;
return true;
}
this.width = rule->targetX;
this.height = rule->targetY;
DEBUG_INFO("Downsampling to: %u x %u", this.width, this.height);
static const char * pshaderSrc =
"Texture2D gInputTexture : register(t0);\n"
"SamplerState gSamplerState : register(s0);\n"
"\n"
"float4 main(\n"
" float4 position : SV_POSITION,\n"
" float2 texCoord : TEXCOORD0) : SV_TARGET"
"{\n"
" return gInputTexture.Sample(gSamplerState, texCoord);\n"
"}\n";
comRef_defineLocal(ID3DBlob, byteCode);
if (!compileShader(byteCode, "main", "ps_5_0", pshaderSrc, NULL))
goto fail;
comRef_defineLocal(ID3D11PixelShader, pshader);
HRESULT status = ID3D11Device_CreatePixelShader(
*this.device,
ID3D10Blob_GetBufferPointer(*byteCode),
ID3D10Blob_GetBufferSize (*byteCode),
NULL,
pshader);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to create the pixel shader", status);
goto fail;
}
const D3D11_SAMPLER_DESC samplerDesc =
{
.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR,
.AddressU = D3D11_TEXTURE_ADDRESS_WRAP,
.AddressV = D3D11_TEXTURE_ADDRESS_WRAP,
.AddressW = D3D11_TEXTURE_ADDRESS_WRAP,
.ComparisonFunc = D3D11_COMPARISON_NEVER,
.MinLOD = 0,
.MaxLOD = D3D11_FLOAT32_MAX
};
comRef_defineLocal(ID3D11SamplerState, sampler);
status = ID3D11Device_CreateSamplerState(*this.device, &samplerDesc, sampler);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to create the sampler state", status);
goto fail;
}
comRef_toGlobal(this.pshader, pshader);
comRef_toGlobal(this.sampler, sampler);
}
D3D11_TEXTURE2D_DESC texDesc =
{
.Width = this.width,
.Height = this.height,
.MipLevels = 1,
.ArraySize = 1,
.SampleDesc.Count = 1,
.SampleDesc.Quality = 0,
.Usage = D3D11_USAGE_DEFAULT,
.Format = DXGI_FORMAT_B8G8R8A8_UNORM,
.BindFlags = D3D11_BIND_RENDER_TARGET |
D3D11_BIND_SHADER_RESOURCE,
.CPUAccessFlags = 0,
.MiscFlags = 0
};
// allow texture sharing with other backends
if (this.shareable)
texDesc.MiscFlags |=
D3D11_RESOURCE_MISC_SHARED |
D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
comRef_defineLocal(ID3D11Texture2D, tex);
status = ID3D11Device_CreateTexture2D(
*this.device, &texDesc, NULL, tex);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to create the output texture", status);
goto fail;
}
comRef_defineLocal(ID3D11RenderTargetView, target);
status = ID3D11Device_CreateRenderTargetView(
*this.device, *(ID3D11Resource **)tex, NULL, target);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to create the render target view", status);
goto fail;
}
*width = *cols = this.width;
*height = *rows = this.height;
comRef_toGlobal(inst->tex , tex );
comRef_toGlobal(inst->target , target );
comRef_scopePop();
return true;
fail:
comRef_scopePop();
return false;
}
static bool downsample_init(void ** opaque)
{
DownsampleInst * inst = (DownsampleInst *)calloc(1, sizeof(*inst));
if (!inst)
{
DEBUG_ERROR("Failed to allocate memory");
return false;
}
*opaque = inst;
return true;
}
static void downsample_free(void * opaque)
{
DownsampleInst * inst = (DownsampleInst *)opaque;
comRef_release(inst->target);
comRef_release(inst->tex );
free(inst);
}
static ID3D11Texture2D * downsample_run(void * opaque,
ID3D11ShaderResourceView * srv)
{
if (this.disabled)
return NULL;
DownsampleInst * inst = (DownsampleInst *)opaque;
// set the pixel shader & resources
ID3D11DeviceContext_PSSetShader(*this.context, *this.pshader, NULL, 0);
ID3D11DeviceContext_PSSetSamplers (*this.context, 0, 1, this.sampler);
ID3D11DeviceContext_PSSetShaderResources(*this.context, 0, 1, &srv);
// set the render target
ID3D11DeviceContext_OMSetRenderTargets(*this.context, 1, inst->target, NULL);
return *inst->tex;
}
DXGIPostProcess DXGIPP_Downsample =
{
.name = "Downsample",
.earlyInit = downsample_earlyInit,
.setup = downsample_setup,
.init = downsample_init,
.free = downsample_free,
.configure = downsample_configure,
.run = downsample_run,
.finish = downsample_finish
};