I'm trying to develop an application that renders the cef texture in offscreen-rendering. I don't want to render it to a form or any window. I'd like to create a texture which I'm then sharing to other processes, for now I would simply be fine in saving it to disk to see if I'm getting anything.
Below is the code I'm using, I have little to no experience in D3D11, I've tried using SharpDX to create a Device to open the shared pointer I'm getting with OnAcceleratedPaint (which gets called correctly from the RenderHandler class).
using System.Runtime.InteropServices;
using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using Buffer = SharpDX.Direct3D11.Buffer;
using Device = SharpDX.Direct3D11.Device;
using Device1 = SharpDX.Direct3D11.Device1;
using Device2 = SharpDX.Direct3D11.Device2;
using Device3 = SharpDX.Direct3D11.Device3;
using Device4 = SharpDX.Direct3D11.Device4;
namespace Browser.HardwareAcceleration;
public class D3DRenderer
{
private Lock _lock = new ();
private Texture2D _texture;
private Device _device;
private int _width = 1280;
private int _height = 720;
private VertexShader _vs;
private PixelShader _ps;
private InputLayout _layout;
private const string ShaderSrc = "Texture2D tex; \n" +
" \n" +
"SamplerState texSampler \n" +
"{ \n" +
" Texture = <tex>; \n" +
" Filter = MIN_MAG_MIP_POINT; \n" +
"}; \n" +
" \n" +
"struct PS_IN \n" +
"{ \n" +
" float4 pos : SV_POSITION; \n" +
" float2 Tex : TEXCOORD; \n" +
"}; \n" +
" \n" +
"PS_IN vertex( PS_IN input ) \n" +
"{ \n" +
" return input; \n" +
"} \n" +
" \n" +
"float4 pixel( PS_IN input ) : SV_Target \n" +
"{ \n" +
" return tex.Sample( texSampler, input.Tex ); \n" +
"} \n" +
" \n";
[StructLayout(LayoutKind.Sequential)]
private struct VertexDX11
{
public Vector4 Position;
public Vector2 TexCoord0;
public static InputElement[] GetInputElements()
{
InputElement[] inputElements = [new("SV_POSITION", 0, Format.R32G32B32A32_Float, 0, 0), new("TEXCOORD", 0, Format.R32G32_Float, 16, 0)];
return inputElements;
}
}
public D3DRenderer()
{
_device = new Device(DriverType.Hardware, DeviceCreationFlags.None);
UpgradeDevice();
var viewPort = new Viewport(0, 0, _width, _height);
_device.ImmediateContext.Rasterizer.SetViewport(viewPort);
//testing
if (_texture == null)
{
var desc = new Texture2DDescription();
desc.Format = Format.R8G8B8A8_UInt;
desc.ArraySize = 1;
desc.Width = _width;
desc.Height = _height;
desc.SampleDescription = new SampleDescription(1, 0);
desc.Usage = ResourceUsage.Default;
desc.BindFlags = BindFlags.None;
desc.CpuAccessFlags = CpuAccessFlags.Read;
_texture = new Texture2D(_device, desc);
}
var renderTarget = new RenderTargetView(_device, _texture);
_device.ImmediateContext.ClearRenderTargetView(renderTarget, Color4.Black);
_device.ImmediateContext.OutputMerger.SetRenderTargets(renderTarget);
//
CreateShaders();
_device.ImmediateContext.VertexShader.Set(_vs);
_device.ImmediateContext.PixelShader.Set(_ps);
_device.ImmediateContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleStrip;
_device.ImmediateContext.InputAssembler.InputLayout = _layout;
}
private void UpgradeDevice()
{
var device5 = _device.QueryInterfaceOrNull<Device5>();
if (device5 != null)
{
_device.Dispose();
_device = device5;
return;
}
var device4 = _device.QueryInterfaceOrNull<Device4>();
if (device4 != null)
{
_device.Dispose();
_device = device4;
return;
}
var device3 = _device.QueryInterfaceOrNull<Device3>();
if (device3 != null)
{
_device = device3;
return;
}
var device2 = _device.QueryInterfaceOrNull<Device2>();
if (device2 != null)
{
_device = device2;
return;
}
var device1 = _device.QueryInterfaceOrNull<Device1>();
if (device1 != null)
{
_device.Dispose();
_device = device1;
}
}
private void CreateShaders()
{
using (ShaderBytecode byteCode = ShaderBytecode.Compile(ShaderSrc, "vertex", "vs_4_0_level_9_1"))
{
_vs = new VertexShader(_device, byteCode);
var signature = ShaderSignature.GetInputSignature(byteCode);
_layout = new InputLayout(_device, signature, VertexDX11.GetInputElements());
}
using (ShaderBytecode byteCode = ShaderBytecode.Compile(ShaderSrc, "pixel", "ps_4_0_level_9_1"))
{
_ps = new PixelShader(_device, byteCode);
}
}
public void OnAcceleratedPaint(IntPtr sharedTextureHandle)
{
lock (_lock)
{
var dev1 = _device as Device1;
using (var cefTex = dev1.OpenSharedResource1<Texture2D>(sharedTextureHandle))
{
_device.ImmediateContext.CopyResource(cefTex, _texture);
//do stuff with texture, like save to png or share it with other processes
}
}
}
}
I've tried many things but to no avail, from what I understood I don't need a swap chain to render in osr because I won't display it on a window/form (I don't know if this is correct). I've tried to create a texture to use as a render target but if I do it like the code above I'm getting an exception SharpDX.SharpDXException: HRESULT: [0x80070057], Module: [General], ApiCode: [E_INVALIDARG/Invalid Arguments], Message: The parameter is incorrect.
at the line var renderTarget = new RenderTargetView(_device, _texture);
if I don't do that I'm getting the same exception when opening the shared resource in the OnAcceleratedPaint. I'm guessing I'm creating the device in the wrong way. What do I need to do?
I'm on .NET Core 9 and Windows 10 with a Nvidia 2070