use core::{mem, num::NonZeroU32};
use anyhow::Context;
use asdf_overlay_common::request::UpdateSharedHandle;
use windows::{
Win32::{
Foundation::HANDLE,
Graphics::{
Direct3D::{D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, D3D_SRV_DIMENSION_TEXTURE2D},
Direct3D11::*,
Dxgi::IDXGIKeyedMutex,
},
},
core::{BOOL, Interface},
};
use crate::{renderer::dx::shaders, texture::OverlayTextureState, util::with_keyed_mutex};
const SAMPLER_DESC: D3D11_SAMPLER_DESC = D3D11_SAMPLER_DESC {
Filter: D3D11_FILTER_MIN_MAG_MIP_POINT,
AddressU: D3D11_TEXTURE_ADDRESS_CLAMP,
AddressV: D3D11_TEXTURE_ADDRESS_CLAMP,
AddressW: D3D11_TEXTURE_ADDRESS_CLAMP,
MipLODBias: 0.0,
MaxAnisotropy: 0,
ComparisonFunc: D3D11_COMPARISON_NEVER,
BorderColor: [0.0; 4],
MinLOD: 0.0,
MaxLOD: D3D11_FLOAT32_MAX,
};
struct Dx11Tex {
mutex: Option<IDXGIKeyedMutex>,
view: ID3D11ShaderResourceView,
}
pub struct Dx11Renderer {
constant_buffer: ID3D11Buffer,
texture: OverlayTextureState<Dx11Tex>,
vertex_shader: ID3D11VertexShader,
pixel_shader: ID3D11PixelShader,
blend_state: ID3D11BlendState,
sampler_state: ID3D11SamplerState,
}
impl Dx11Renderer {
#[tracing::instrument]
pub fn new(device: &ID3D11Device) -> anyhow::Result<Self> {
unsafe {
let mut vertex_shader = None;
device
.CreateVertexShader(shaders::VERTEX_SHADER, None, Some(&mut vertex_shader))
.context("vertex shader failed to link")?;
let vertex_shader = vertex_shader.unwrap();
let mut pixel_shader = None;
device
.CreatePixelShader(shaders::PIXEL_SHADER, None, Some(&mut pixel_shader))
.context("pixel shader failed to link")?;
let pixel_shader = pixel_shader.unwrap();
let mut constant_buffer = None;
device
.CreateBuffer(
&D3D11_BUFFER_DESC {
ByteWidth: mem::size_of::<[f32; 4]>() as _,
Usage: D3D11_USAGE_DYNAMIC,
BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as _,
CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as _,
MiscFlags: 0,
StructureByteStride: 0,
},
None,
Some(&mut constant_buffer),
)
.context("cannot create constant buffer")?;
let constant_buffer = constant_buffer.unwrap();
let mut blend_state = None;
device
.CreateBlendState(
&D3D11_BLEND_DESC {
AlphaToCoverageEnable: BOOL(0),
IndependentBlendEnable: BOOL(0),
RenderTarget: [D3D11_RENDER_TARGET_BLEND_DESC {
BlendEnable: BOOL(1),
SrcBlend: D3D11_BLEND_SRC_ALPHA,
DestBlend: D3D11_BLEND_INV_SRC_ALPHA,
BlendOp: D3D11_BLEND_OP_ADD,
SrcBlendAlpha: D3D11_BLEND_ONE,
DestBlendAlpha: D3D11_BLEND_INV_SRC_ALPHA,
BlendOpAlpha: D3D11_BLEND_OP_ADD,
RenderTargetWriteMask: D3D11_COLOR_WRITE_ENABLE_ALL.0 as _,
}; 8],
},
Some(&mut blend_state),
)
.context("cannot create blend state")?;
let blend_state = blend_state.unwrap();
let mut sampler_state = None;
device
.CreateSamplerState(&SAMPLER_DESC, Some(&mut sampler_state))
.context("cannot create blend state")?;
let sampler_state = sampler_state.unwrap();
Ok(Self {
constant_buffer,
texture: OverlayTextureState::new(),
vertex_shader,
pixel_shader,
blend_state,
sampler_state,
})
}
}
pub fn update_texture(&mut self, shared: UpdateSharedHandle) {
self.texture.update(shared);
}
#[tracing::instrument(skip(self))]
pub fn draw(
&mut self,
device: &ID3D11Device,
cx: &ID3D11DeviceContext,
position: (i32, i32),
size: (u32, u32),
screen: (u32, u32),
) -> anyhow::Result<()> {
if screen.0 == 0 || screen.1 == 0 {
return Ok(());
}
let Some(Dx11Tex { view, mutex, .. }) = self
.texture
.get_or_create(|handle| open_shared_texture(device, handle))?
else {
return Ok(());
};
let rect = [
(position.0 as f32 / screen.0 as f32) * 2.0 - 1.0,
-(position.1 as f32 / screen.1 as f32) * 2.0 + 1.0,
(size.0 as f32 / screen.0 as f32) * 2.0,
-(size.1 as f32 / screen.1 as f32) * 2.0,
];
unsafe {
{
let mut mapped_cbuffer = D3D11_MAPPED_SUBRESOURCE::default();
cx.Map(
&self.constant_buffer,
0,
D3D11_MAP_WRITE_DISCARD,
0,
Some(&mut mapped_cbuffer),
)?;
mapped_cbuffer.pData.cast::<[f32; 4]>().write(rect);
cx.Unmap(&self.constant_buffer, 0);
}
cx.OMSetBlendState(&self.blend_state, None, 0x00ffffff);
cx.RSSetViewports(Some(&[D3D11_VIEWPORT {
TopLeftX: 0.0,
TopLeftY: 0.0,
Width: screen.0 as _,
Height: screen.1 as _,
MinDepth: 0.0,
MaxDepth: 1.0,
}]));
cx.VSSetShader(&self.vertex_shader, None);
cx.PSSetShader(&self.pixel_shader, None);
cx.PSSetSamplers(0, Some(&[Some(self.sampler_state.clone())]));
cx.VSSetConstantBuffers(0, Some(&[Some(self.constant_buffer.clone())]));
cx.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
with_keyed_mutex(mutex.as_ref(), || {
cx.PSSetShaderResources(0, Some(&[Some(view.clone())]));
cx.Draw(4, 0);
})?;
}
Ok(())
}
}
fn open_shared_texture(
device: &ID3D11Device,
handle: NonZeroU32,
) -> anyhow::Result<Option<Dx11Tex>> {
let mut texture = None;
if unsafe {
device.OpenSharedResource::<ID3D11Texture2D>(HANDLE(handle.get() as _), &mut texture)
}
.is_err()
{
return Ok(None);
}
let texture = texture.context("failed to open shared texture")?;
let mut desc = D3D11_TEXTURE2D_DESC::default();
unsafe {
texture.GetDesc(&mut desc);
}
let size = (desc.Width, desc.Height);
if size.0 == 0 || size.1 == 0 {
return Ok(None);
}
let mutex = texture.cast::<IDXGIKeyedMutex>().ok();
let mut view = None;
unsafe {
device.CreateShaderResourceView(
&texture,
Some(&D3D11_SHADER_RESOURCE_VIEW_DESC {
Format: desc.Format,
ViewDimension: D3D_SRV_DIMENSION_TEXTURE2D,
Anonymous: D3D11_SHADER_RESOURCE_VIEW_DESC_0 {
Texture2D: D3D11_TEX2D_SRV {
MostDetailedMip: 0,
MipLevels: 1,
},
},
}),
Some(&mut view),
)?;
}
let view = view.context("cannot create texture view")?;
Ok(Some(Dx11Tex { mutex, view }))
}