qt+gxdi+ffmpeg远程控制(五)

本文讲述了如何在Qt应用中优化Dx11渲染过程,通过创建新类分离渲染逻辑,使截图和窗口渲染解耦。同时介绍了如何在不同设备间共享和复制纹理数据,确保资源管理的清晰和高效。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

qt+gxdi+ffmpeg远程控制(四)

完成代码结构优化,将渲染部分与截图部分分开。

上回说到,将gxdi截图获取的纹理渲染到指定窗口中,但是耦合性太高,渲染逻辑直接写在负责截图的类中,今天讲怎么将他们分开。

首先创建一个类负责渲染,初始化渲染需要的设备、上下文、以及对应的顶点索引与着色器

MyDx11::MyDx11(HWND _hwnd, int _windowWidth, int _windowHeight, const Vertex* _vertices, size_t _verticesSize, const UINT16* _indices, size_t _indicesSize)
{
	// 创建设备及交换链
	DXGI_MODE_DESC bufferDesc;
	ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
	bufferDesc.Width = _windowWidth;
	bufferDesc.Height = _windowHeight;
	bufferDesc.RefreshRate.Numerator = 0;
	bufferDesc.RefreshRate.Denominator = 1;
	bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

	DXGI_SWAP_CHAIN_DESC swapChainDesc;
	ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
	swapChainDesc.BufferDesc = bufferDesc;
	swapChainDesc.SampleDesc.Count = 1;
	swapChainDesc.SampleDesc.Quality = 0;
	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	swapChainDesc.BufferCount = 1;
	swapChainDesc.OutputWindow = _hwnd;
	swapChainDesc.Windowed = TRUE;
	swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

	D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL, D3D11_SDK_VERSION, &swapChainDesc,
		&this->m_swapChain, &this->m_device, NULL, &this->m_context);

	// 创建呈现目标
	ID3D11Texture2D* backBuffer;
	this->m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
	this->m_device->CreateRenderTargetView(backBuffer, nullptr, &this->m_renderTargetView);
	SAFE_RELEASE(backBuffer);
	// 绑定呈现目标
	this->m_context->OMSetRenderTargets(1, &this->m_renderTargetView, nullptr);

	// 将视区数组绑定到管道的光栅器阶段
	D3D11_VIEWPORT viewPort;
	viewPort.TopLeftX = 0;
	viewPort.TopLeftY = 0;
	viewPort.Width = _windowWidth;
	viewPort.Height = _windowHeight;
	viewPort.MinDepth = 0.0f;
	viewPort.MaxDepth = 1.0f;
	this->m_context->RSSetViewports(1, &viewPort);

	// 顶点缓存
	D3D11_BUFFER_DESC bd = {};
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.ByteWidth = _verticesSize;
	bd.StructureByteStride = sizeof(Vertex);
	D3D11_SUBRESOURCE_DATA sd = {};
	sd.pSysMem = _vertices;
	this->m_device->CreateBuffer(&bd, &sd, &this->m_vertexBuffer);

	// 顶点索引
	D3D11_BUFFER_DESC ibd = {};
	ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
	ibd.ByteWidth = _indicesSize;
	ibd.StructureByteStride = sizeof(UINT16);
	D3D11_SUBRESOURCE_DATA isd = {};
	isd.pSysMem = _indices;
	this->m_device->CreateBuffer(&ibd, &isd, &this->m_indexBuffer);
	this->m_indicesSize = _indicesSize / sizeof(UINT16);

	// 顶点着色器
	D3D11_INPUT_ELEMENT_DESC ied[] = {
		{"POSITION", 0, DXGI_FORMAT::DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
		{"TEXCOORD", 0, DXGI_FORMAT::DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
	};
	this->m_device->CreateInputLayout(ied, std::size(ied), g_main_VS, sizeof(g_main_VS), &this->m_inputLayout);
	this->m_device->CreateVertexShader(g_main_VS, sizeof(g_main_VS), nullptr, &this->m_vertexShader);

	// 采样器
	D3D11_SAMPLER_DESC samplerDesc = {};
	samplerDesc.Filter = D3D11_FILTER::D3D11_FILTER_ANISOTROPIC;
	samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.MaxAnisotropy = 16;
	this->m_device->CreateSamplerState(&samplerDesc, &this->m_sampler);

	// 像素着色器
	this->m_device->CreatePixelShader(g_main_PS, sizeof(g_main_PS), nullptr, &this->m_pixelShader);

	

	// 绑定到管线
	UINT stride = sizeof(Vertex);
	UINT offset = 0u;
	this->m_context->IASetVertexBuffers(0, 1, &this->m_vertexBuffer, &stride, &offset);
	this->m_context->IASetIndexBuffer(this->m_indexBuffer, DXGI_FORMAT_R16_UINT, 0);
	this->m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY::D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	this->m_context->IASetInputLayout(this->m_inputLayout);
	this->m_context->VSSetShader(this->m_vertexShader, 0, 0);

	this->m_context->PSSetShader(this->m_pixelShader, 0, 0);
	/*ID3D11ShaderResourceView* srvs[] = { this->m_shaderResourceView };
	this->m_context->PSSetShaderResources(0, 1, srvs);*/

	ID3D11SamplerState* samplers[] = { this->m_sampler };
	this->m_context->PSSetSamplers(0, 1, samplers);
}

qt组件采用dx渲染时,需要重载一些方法

public:
	QPaintEngine* paintEngine() const { return nullptr; }
public:
	void paintEvent(QPaintEvent* event);
	void resizeEvent(QResizeEvent* event);

首先将paintEngine关闭,重写绘制与改变大小方法既可。

void ShowScreenWindow::initD3D()
{
	delete this->m_d3d11;
	const Vertex vertices[] = {
		{-1,	1,	0,	0,	0},
		{1,		1,	0,	1,	0},
		{1,		-1,	0,	1,	1},
		{-1,	-1,	0,	0,	1},
	};

	const UINT16 indices[] = {
		0,1,2, 0,2,3
	};
	this->m_d3d11 = new MyDx11(reinterpret_cast<HWND>(this->winId()), this->width(), this->height(), vertices, sizeof(vertices), indices, sizeof(indices));
}


void ShowScreenWindow::resizeD3D()
{
	this->initD3D();
}

这里只是简单写法,目前窗口给定的是不能修改大小,所以修改窗口大小的回调随便写写,不需要关心性能问题了。

杂七杂八的代码就不贴出来了,主要涉及到的是如何将纹理信息在不同设备中拷贝,根据api给出的信息,首先需要将texture中的MiscFlags属性设置为D3D11_RESOURCE_MISC_SHARED,在去到sharedHandle,根据sharedHandle获取纹理地址进行拷贝。

void MyDx11::createTexture(int _srcWidth, int _srcHeight)
{
	// 创建纹理与视图
	D3D11_TEXTURE2D_DESC tdesc;
	ZeroMemory(&tdesc, sizeof(D3D11_TEXTURE2D_DESC));
	tdesc.Width = _srcWidth;
	tdesc.Height = _srcHeight;
	tdesc.MipLevels = 1;
	tdesc.ArraySize = 1;
	tdesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
	tdesc.SampleDesc.Count = 1;
	tdesc.SampleDesc.Quality = 0;
	tdesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	tdesc.CPUAccessFlags = 0;
	tdesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
	this->m_device->CreateTexture2D(&tdesc, nullptr, &this->m_texture);

	IDXGIResource* sharedTexture = nullptr;
	this->m_texture->QueryInterface(__uuidof(IDXGIResource), (void**)&sharedTexture);
	sharedTexture->GetSharedHandle(&this->m_sharedHandle);
	SAFE_RELEASE(sharedTexture);

	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = CD3D11_SHADER_RESOURCE_VIEW_DESC(
		this->m_texture,
		D3D11_SRV_DIMENSION_TEXTURE2D,
		DXGI_FORMAT_B8G8R8A8_UNORM
	);
	this->m_device->CreateShaderResourceView(this->m_texture, &srvDesc, &this->m_shaderResourceView);

	ID3D11ShaderResourceView* srvs[] = { this->m_shaderResourceView };
	this->m_context->PSSetShaderResources(0, 1, srvs);
}

这里拿到m_sharedHandle之后,每次需要渲染的时候,从截图类中将获取到的纹理拷贝过来

bool Monitor::copy_frame_data(HANDLE _sharedHandle, uint8_t* _buffer, size_t _size)
{
	if (_sharedHandle != nullptr) {
		ID3D11Texture2D* _texture = nullptr;
		this->m_device->OpenSharedResource(_sharedHandle, __uuidof(ID3D11Texture2D), (void**)&_texture);
		if (_texture != nullptr) {
			this->m_context->CopySubresourceRegion(_texture, 0, 0, 0, 0, this->m_image, 0, 0);
			// this->m_context->CopyResource(_texture, this->m_image);
		}
	}
	
	D3D11_TEXTURE2D_DESC desc;
	this->m_image->GetDesc(&desc);

	ID3D11Texture2D* _target = nullptr;
	D3D11_TEXTURE2D_DESC target_desc;
	memcpy_s(&target_desc, sizeof(target_desc), &desc, sizeof(desc));
	target_desc.Usage = D3D11_USAGE_STAGING;
	target_desc.BindFlags = 0;
	target_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
	target_desc.MiscFlags = 0;
	HRESULT hr = this->m_device->CreateTexture2D(&target_desc, nullptr, &_target);
	if (FAILED(hr)) {
		return false;
	}
	this->m_context->CopyResource(_target, this->m_image);

	D3D11_MAPPED_SUBRESOURCE target_map_res;
	UINT subresource = D3D11CalcSubresource(0, 0, 0);
	hr = this->m_context->Map(_target, subresource, D3D11_MAP_READ, 0, &target_map_res);
	if (FAILED(hr)) {
		_target->Release();
		this->m_duplication->ReleaseFrame();
		return false;
	}

	memcpy_s(_buffer, _size, target_map_res.pData, _size);
	this->m_context->Unmap(_target, subresource);
	_target->Release();
	this->m_duplication->ReleaseFrame();
	return true;
}

OpenSharedResource负责根据sharedHandle获取可以供不同设备共享的纹理地址,拷贝过去后就可以显示了,此处调用copyResource与CopySubresourceRegion都可。下面是windows给出的解释。具体不调用flush会出现什么,这里还没看出来。

向设备授予对在不同设备上创建的共享资源的访问权限。

从 D3D9 共享到 D3D11 的纹理具有以下限制。

  • 纹理必须是 2D
  • 仅允许 1 个 mip 级别
  • 纹理必须具有默认用法
  • 纹理必须仅写入
  • 不允许使用 MSAA 纹理
  • 绑定标志必须设置SHADER_RESOURCE和RENDER_TARGET
  • 仅允许R10G10B10A2_UNORM、R16G16B16A16_FLOAT和R8G8B8A8_UNORM格式

如果在一台设备上更新了共享纹理,则必须在该设备上调用 ID3D11DeviceContext::Flush 。

 源码在这

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值