I noticed the videoeffect sdk example doesn't show how to write out the effect? I.E. I see how to get the texture and then process it with my own code, but how do I write result to the DX11 output for virtual to consume for other effects in the chain?
Posted Sun 24 Dec 23 @ 8:08 pm
Tried writing back to the texture supplied but unsure if that's the right way of doing it.
Posted Wed 27 Dec 23 @ 3:37 am
This does not appear to work, so still looking for an updated example on how to write to the DX11 texture for the output in the FX chain.
Posted Wed 27 Dec 23 @ 5:11 pm
You can read maybe this example.
https://github.com/leadedge/SpoutVDJ
You can use PixelShader
https://github.com/leadedge/SpoutVDJ
You can use PixelShader
Posted Wed 27 Dec 23 @ 9:39 pm
I was hoping it was simpler :), but thanks, I’ll start down this path.
Posted Wed 27 Dec 23 @ 10:59 pm
Not sure what I am doing wrong but I am still seeing the standard output on the VDJ output. Everything is created properly.
TouchObject<TETexture> test;
result = TEInstanceLinkGetTextureValue(instance, "op/vdjtextureout", TELinkValueCurrent, test.take());
if (result == TEResultSuccess && test != nullptr) {
if (TETextureGetType(test) == TETextureTypeD3DShared && result == TEResultSuccess)
{
TouchObject<TED3D11Texture> texture;
result = TED3D11ContextGetTexture(D3DContext, static_cast<TED3DSharedTexture*>(test.get()), texture.take());
if (result != TEResultSuccess)
{
return S_FALSE;
}
devContext->CopyResource(TED3D11TextureGetTexture(texture), D3DTextureOutput);
}
}
if (result != TEResultSuccess)
{
return S_FALSE;
}
devContext->PSSetShader(D3DPixelShader, nullptr, 0);
devContext->PSSetShaderResources(0, 1, &D3DOutputTextureView);
UINT stride = sizeof(TLVERTEX);
UINT offset = 0;
devContext->IASetVertexBuffers(0, 1, &D3DVertexBuffer, &stride, &offset);
devContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
devContext->Draw(4, 0);
}
Posted Thu 28 Dec 23 @ 6:10 pm
Anyone have any thoughts, haven't gotten any further
Posted Wed 03 Jan 24 @ 3:15 pm
Here's some code of how it works in the NDI plugin:
Some definitions, TLVERTEX should match the way your pixel shader works.
A pretty minimal pixel shader
If you keep this layout, you also don't need a vertex shader, as the default one that VirtualDJ already set will be fine.
Helper function to create a texture:
Helper functions to fill the vertices:
Helper function to draw a texture
OnDraw() to actually draw a texture
Creating a texture with data in memory:
Would indeed be useful to make a clean example from this, but I think this should get you started.
Some definitions, TLVERTEX should match the way your pixel shader works.
#define D3DFVF_TLVERTEX 1
struct D3DXCOLOR
{
public:
D3DXCOLOR() = default;
D3DXCOLOR(FLOAT r, FLOAT g, FLOAT b, FLOAT a)
{
this->r = r;
this->g = g;
this->b = b;
this->a = a;
}
operator FLOAT* ()
{
return &r;
}
FLOAT r, g, b, a;
};
struct TLVERTEX
{
FLOAT x, y, z; // position
D3DXCOLOR colour; // color
FLOAT u, v; // texture coordinate
};
A pretty minimal pixel shader
Texture2D shaderTexture;
SamplerState SampleType;
float4 main(float4 position : SV_Position, float4 color : COLOR, float2 texcoord : TEXCOORD) : SV_TARGET0
{
float4 output;
output = shaderTexture.Sample(SampleType, texcoord);
output = output * color;
return output;
}
If you keep this layout, you also don't need a vertex shader, as the default one that VirtualDJ already set will be fine.
Helper function to create a texture:
ID3D11Texture2D* createTexture(ID3D11Device *d3dDev, int w, int h, DXGI_FORMAT format, D3D11_SUBRESOURCE_DATA *data, ID3D11ShaderResourceView **view)
{
D3D11_TEXTURE2D_DESC desc{ 0 };
desc.Width = w;
desc.Height = h;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = format ? format : DXGI_FORMAT_B8G8R8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
ID3D11Texture2D* d3dTexture = nullptr;
HRESULT res = d3dDev->CreateTexture2D(&desc, data, &d3dTexture);
if (d3dTexture)
{
D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc{};
viewDesc.Format = desc.Format;
if (desc.Format==DXGI_FORMAT_NV12)
viewDesc.Format = DXGI_FORMAT_R8_UNORM;
else if (desc.Format==DXGI_FORMAT_P010 || desc.Format==DXGI_FORMAT_P016)
viewDesc.Format = DXGI_FORMAT_R16_UNORM;
else if (desc.Format==DXGI_FORMAT_YUY2) // https://msdn.microsoft.com/en-us/library/bb173059(v=vs.85).aspx
viewDesc.Format = DXGI_FORMAT_R8G8B8A8_UINT;
viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
viewDesc.Texture2D.MipLevels = -1;
d3dDev->CreateShaderResourceView(d3dTexture, &viewDesc, view);
if (!*view)
{
DebugBreak();
}
}
return d3dTexture;
}
Helper functions to fill the vertices:
template<class T> void setVertexDst(T* vertices, float dstX, float dstY, float width, float height, DWORD col)
{
if (!vertices)
return;
const D3DXCOLOR color = D3DXCOLOR(GetRValue(col)/255.f, GetGValue(col)/255.f, GetBValue(col)/255.f, 1.f);
vertices[0].colour = color;
vertices[0].x = dstX+width;
vertices[0].y = dstY;
vertices[0].z = 0.0f;
vertices[1].colour = color;
vertices[1].x = dstX + width;
vertices[1].y = dstY + height;
vertices[1].z = 0.0f;
vertices[2].colour = color;
vertices[2].x = dstX;
vertices[2].y = dstY + height;
vertices[2].z = 0.0f;
vertices[3].colour = color;
vertices[3].x = dstX;
vertices[3].y = dstY + height;
vertices[3].z = 0.0f;
vertices[4].colour = color;
vertices[4].x = dstX;
vertices[4].y = dstY;
vertices[4].z = 0.0f;
vertices[5].colour = color;
vertices[5].x = dstX + width;
vertices[5].y = dstY;
vertices[5].z = 0.0f;
}
template<class T> void setVertexSrc(T* vertices, float srcX, float srcY, float srcWidth, float srcHeight, float textureWidth, float textureHeight)
{
if (!vertices)
return;
vertices[0].u = (srcX+srcWidth) / textureWidth;
vertices[0].v = srcY / textureHeight;
vertices[1].u = (srcX+srcWidth) / textureWidth;
vertices[1].v = (srcY+srcHeight) / textureHeight;
vertices[2].u = srcX / textureWidth;
vertices[2].v = (srcY+srcHeight) / textureHeight;
vertices[3].u = srcX / textureWidth;
vertices[3].v = (srcY+srcHeight) / textureHeight;
vertices[4].u = srcX / textureWidth;
vertices[4].v = srcY / textureHeight;
vertices[5].u = (srcX+srcWidth) / textureWidth;
vertices[5].v = srcY / textureHeight;
}
Helper function to draw a texture
void bitblt(ID3D11Device* d3dDev, ID3D11ShaderResourceView* textureView)
{
int nbVertices = 6;
if (!vertexBuffer)
{
D3D11_BUFFER_DESC bd{ 0 };
bd.Usage = D3D11_USAGE_DYNAMIC; // write access access by CPU and GPU
bd.ByteWidth = sizeof(TLVERTEX) * nbVertices;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; // use as a vertex buffer
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // allow CPU to write in buffer
HRESULT hr = d3dDev->CreateBuffer(&bd, nullptr, &vertexBuffer.p);
if (!vertexBuffer)
return;
}
D3D11_MAPPED_SUBRESOURCE ms;
HRESULT hr = devContext->Map(vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &ms); // map the buffer
if (hr!=S_OK)
return;
int srcX=0, srcY=0, srcWidth=curTextureW, srcHeight=curTextureH, dstX=0, dstY=0, dstWidth=width, dstHeight=height;
initImageSize(&srcX, &srcY, &srcWidth, &srcHeight, curTextureAR, 0, width, height, &dstX, &dstY, &dstWidth, &dstHeight);
TLVERTEX *vertices = (TLVERTEX*)ms.pData;
setVertexDst(vertices, (float)dstX, (float)dstY, (float)dstWidth, (float)dstHeight, RGB(255, 255, 255));
setVertexSrc(vertices, (float)srcX, (float)srcY, (float)srcWidth, (float)srcHeight, (float)curTextureW, (float)curTextureH);
devContext->Unmap(vertexBuffer, 0);
UINT stride = sizeof(TLVERTEX);
UINT offset = 0;
devContext->IASetVertexBuffers(0, 1, &vertexBuffer.p, &stride, &offset);
devContext->OMSetBlendState(nullptr, nullptr, 0xffffffff);
if (!pPS_Alpha)
{
std::string_view PS_Alpha = getResource(L"pshader.cso");
hr = d3dDev->CreatePixelShader(PS_Alpha.data(), PS_Alpha.length(), nullptr, &pPS_Alpha);
}
devContext->PSSetShader(pPS_Alpha, nullptr, 0);
devContext->PSSetShaderResources(0, 1, &textureView);
devContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
devContext->Draw(6, 0);
}
OnDraw() to actually draw a texture
HRESULT VDJ_API OnDraw() override
{
std::lock_guard<std::mutex> ATS(threadSync);
ID3D11Device* d3dDev = nullptr; //GetDevice doesn't AddRef, so doesn't need to be released
if (GetDevice(VdjVideoEngineDirectX11, (void**)&d3dDev)!=S_OK)
return E_FAIL;
if (!devContext)
{
d3dDev->GetImmediateContext(&devContext.p);
if (!devContext)
return E_FAIL;
}
if (curTextureView)
bitblt(d3dDev, curTextureView);
return S_OK;
}
Creating a texture with data in memory:
CComPtr<ID3D11Device> d3dDev;
{
std::lock_guard<std::mutex> ATS(threadSync);
if (devContext)
devContext->GetDevice(&d3dDev.p);
}
if (d3dDev)
{
D3D11_SUBRESOURCE_DATA frameData{ 0 };
frameData.pSysMem = video_frame.p_data;
frameData.SysMemPitch = video_frame.line_stride_in_bytes;
frameData.SysMemSlicePitch = video_frame.line_stride_in_bytes * video_frame.yres;
CComPtr<ID3D11Texture2D> newTexture;
CComPtr<ID3D11ShaderResourceView> newView;
newTexture.Attach(createTexture(d3dDev.p, video_frame.xres, video_frame.yres, DXGI_FORMAT_B8G8R8A8_UNORM, &frameData, &newView.p));
if (newTexture)
{
std::lock_guard<std::mutex> ATS(threadSync);
curTexture = newTexture;
curTextureView = newView;
}
}
Would indeed be useful to make a clean example from this, but I think this should get you started.
Posted Tue 09 Jan 24 @ 9:30 am
Thanks! This is much more complex than the audio DSP processing haha. i wish there was a way to use a CopyResource function call on a texture instead. but I will get to working on this.
Posted Tue 09 Jan 24 @ 5:23 pm
DirectX 11 indeed requires a bit of code to get started, but once you get through the basics it's not too bad
Posted Tue 09 Jan 24 @ 6:23 pm
In this case it appears data is being loaded from CPU. In my case I have access to a DX11 texture source that i need to copy to VDJ. Would this still work?
Posted Wed 10 Jan 24 @ 12:52 am
Also the initImageSize function, do you have a declaraion for what that does.
Posted Wed 10 Jan 24 @ 1:32 am
How to use an existing texture depends on how it was created and how it was stored.
If it is suitable to create a view from, you could just use CreateShaderResourceView on the texture you have and use that for drawing.
Otherwise CopyResource could indeed work, using CreateTexture2D first without passing any data.
Best to check the documentation on the specific requirements in that case
https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-copyresource
If it is suitable to create a view from, you could just use CreateShaderResourceView on the texture you have and use that for drawing.
Otherwise CopyResource could indeed work, using CreateTexture2D first without passing any data.
Best to check the documentation on the specific requirements in that case
https://learn.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-copyresource
Posted Wed 10 Jan 24 @ 4:49 am
initImageSize is just a helper to allow for crop/zoom/stretch to be handled easier
void initImageSize(int *srcX, int *srcY, int *srcWidth, int *srcHeight, float srcAr, int srcOrientation, int width, int height, int* dstX, int* dstY, int* dstWidth, int* dstHeight)
{
int letterBoxing = 0;
*dstWidth = width;
*dstHeight= height;
*dstX = 0;
*dstY = 0;
if (letterBoxing==2) //zoom (stretch)
return;
if (srcWidth==0 || srcHeight==0)
return;
if (srcOrientation>=6&&srcOrientation<=8)
{
*srcWidth = *srcHeight;
*srcHeight = *srcWidth;
}
if (letterBoxing==0) //borders (scale)
{
*dstWidth = (int)(height * *srcWidth*srcAr / *srcHeight);
*dstHeight= height;
if (*dstWidth > width)
{
*dstWidth = width;
*dstHeight= (int)(width * *srcHeight / (*srcWidth*srcAr));
}
*dstX = (width - *dstWidth)/2;
*dstY = (height- *dstHeight)/2;
}
else if (letterBoxing==1) //crop
{
*dstWidth = (int)(height * *srcWidth*srcAr / *srcHeight);
*dstHeight= height;
if (*dstWidth < width)
{
*dstWidth = width;
*dstHeight= (int)(width * *srcHeight / (*srcWidth*srcAr));
}
*dstX = (width - *dstWidth)/2;
*dstY = (height- *dstHeight)/2;
//TODO: Is this correct in combination with rotation?
if (*dstX<0)
{
*srcX = -(*dstX * *srcWidth / *dstWidth);
*srcWidth -= 2 * *srcX;
*dstWidth -= *dstX*2;
*dstX = 0;
}
if (*dstY<0)
{
*srcY = -(*dstY * *srcHeight / *dstHeight);
*srcHeight -= 2 * *srcY;
*dstHeight -= *dstY*2;
*dstY = 0;
}
}
}
Posted Wed 10 Jan 24 @ 4:51 am
Thanks, BTW you can see the progress of the plugin here. https://github.com/medcelerate/VDJTouchEngine
Posted Thu 11 Jan 24 @ 2:08 am
Well, I'm closer, getting half of the screen black diagonally.
Posted Thu 11 Jan 24 @ 2:24 am
Any recommendations on debugging what could be preventing the texture from passing through and the diagonal black split on the screen? I'm guessing it has to do with the vertices not being correct, but I've copied bit for bit pretty much what you supplied.
Posted Sun 14 Jan 24 @ 10:16 pm
I think it's indeed vertices too.
Drawing is based on 2 triangles ("D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST") and the index of the vertices is important. (Clockwise or counterclockwise)
Can you post a screenshot so we can see it?
Drawing is based on 2 triangles ("D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST") and the index of the vertices is important. (Clockwise or counterclockwise)
Can you post a screenshot so we can see it?
Posted Mon 15 Jan 24 @ 4:54 pm
Here is the image and here is the link to the current iteration of the code. https://github.com/medcelerate/VDJTouchEngine/blob/0e38fcea10ebd81f23bee3611e9e8aaf5e355482/src/VDJTouchEngine.cpp#L979-L1024
Posted Mon 15 Jan 24 @ 8:42 pm
Adion's code should be ok but here is another way to write the vertices
struct D3DPOSITION
{
float x;
float y;
float z;
};
struct D3DTEXCOORD
{
float tu;
float tv;
};
struct D3DXCOLOR
{
public:
D3DXCOLOR() = default;
D3DXCOLOR(FLOAT r, FLOAT g, FLOAT b, FLOAT a)
{
this->r = r;
this->g = g;
this->b = b;
this->a = a;
}
operator FLOAT* ()
{
return &r;
}
FLOAT r, g, b, a;
};
struct TLVERTEX
{
D3DPOSITION position;
D3DXCOLOR color;
D3DTEXCOORD textcoord;
};
TLVERTEX pNewVertices[6];
void CMyPlugin::UpdateVertices_D3D11()
{
pos_x = Xo * width; // 0
pos_y = Yo * height; // 0
pos_width =(Xmax - Xo) * width; // width
pos_height = (Ymax - Yo) * height; // height
D3DPOSITION P1 = { pos_x , pos_y , 0.0f }, // Top Left
P2 = { pos_x , pos_height + pos_y * 2 , 0.0f }, // Bottom Left
P3 = { pos_width + pos_x * 2 , pos_y , 0.0f }, // Top Right
P4 = { pos_width + pos_x * 2 , pos_height + pos_y * 2 , 0.0f }; // Bottom Right
D3DXCOLOR color_vertex = D3DXCOLOR(1.0f, 1.0f, 1.0f, alpha);
D3DTEXCOORD T1 = { 0.0f , 0.0f }, T2 = { 0.0f , 1.0f }, T3 = { 1.0f , 0.0f }, T4 = { 1.0f , 1.0f };
// Triangle n°1 (Bottom Right)
pNewVertices[0] = { P3 , color_vertex , T3 };
pNewVertices[1] = { P4 , color_vertex , T4 };
pNewVertices[2] = { P2 , color_vertex , T2 };
// Triangle n°2 (Top Left)
pNewVertices[3] = { P2 , color_vertex , T2 };
pNewVertices[4] = { P1 , color_vertex , T1 };
pNewVertices[5] = { P3 , color_vertex , T3 };
}
Posted Sun 21 Jan 24 @ 8:02 pm