Modify a IDirect3DSurface9* with shader

532 Views Asked by At

I got a IDirect3DSurface9 * from a DXVA2 video decoder. I'd like to modify that surface with a shader. I' m able to render the video frames without shader by "drawing" the surface in the back buffer. I use the following code to render the frames without shader:

void Draw(Example* exps){

    IDirect3DSurface9* backbuffer;
    hwctx->d3d9device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    hwctx->d3d9device->BeginScene();
    hwctx->swap_chain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);

    hwctx->d3d9device->StretchRect(videoSurface, NULL, backbuffer, NULL, D3DTEXF_LINEAR);

    hwctx->d3d9device->EndScene();
    hwctx->swap_chain->Present(0, 0, 0, 0, 0);
    backbuffer->Release();
}

Up until here, everithing works. I would modify the Draw function to render video frames with the following shader:

uniform extern float4x4 gWVP;
uniform extern texture  gTexRGB;
uniform extern texture  gTexAlpha;

sampler TexRGB = sampler_state
{
    Texture = <gTexRGB>;
    AddressU  = WRAP;
    AddressV  = WRAP;
};

sampler TexAlpha = sampler_state
{
    Texture = <gTexAlpha>;
    AddressU = WRAP;
    AddressV = WRAP;
};

struct OutputVS
{
    float4 posH    : POSITION0;
    float2 tex0    : TEXCOORD0;
};

OutputVS DirLightTexVS(float3 posL : POSITION0, float3 normalL : NORMAL0, float2 tex0: TEXCOORD0)
{
    // Zero out our output.
    OutputVS outVS = (OutputVS)0;

    // Transform to homogeneous clip space.
    outVS.posH = mul(float4(posL, 1.0f), gWVP);

    // Pass on texture coordinates to be interpolated in rasterization.
    outVS.tex0 = tex0;

    // Done--return the output.
    return outVS;
}

float4 DirLightTexPS(float4 c : COLOR0, float4 spec : COLOR1, float2 tex0 : TEXCOORD0) : COLOR
{
    float3  rgb = tex2D(TexRGB, tex0).rgb;
    float   alpha = tex2D(TexAlpha, tex0).g;
    return float4(rgb, alpha);
}

technique DirLightTexTech
{
    pass P0
    {
        // Specify the vertex and pixel shader associated with this pass.
        vertexShader = compile vs_2_0 DirLightTexVS();
        pixelShader  = compile ps_2_0 DirLightTexPS();

    }
}

where TexRGB is the texture associated to the video frame, while TexAlpha is another texture that contains alpha values. How can I pass the decoded surface to the shader? I never used Directx9 so an example is appreciated and could help me to solve the problem.

UPDATE 1: I created the InitEffect function to load the effect from file

void InitEffect(Example* ctx) {
    auto hwctx = ctx->decoder->stream->HWAccelCtx;

    // Create the FX from a .fx file.
    ID3DXBuffer* errors = 0;
    D3DXCreateEffectFromFile(hwctx->d3d9device, "basicEffect.fx", 0, 0, D3DXSHADER_DEBUG, 0, &ctx->mFX, &errors);

    if (errors)
        MessageBox(0, (LPCSTR)errors->GetBufferPointer(), 0, 0);

    // Obtain handles.
    ctx->mhTech = ctx->mFX->GetTechniqueByName("DirLightTexTech");
    ctx->mhWVP = ctx->mFX->GetParameterByName(0, "gWVP");
    ctx->mhTexAlpha = ctx->mFX->GetParameterByName(0, "gTexAlpha");
    ctx->mhTexRGB = ctx->mFX->GetParameterByName(0, "gTexRGB");
}

and changed the rendering fuction to:

