RHI: Add explicit RenderPass abstraction (BeginRenderPass/EndRenderPass)

New abstractions:
- RHIFramebuffer: Framebuffer interface with Initialize/Bind/GetHandle
- RHIRenderPass: RenderPass interface with AttachmentDesc for load/store actions
- D3D12Framebuffer/D3D12RenderPass: D3D12 implementation
- OpenGLFramebuffer/OpenGLRenderPass: OpenGL implementation (adapted from existing)

RHICommandList changes:
- Added BeginRenderPass(RHIRenderPass*, RHIFramebuffer*, Rect, clearValues...)
- Added EndRenderPass()

Implementation notes:
- D3D12: Uses OMSetRenderTargets + ClearRenderTargetView (fallback from native RenderPass API)
- OpenGL: Uses glBindFramebuffer + glClearBufferfv for LoadOp handling
- Old SetRenderTargets/ClearRenderTarget retained for backward compatibility

All 845 tests pass.
This commit is contained in:
2026-03-24 23:59:44 +08:00
parent 1e88beacb8
commit 5eb731bc2d
15 changed files with 559 additions and 6 deletions

View File

@@ -3,6 +3,8 @@
#include "XCEngine/RHI/D3D12/D3D12PipelineState.h"
#include "XCEngine/RHI/D3D12/D3D12Shader.h"
#include "XCEngine/RHI/D3D12/D3D12PipelineLayout.h"
#include "XCEngine/RHI/D3D12/D3D12RenderPass.h"
#include "XCEngine/RHI/D3D12/D3D12Framebuffer.h"
namespace XCEngine {
namespace RHI {
@@ -245,6 +247,52 @@ void D3D12CommandList::AliasBarrierInternal(ID3D12Resource* beforeResource, ID3D
m_commandList->ResourceBarrier(1, &barrier);
}
void D3D12CommandList::BeginRenderPass(RHIRenderPass* renderPass, RHIFramebuffer* framebuffer,
const Rect& renderArea, uint32_t clearValueCount, const ClearValue* clearValues) {
(void)renderArea;
if (!framebuffer || !renderPass) return;
D3D12Framebuffer* d3d12Framebuffer = static_cast<D3D12Framebuffer*>(framebuffer);
uint32_t rtCount = d3d12Framebuffer->GetRenderTargetCount();
const D3D12_CPU_DESCRIPTOR_HANDLE* rtHandles = d3d12Framebuffer->GetRenderTargetHandles();
bool hasDS = d3d12Framebuffer->HasDepthStencil();
D3D12_CPU_DESCRIPTOR_HANDLE dsHandle = d3d12Framebuffer->GetDepthStencilHandle();
D3D12RenderPass* d3d12RenderPass = static_cast<D3D12RenderPass*>(renderPass);
const AttachmentDesc* attachments = d3d12RenderPass->GetColorAttachments();
for (uint32_t i = 0; i < rtCount; ++i) {
if (attachments[i].loadOp == LoadAction::Clear && clearValueCount > i) {
m_commandList->ClearRenderTargetView(rtHandles[i], &clearValues[i].color.r, 0, nullptr);
}
}
if (hasDS) {
const AttachmentDesc* dsAttachment = d3d12RenderPass->GetDepthStencilAttachment();
if (dsAttachment) {
uint32_t clearFlags = 0;
if (dsAttachment->loadOp == LoadAction::Clear) clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
if (dsAttachment->stencilLoadOp == LoadAction::Clear) clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
if (clearFlags && clearValueCount > rtCount) {
m_commandList->ClearDepthStencilView(dsHandle, static_cast<D3D12_CLEAR_FLAGS>(clearFlags), clearValues[rtCount].depth, clearValues[rtCount].stencil, 0, nullptr);
}
}
}
m_commandList->OMSetRenderTargets(rtCount, rtHandles, FALSE, hasDS ? &dsHandle : nullptr);
m_boundRenderTargets.clear();
for (uint32_t i = 0; i < rtCount; ++i) {
m_boundRenderTargets.push_back(rtHandles[i]);
}
m_boundDepthStencil = dsHandle;
m_depthStencilBound = hasDS;
}
void D3D12CommandList::EndRenderPass() {
}
void D3D12CommandList::SetPipelineStateInternal(ID3D12PipelineState* pso) {
m_commandList->SetPipelineState(pso);
m_currentPipelineState = pso;