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;

View File

@@ -0,0 +1,56 @@
#include "XCEngine/RHI/D3D12/D3D12Framebuffer.h"
#include "XCEngine/RHI/D3D12/D3D12RenderPass.h"
#include "XCEngine/RHI/D3D12/D3D12ResourceView.h"
namespace XCEngine {
namespace RHI {
D3D12Framebuffer::D3D12Framebuffer() {
}
D3D12Framebuffer::~D3D12Framebuffer() {
Shutdown();
}
void D3D12Framebuffer::Shutdown() {
m_renderTargetHandles.clear();
m_depthStencilHandle = {};
m_renderPass = nullptr;
m_width = 0;
m_height = 0;
}
bool D3D12Framebuffer::Initialize(D3D12RenderPass* renderPass, uint32_t width, uint32_t height,
uint32_t colorAttachmentCount, RHIResourceView** colorAttachments,
RHIResourceView* depthStencilAttachment) {
m_renderPass = renderPass;
m_width = width;
m_height = height;
m_renderTargetHandles.resize(colorAttachmentCount);
for (uint32_t i = 0; i < colorAttachmentCount; ++i) {
if (colorAttachments[i]) {
D3D12ResourceView* view = static_cast<D3D12ResourceView*>(colorAttachments[i]);
m_renderTargetHandles[i] = view->GetCPUHandle();
}
}
if (depthStencilAttachment) {
D3D12ResourceView* view = static_cast<D3D12ResourceView*>(depthStencilAttachment);
m_depthStencilHandle = view->GetCPUHandle();
} else {
m_depthStencilHandle = {};
}
return true;
}
D3D12_CPU_DESCRIPTOR_HANDLE D3D12Framebuffer::GetRenderTargetHandle(uint32_t index) const {
if (index >= m_renderTargetHandles.size()) {
return {};
}
return m_renderTargetHandles[index];
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -0,0 +1,103 @@
#include "XCEngine/RHI/D3D12/D3D12RenderPass.h"
#include "XCEngine/RHI/D3D12/D3D12Framebuffer.h"
namespace XCEngine {
namespace RHI {
D3D12RenderPass::D3D12RenderPass() {
}
D3D12RenderPass::~D3D12RenderPass() {
Shutdown();
}
void D3D12RenderPass::Shutdown() {
m_colorAttachments.clear();
m_colorAttachmentCount = 0;
m_hasDepthStencil = false;
}
bool D3D12RenderPass::Initialize(uint32_t colorAttachmentCount, const AttachmentDesc* colorAttachments,
const AttachmentDesc* depthStencilAttachment) {
m_colorAttachmentCount = colorAttachmentCount;
m_colorAttachments.resize(colorAttachmentCount);
for (uint32_t i = 0; i < colorAttachmentCount; ++i) {
m_colorAttachments[i] = colorAttachments[i];
}
if (depthStencilAttachment != nullptr) {
m_hasDepthStencil = true;
m_depthStencilAttachment = *depthStencilAttachment;
} else {
m_hasDepthStencil = false;
}
return true;
}
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE D3D12RenderPass::GetBeginningAccessType(uint32_t index) const {
if (index >= m_colorAttachmentCount) {
return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE::D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS;
}
switch (m_colorAttachments[index].loadOp) {
case LoadAction::Clear:
return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE::D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR;
case LoadAction::Load:
return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE::D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE;
case LoadAction::Undefined:
default:
return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE::D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD;
}
}
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE D3D12RenderPass::GetEndingAccessType(uint32_t index) const {
if (index >= m_colorAttachmentCount) {
return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE::D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS;
}
switch (m_colorAttachments[index].storeOp) {
case StoreAction::Store:
return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE::D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE;
case StoreAction::Discard:
return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE::D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD;
case StoreAction::Resolve:
return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE::D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_RESOLVE;
case StoreAction::StoreAndResolve:
return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE::D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_STORE_AND_RESOLVE;
case StoreAction::Undefined:
default:
return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE::D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS;
}
}
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE D3D12RenderPass::GetDepthBeginningAccessType() const {
if (!m_hasDepthStencil) {
return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE::D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS;
}
switch (m_depthStencilAttachment.loadOp) {
case LoadAction::Clear:
return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE::D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR;
case LoadAction::Load:
return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE::D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE;
case LoadAction::Undefined:
default:
return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE::D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD;
}
}
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE D3D12RenderPass::GetDepthEndingAccessType() const {
if (!m_hasDepthStencil) {
return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE::D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS;
}
switch (m_depthStencilAttachment.storeOp) {
case StoreAction::Store:
return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE::D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE;
case StoreAction::Discard:
return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE::D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD;
case StoreAction::Undefined:
default:
return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE::D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS;
}
}
} // namespace RHI
} // namespace XCEngine

View File

@@ -2,6 +2,8 @@
#include "XCEngine/RHI/OpenGL/OpenGLResourceView.h"
#include "XCEngine/RHI/OpenGL/OpenGLPipelineState.h"
#include "XCEngine/RHI/OpenGL/OpenGLShader.h"
#include "XCEngine/RHI/OpenGL/OpenGLFramebuffer.h"
#include "XCEngine/RHI/OpenGL/OpenGLRenderPass.h"
#include <glad/glad.h>
namespace XCEngine {
@@ -508,6 +510,45 @@ void OpenGLCommandList::TransitionBarrier(RHIResourceView* resource, ResourceSta
glMemoryBarrier(GL_ALL_BARRIER_BITS);
}
void OpenGLCommandList::BeginRenderPass(RHIRenderPass* renderPass, RHIFramebuffer* framebuffer,
const Rect& renderArea, uint32_t clearValueCount, const ClearValue* clearValues) {
(void)renderArea;
(void)clearValueCount;
(void)clearValues;
if (!framebuffer) return;
OpenGLFramebuffer* glFramebuffer = static_cast<OpenGLFramebuffer*>(framebuffer);
glFramebuffer->Bind();
OpenGLRenderPass* glRenderPass = static_cast<OpenGLRenderPass*>(renderPass);
if (glRenderPass) {
const AttachmentDesc* colorAttachments = glRenderPass->GetColorAttachments();
uint32_t colorCount = glRenderPass->GetColorAttachmentCount();
for (uint32_t i = 0; i < colorCount && i < clearValueCount; ++i) {
if (colorAttachments[i].loadOp == LoadAction::Clear) {
glClearBufferfv(GL_COLOR, i, &clearValues[i].color.r);
}
}
const AttachmentDesc* dsAttachment = glRenderPass->GetDepthStencilAttachment();
if (dsAttachment && clearValueCount > colorCount) {
if (dsAttachment->loadOp == LoadAction::Clear) {
glClearBufferfv(GL_DEPTH, 0, &clearValues[colorCount].depth);
}
if (dsAttachment->stencilLoadOp == LoadAction::Clear) {
float stencilValue = static_cast<float>(clearValues[colorCount].stencil);
glClearBufferfv(GL_STENCIL, 0, &stencilValue);
}
}
}
}
void OpenGLCommandList::EndRenderPass() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void OpenGLCommandList::SetViewport(const Viewport& viewport) {
glViewport(static_cast<int>(viewport.topLeftX), static_cast<int>(viewport.topLeftY),
static_cast<int>(viewport.width), static_cast<int>(viewport.height));

View File

@@ -1,4 +1,5 @@
#include "XCEngine/RHI/OpenGL/OpenGLFramebuffer.h"
#include "XCEngine/RHI/OpenGL/OpenGLResourceView.h"
#include <glad/glad.h>
namespace XCEngine {
@@ -97,6 +98,57 @@ void OpenGLFramebuffer::Shutdown() {
m_samples = 1;
}
bool OpenGLFramebuffer::Initialize(class RHIRenderPass* renderPass, uint32_t width, uint32_t height,
uint32_t colorAttachmentCount, RHIResourceView** colorAttachments,
RHIResourceView* depthStencilAttachment) {
(void)renderPass;
m_width = static_cast<int>(width);
m_height = static_cast<int>(height);
m_samples = 1;
glGenFramebuffers(1, &m_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
GLenum drawBuffers[16] = { GL_NONE };
int drawBufferCount = 0;
for (uint32_t i = 0; i < colorAttachmentCount && i < 16; ++i) {
if (colorAttachments[i]) {
OpenGLResourceView* view = static_cast<OpenGLResourceView*>(colorAttachments[i]);
GLenum glAttachment = GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(i);
unsigned int texture = view->GetTexture();
if (texture) {
glFramebufferTexture2D(GL_FRAMEBUFFER, glAttachment, GL_TEXTURE_2D, texture, 0);
}
drawBuffers[drawBufferCount++] = glAttachment;
}
}
if (depthStencilAttachment) {
OpenGLResourceView* view = static_cast<OpenGLResourceView*>(depthStencilAttachment);
unsigned int texture = view->GetTexture();
if (texture) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
}
}
if (drawBufferCount > 0) {
glDrawBuffers(drawBufferCount, drawBuffers);
} else {
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
}
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return true;
}
void OpenGLFramebuffer::Bind() {
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
}

View File

@@ -0,0 +1,38 @@
#include "XCEngine/RHI/OpenGL/OpenGLRenderPass.h"
namespace XCEngine {
namespace RHI {
OpenGLRenderPass::OpenGLRenderPass() {
}
OpenGLRenderPass::~OpenGLRenderPass() {
Shutdown();
}
void OpenGLRenderPass::Shutdown() {
m_colorAttachments.clear();
m_colorAttachmentCount = 0;
m_hasDepthStencil = false;
}
bool OpenGLRenderPass::Initialize(uint32_t colorAttachmentCount, const AttachmentDesc* colorAttachments,
const AttachmentDesc* depthStencilAttachment) {
m_colorAttachmentCount = colorAttachmentCount;
m_colorAttachments.resize(colorAttachmentCount);
for (uint32_t i = 0; i < colorAttachmentCount; ++i) {
m_colorAttachments[i] = colorAttachments[i];
}
if (depthStencilAttachment != nullptr) {
m_hasDepthStencil = true;
m_depthStencilAttachment = *depthStencilAttachment;
} else {
m_hasDepthStencil = false;
}
return true;
}
} // namespace RHI
} // namespace XCEngine