void Draw(Example* ctx) {
    InitMatrices(ctx);

    IDirect3DSurface9* backbuffer;

    hwctx->d3d9device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffeeeeee, 1.0f, 0);
    hwctx->d3d9device->BeginScene();

    ctx->mFX->SetTechnique(ctx->mhTech);
    ctx->mFX->SetMatrix(ctx->mhWVP, &(ctx->mCrateWorld*ctx->mView*ctx->mProj));

    ctx->texRGB->GetSurfaceLevel(0, &ctx->surfRGB);
    hwctx->d3d9device->SetRenderTarget(0, ctx->surfRGB);
    hwctx->d3d9device->StretchRect((IDirect3DSurface9*)s->frame->data[3], NULL, ctx->surfRGB, NULL, D3DTEXF_LINEAR);

    ctx->mFX->SetTexture(ctx->mhTexAlpha, ctx->texAlpha);
    ctx->mFX->SetTexture(ctx->mhTexRGB, ctx->texRGB);

    // Enable alpha blending.
    hwctx->d3d9device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
    hwctx->d3d9device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
    hwctx->d3d9device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

    hwctx->d3d9device->SetVertexDeclaration(VertexPNT::Decl);
    hwctx->d3d9device->SetStreamSource(0, ctx->mBoxVB, 0, sizeof(VertexPNT));
    hwctx->d3d9device->SetIndices(ctx->mBoxIB);

    UINT numPasses = 0;
    ctx->mFX->Begin(&numPasses, 0);
    for (UINT i = 0; i < numPasses; ++i){
        ctx->mFX->BeginPass(i);
        hwctx->d3d9device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 24, 0, 12);
        ctx->mFX->EndPass();
    }
    ctx->mFX->End();

    hwctx->d3d9device->EndScene();
    hwctx->swap_chain->Present(0, 0, 0, 0, 0);

    backbuffer->Release();

    // Disable alpha blending.
    hwctx->d3d9device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
}

but it still doesn't work.

UPDATE 2 I modified the code by following the code Asesh shared. In the InitEffect function I added the following lines to create a render target surface:

ctx->texRGB->GetSurfaceLevel(0, &ctx->surfRGB);
// store orginal rendertarget
hwctx->d3d9device->GetRenderTarget(0, &ctx->origTarget_);
D3DSURFACE_DESC desc;
ctx->origTarget_->GetDesc(&desc);

// create our surface as render target
hwctx->d3d9device->CreateRenderTarget(1920, 1080, D3DFMT_X8R8G8B8,
    desc.MultiSampleType, desc.MultiSampleQuality,
    false, &ctx->surfRGB, NULL);

the draw function is:

void drawScene(Example* ctx) {
    InitMatrices(ctx);

    auto hwctx = ctx->decoder->stream->HWAccelCtx;
    auto s = (VdrStreamContext*)ctx->decoder->stream->vdrCodecCtx->opaque;

    IDirect3DSurface9* backbuffer;
    hwctx->d3d9device->SetRenderTarget(0, ctx->surfRGB);
    hwctx->d3d9device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffeeeeee, 1.0f, 0);
    hwctx->d3d9device->BeginScene();


    hwctx->d3d9device->StretchRect((IDirect3DSurface9*)s->vdrFrame->data[3], NULL, ctx->surfRGB, NULL, D3DTEXF_NONE);
    hwctx->d3d9device->SetRenderTarget(0, ctx->origTarget_);

    if (!hwctx->d3d9device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer)) {
        hwctx->d3d9device->StretchRect(ctx->surfRGB, NULL, backbuffer, NULL, D3DTEXF_NONE);
    }

    ctx->mFX->SetTechnique(ctx->mhTech);
    ctx->mFX->SetMatrix(ctx->mhWVP, &(ctx->mCrateWorld*ctx->mView*ctx->mProj));

    ctx->mFX->SetTexture(ctx->mhTexAlpha, ctx->texAlpha);
    ctx->mFX->SetTexture(ctx->mhTexRGB, ctx->texRGB);

    // Enable alpha blending.
    hwctx->d3d9device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
    hwctx->d3d9device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
    hwctx->d3d9device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

    hwctx->d3d9device->SetVertexDeclaration(VertexPNT::Decl);
    hwctx->d3d9device->SetStreamSource(0, ctx->mBoxVB, 0, sizeof(VertexPNT));
    hwctx->d3d9device->SetIndices(ctx->mBoxIB);

    UINT numPasses = 0;
    ctx->mFX->Begin(&numPasses, 0);
    for (UINT i = 0; i < numPasses; ++i){
        ctx->mFX->BeginPass(i);
        hwctx->d3d9device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 24, 0, 12);
        ctx->mFX->EndPass();
    }

    ctx->mFX->End();

    hwctx->d3d9device->EndScene();
    hwctx->d3d9device->Present(0, 0, 0, 0);

    backbuffer->Release();

    // Disable alpha blending.
    hwctx->d3d9device->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
}

by drawing in the backbuffer by hwctx->d3d9device->StretchRect(ctx->surfRGB, NULL, backbuffer, NULL, D3DTEXF_NONE); even if the ctx->surfRGB is associated to the texture passed to the shader, the video frame is showed on the screen but alpha blending is not applied. If I remove hwctx->d3d9device->StretchRect(ctx->surfRGB, NULL, backbuffer, NULL, D3DTEXF_NONE); video frame is not showed even if the ctx->surfRGB is not empty.

0

There are 0 best solutions below