LukasBanana / LLGL

Low Level Graphics Library (LLGL) is a thin abstraction layer for the modern graphics APIs OpenGL, Direct3D, Vulkan, and Metal
BSD 3-Clause "New" or "Revised" License
2.04k stars 134 forks source link

D3D11 RenderTarget is still bound on input #126

Closed st0rmbtw closed 1 month ago

st0rmbtw commented 1 month ago

Sometimes this warning shows up when I resize the window:

D3D11 WARNING: ID3D11DeviceContext::OMSetRenderTargets: Resource being set to OM RenderTarget slot 0 is still bound on input! [ STATE_SETTING WARNING #9: DEVICE_OMSETRENDERTARGETS_HAZARD]
D3D11 WARNING: ID3D11DeviceContext::OMSetRenderTargets[AndUnorderedAccessViews]: Forcing PS shader resource slot 6 to NULL. [ STATE_SETTING WARNING #7: DEVICE_PSSETSHADERRESOURCES_HAZARD]

I use the GLFW library for window handling.

Here's the rendering code for context:

LLGL::ClearValue clear_value = LLGL::ClearValue(0.0f, 0.0f, 0.0f, 0.0f);

commands->BeginRenderPass(*state.background_render_target);
    commands->Clear(LLGL::ClearFlags::Color, clear_value);
    state.background_renderer.render();
commands->EndRenderPass();

commands->BeginRenderPass(*state.world_render_target);
    commands->Clear(LLGL::ClearFlags::Color, clear_value);
    state.world_renderer.render(chunk_manager);
commands->EndRenderPass();

commands->BeginRenderPass(*Renderer::SwapChain(), Renderer::DefaultRenderPass());
    commands->SetVertexBuffer(*state.fullscreen_triangle_vertex_buffer);
    commands->SetPipelineState(*state.postprocess_pipeline);
    commands->SetResource(0, *state.constant_buffer);

    commands->SetResource(1, *state.background_render_texture);
    commands->SetResource(2, Assets::GetSampler(TextureSampler::Nearest));

    commands->SetResource(3, *state.world_render_texture);
    commands->SetResource(4, Assets::GetSampler(TextureSampler::Nearest));

    commands->SetResource(5, *state.lightmap_texture);
    commands->SetResource(6, Assets::GetSampler(TextureSampler::Nearest));
    commands->Draw(3, 0);
commands->EndRenderPass();
commands->End();
queue->Submit(*commands);

swap_chain->Present();

UPD: It happens after I resize the textures. To resize a texture, I have to release the previous one and create the new one with the new size, and then recreate render targets as well. Here's the resizing code:

const LLGL::RenderSystemPtr& context = state.context;

context->Release(*state.world_render_texture);
context->Release(*state.background_render_texture);

context->Release(*state.world_render_target);
context->Release(*state.background_render_target);

LLGL::TextureDescriptor texture_desc;
texture_desc.extent = LLGL::Extent3D(width, height, 1);
texture_desc.miscFlags = 0;
texture_desc.cpuAccessFlags = 0;

state.world_render_texture = context->CreateTexture(texture_desc);
state.background_render_texture = context->CreateTexture(texture_desc);

LLGL::RenderTargetDescriptor render_target_desc;
render_target_desc.resolution = resolution;
render_target_desc.colorAttachments[0] = state.world_render_texture;
state.world_render_target = context->CreateRenderTarget(render_target_desc);

LLGL::RenderTargetDescriptor background_target_desc;
background_target_desc.resolution = resolution;
background_target_desc.colorAttachments[0] = state.background_render_texture;
state.background_render_target = context->CreateRenderTarget(background_target_desc);
LukasBanana commented 1 month ago

Thanks for providing the sample code. Regarding this validation warning:

Forcing PS shader resource slot 6 to NULL.

Which of your shader resources is at slot 6? And does this warning only show up in the first frame after the resize or permanently?

st0rmbtw commented 1 month ago

Which of your shader resources is at slot 6?

Oh, I have a quite a lot of render targets, so to simplify the code sample I removed some. But the warning complains about random render target.

And does this warning only show up in the first frame after the resize or permanently?

Permanently, until I resize the window again.

LukasBanana commented 1 month ago

With all the resource testing in TestResourceBinding I completely forgot to test delete/recreate. But that should be a relatively easy fix. I see what I can do tonight or tomorrow.

If you need a workaround now, you could add the following to D3D11RenderSystem::Release(RenderTarget&) to clear the device context and state manager cache:

void D3D11RenderSystem::Release(RenderTarget& renderTarget)
{
    renderTargets_.erase(&renderTarget);
    stateMngr_->ClearState(); // FIXME - workaround to clear binding table cache after render target is deleted
}

Potentially the same for the Texture release function.

st0rmbtw commented 1 month ago

Hm. I added this line

stateMngr_->ClearState();

to release functions for Texture and RenderTarget, but it didn't help

LukasBanana commented 1 month ago

Does your render pipeline rely on deferred command buffers or can you also create your command buffer with the flag LLGL::CommandBufferFlags::ImmediateSubmit? Does the issue persist if you use an immediate context (including the workaround)?

st0rmbtw commented 1 month ago

Yes, the command buffer was deferred, I changed it to be immediate and it helped! No warnings now.

LukasBanana commented 1 month ago

I submitted a fix and updated the ResourceBinding unit test to incorporate delete/re-create operations on the test resources. The unit test still shows a lot of warnings of D3D resources being alive after D3D device is released, but that is a different issue I can take care of another time. You should be able to revert your workaround with this fix as it also handles deferred contexts, i.e. no need to switch to an immediate context.

st0rmbtw commented 1 month ago

Thank you!