Extra2D/src/renderer/command_queue.cpp

554 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <algorithm>
#include <cstring>
#include <vector>
#include <renderer/command_queue.h>
#include <renderer/material.h>
#include <renderer/mesh.h>
#include <renderer/rhi_module.h>
#include <renderer/rhi/opengl/gl_command_list.h>
#include <renderer/rhi/opengl/gl_texture.h>
#include <types/math/transform.h>
#include <types/ptr/intrusive_ptr.h>
#include <utils/logger.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
namespace extra2d {
// ========================================
// CommandSorter 实现
// ========================================
CommandSorter::CommandSorter() {
// 预分配初始容量
commands_.reserve(INITIAL_CAPACITY);
sortedIndices_.reserve(INITIAL_CAPACITY);
}
CommandSorter::~CommandSorter() = default;
CommandSorter::CommandSorter(CommandSorter&& other) noexcept
: commands_(std::move(other.commands_)),
sortedIndices_(std::move(other.sortedIndices_)),
commandCount_(other.commandCount_) {
other.commandCount_ = 0;
}
CommandSorter& CommandSorter::operator=(CommandSorter&& other) noexcept {
if (this != &other) {
commands_ = std::move(other.commands_);
sortedIndices_ = std::move(other.sortedIndices_);
commandCount_ = other.commandCount_;
other.commandCount_ = 0;
}
return *this;
}
uint32_t CommandSorter::addCommand(const DrawCommand &cmd) {
uint32_t index = commandCount_;
// 如果缓冲区不够大,扩展它
if (index >= commands_.size()) {
commands_.push_back(cmd);
sortedIndices_.push_back(index);
} else {
commands_[index] = cmd;
sortedIndices_[index] = index;
}
commandCount_++;
return index;
}
void CommandSorter::sort() {
if (commandCount_ == 0) return;
// 只排序有效范围的索引
auto indicesBegin = sortedIndices_.begin();
auto indicesEnd = sortedIndices_.begin() + commandCount_;
// 使用稳定排序保持相同键的命令顺序
std::stable_sort(indicesBegin, indicesEnd,
[this](uint32_t a, uint32_t b) {
return commands_[a].key < commands_[b].key;
});
}
void CommandSorter::clear() {
// 重置计数器,保留内存
commandCount_ = 0;
}
void CommandSorter::reserve(uint32_t capacity) {
commands_.reserve(capacity);
sortedIndices_.reserve(capacity);
}
// ========================================
// CommandBatcher 实现
// ========================================
void CommandBatcher::process(const CommandSorter &sorter) {
clear();
sorter_ = &sorter;
if (sorter.getCount() == 0) {
return;
}
CommandBatch currentBatch;
currentBatch.startIndex = 0;
currentBatch.count = 1;
const auto &firstCmd = sorter.getCommand(0);
currentBatch.pipeline = firstCmd.pipeline;
currentBatch.textureCount = firstCmd.textureCount;
for (uint32_t i = 0; i < firstCmd.textureCount; ++i) {
currentBatch.textures[i] = firstCmd.textures[i];
}
// 遍历所有命令,合并兼容的命令
for (uint32_t i = 1; i < sorter.getCount(); ++i) {
const auto &cmd = sorter.getCommand(i);
if (currentBatch.isCompatibleWith(cmd)) {
// 兼容,加入当前批次
currentBatch.count++;
} else {
// 不兼容,保存当前批次并创建新批次
batches_.push_back(currentBatch);
currentBatch.startIndex = i;
currentBatch.count = 1;
currentBatch.pipeline = cmd.pipeline;
currentBatch.textureCount = cmd.textureCount;
for (uint32_t j = 0; j < cmd.textureCount; ++j) {
currentBatch.textures[j] = cmd.textures[j];
}
}
}
// 保存最后一个批次
batches_.push_back(currentBatch);
}
const DrawCommand &CommandBatcher::getCommand(uint32_t batchIndex,
uint32_t cmdIndex) const {
const auto &batch = batches_[batchIndex];
return sorter_->getCommand(batch.startIndex + cmdIndex);
}
void CommandBatcher::clear() {
batches_.clear();
sorter_ = nullptr;
}
// ========================================
// CommandQueue 实现
// ========================================
CommandQueue::CommandQueue() = default;
CommandQueue::~CommandQueue() { shutdown(); }
CommandQueue::CommandQueue(CommandQueue &&other) noexcept
: sorter_(std::move(other.sorter_)), batcher_(std::move(other.batcher_)),
context_(other.context_), commandList_(std::move(other.commandList_)),
uboManager_(std::move(other.uboManager_)),
globalUBOData_(other.globalUBOData_),
materialUBOData_(std::move(other.materialUBOData_)),
nextMaterialId_(other.nextMaterialId_),
materialIds_(std::move(other.materialIds_)),
currentMaterialUBO_(other.currentMaterialUBO_),
materialUBOBufferOffset_(other.materialUBOBufferOffset_) {
other.context_ = nullptr;
other.globalUBOData_ = {};
other.nextMaterialId_ = 1;
other.currentMaterialUBO_ = nullptr;
other.materialUBOBufferOffset_ = 0;
}
CommandQueue &CommandQueue::operator=(CommandQueue &&other) noexcept {
if (this != &other) {
shutdown();
sorter_ = std::move(other.sorter_);
batcher_ = std::move(other.batcher_);
context_ = other.context_;
commandList_ = std::move(other.commandList_);
uboManager_ = std::move(other.uboManager_);
globalUBOData_ = other.globalUBOData_;
materialUBOData_ = std::move(other.materialUBOData_);
nextMaterialId_ = other.nextMaterialId_;
materialIds_ = std::move(other.materialIds_);
currentMaterialUBO_ = other.currentMaterialUBO_;
materialUBOBufferOffset_ = other.materialUBOBufferOffset_;
other.context_ = nullptr;
other.globalUBOData_ = {};
other.nextMaterialId_ = 1;
other.currentMaterialUBO_ = nullptr;
other.materialUBOBufferOffset_ = 0;
}
return *this;
}
bool CommandQueue::initialize() {
// 获取 RHI 模块
auto *rhiModule = RHIModule::get();
if (!rhiModule) {
E2D_LOG_ERROR("RHIModule not available");
return false;
}
auto *device = rhiModule->getDevice();
if (!device) {
E2D_LOG_ERROR("RHIDevice not available");
return false;
}
context_ = rhiModule->getContext();
if (!context_) {
E2D_LOG_ERROR("RHIContext not available");
return false;
}
// 创建命令列表
commandList_ = device->createCommandList();
if (!commandList_) {
E2D_LOG_ERROR("Failed to create command list");
return false;
}
// 初始化 UBO 管理器
uboManager_ = std::make_unique<UniformBufferManager>();
if (!uboManager_->initialize()) {
E2D_LOG_ERROR("Failed to initialize UniformBufferManager");
return false;
}
// 预分配材质 UBO 数据缓冲区
materialUBOData_.reserve(1024 * 1024); // 1MB
E2D_LOG_INFO("CommandQueue initialized");
return true;
}
void CommandQueue::shutdown() {
uboManager_.reset();
commandList_.reset();
context_ = nullptr;
materialUBOData_.clear();
E2D_LOG_INFO("CommandQueue shutdown");
}
void CommandQueue::beginFrame() {
sorter_.clear();
batcher_.clear();
materialIds_.clear();
nextMaterialId_ = 1;
// 重置材质 UBO 缓冲区(保留容量,避免重新分配)
materialUBOBufferOffset_ = 0;
currentMaterialUBO_ = nullptr;
// 重置 UBO 管理器的材质 UBO 池
if (uboManager_) {
uboManager_->resetMaterialUBOs();
}
// 重置统计
stats_ = {};
// 开始录制命令
if (commandList_) {
commandList_->begin();
}
}
void CommandQueue::endFrame() {
// 结束录制
if (commandList_) {
commandList_->end();
}
}
uint32_t CommandQueue::getMaterialId(Material *material) {
auto it = materialIds_.find(material);
if (it != materialIds_.end()) {
return it->second;
}
uint32_t id = nextMaterialId_++;
materialIds_[material] = id;
return id;
}
std::pair<UniformBuffer*, uint32_t> CommandQueue::allocateMaterialUBO(uint32_t size) {
if (!uboManager_ || size == 0) {
return {nullptr, 0};
}
// OpenGL 要求 UBO 偏移对齐到 GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT (通常是 256)
static constexpr uint32_t UBO_ALIGNMENT = 256;
// 使用批量更新机制:先分配到 CPU 缓冲区
uint32_t offset = materialUBOBufferOffset_;
// 对齐到 UBO_ALIGNMENT 字节
offset = (offset + UBO_ALIGNMENT - 1) & ~(UBO_ALIGNMENT - 1);
uint32_t alignedSize = (size + UBO_ALIGNMENT - 1) & ~(UBO_ALIGNMENT - 1);
// 检查 CPU 缓冲区是否有足够空间
if (offset + alignedSize > materialUBOData_.capacity()) {
// 如果缓冲区已满,先刷新到 GPU
flushMaterialUBOToGPU();
offset = 0; // 重置偏移量
}
// 确保缓冲区足够大
if (offset + alignedSize > materialUBOData_.size()) {
materialUBOData_.resize(offset + alignedSize);
}
materialUBOBufferOffset_ = offset + alignedSize;
return {nullptr, offset}; // 返回 nullptr 表示使用 CPU 缓冲区offset 是 CPU 缓冲区偏移
}
void CommandQueue::flushMaterialUBOToGPU() {
if (materialUBOBufferOffset_ == 0) {
return;
}
// 获取一个足够大的 UBO
currentMaterialUBO_ = uboManager_->acquireMaterialUBO(materialUBOBufferOffset_);
if (!currentMaterialUBO_) {
E2D_LOG_ERROR("Failed to acquire material UBO for flush");
return;
}
// 批量更新到 GPU
currentMaterialUBO_->update(materialUBOData_.data(), materialUBOBufferOffset_, 0);
// 重置 CPU 缓冲区偏移
materialUBOBufferOffset_ = 0;
}
UniformBuffer* CommandQueue::getCurrentMaterialUBO() const {
return currentMaterialUBO_;
}
void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
const struct Transform &transform,
const Color &color) {
if (!material || !mesh || !material->getShader()) {
return;
}
DrawCommand cmd;
// 构建排序键
uint32_t materialId = getMaterialId(material.get());
cmd.key = DrawKey::make(materialId, 0, 0);
// 设置管线
cmd.pipeline = material->getPipeline();
// 设置网格数据
cmd.vertexBuffer = mesh->getVertexBuffer();
cmd.indexBuffer = mesh->getIndexBuffer();
cmd.vertexCount = mesh->getVertexCount();
cmd.indexCount = mesh->getIndexCount();
// 设置变换和颜色
float matrixData[16];
transform.toMatrix(matrixData);
cmd.modelMatrix = glm::make_mat4(matrixData);
cmd.color = color;
// 设置纹理
const auto &textures = material->getTextures();
cmd.textureCount =
static_cast<uint32_t>(std::min(textures.size(), size_t(8)));
for (uint32_t i = 0; i < cmd.textureCount; ++i) {
if (textures[i].texture) {
cmd.textures[i] = textures[i].texture->getHandle();
}
}
// 分配材质 UBO 空间(使用 CPU 缓冲区)
uint32_t materialDataSize = material->getDataSize();
if (materialDataSize > 0) {
auto [ubo, offset] = allocateMaterialUBO(materialDataSize);
(void)ubo; // ubo 为 nullptr使用 CPU 缓冲区
// 复制材质数据到 CPU 缓冲区,并修改颜色
if (offset + materialDataSize <= materialUBOData_.size()) {
std::memcpy(materialUBOData_.data() + offset, material->getData(), materialDataSize);
// 将实例颜色应用到 UBO 数据中的 uColor 参数
auto layout = material->getLayout();
if (layout) {
const auto* param = layout->getParam("uColor");
if (param && param->type == MaterialParamType::Color) {
std::memcpy(materialUBOData_.data() + offset + param->offset, &color.r, sizeof(float) * 4);
}
}
cmd.materialUBOOffset = offset;
cmd.materialUBOSize = materialDataSize;
}
}
sorter_.addCommand(cmd);
}
void CommandQueue::submitClear(const Color &color, uint32_t flags) {
// 清除命令直接执行,不加入排序队列
if (!commandList_) {
return;
}
commandList_->clear(static_cast<ClearFlags>(flags), color, 1.0f, 0);
}
void CommandQueue::setViewport(int32_t x, int32_t y, int32_t width,
int32_t height) {
if (!commandList_) {
return;
}
Viewport viewport;
viewport.x = static_cast<float>(x);
viewport.y = static_cast<float>(y);
viewport.width = static_cast<float>(width);
viewport.height = static_cast<float>(height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
commandList_->setViewport(viewport);
}
void CommandQueue::updateGlobalUBO(const Mat4& viewProjection, float deltaTime,
uint32_t screenWidth, uint32_t screenHeight, uint32_t frameIndex) {
if (!uboManager_) {
E2D_LOG_WARN("CommandQueue::updateGlobalUBO: uboManager is null");
return;
}
// 填充全局 UBO 数据
std::memcpy(globalUBOData_.viewProjection, glm::value_ptr(viewProjection), sizeof(float) * 16);
globalUBOData_.cameraPosition[0] = 0.0f;
globalUBOData_.cameraPosition[1] = 0.0f;
globalUBOData_.cameraPosition[2] = 0.0f;
globalUBOData_.cameraPosition[3] = 1.0f;
globalUBOData_.time = 0.0f; // TODO: 传递实际时间
globalUBOData_.deltaTime = deltaTime;
globalUBOData_.screenSize[0] = static_cast<float>(screenWidth);
globalUBOData_.screenSize[1] = static_cast<float>(screenHeight);
// 使用双缓冲更新 UBO
uboManager_->updateGlobalUBO(&globalUBOData_, sizeof(globalUBOData_), frameIndex);
}
void CommandQueue::execute(uint32_t frameIndex) {
if (!commandList_) {
E2D_LOG_ERROR("CommandQueue::execute: commandList is null");
return;
}
// 在排序前刷新材质 UBO 数据到 GPU
flushMaterialUBOToGPU();
// 排序命令
sorter_.sort();
// 批处理
batcher_.process(sorter_);
// 执行批次
for (uint32_t i = 0; i < batcher_.getBatchCount(); ++i) {
executeBatch(i, batcher_.getBatch(i), frameIndex);
}
// 提交命令到 GPU
commandList_->submit();
}
void CommandQueue::executeBatch(uint32_t batchIndex, const CommandBatch &batch, uint32_t frameIndex) {
if (!commandList_) {
return;
}
// 绑定管线
if (batch.pipeline.isValid()) {
commandList_->setPipeline(batch.pipeline.get());
stats_.pipelineBinds++;
} else {
E2D_LOG_WARN("Batch has no valid pipeline!");
}
// 绑定全局 UBO (binding = 0) - 使用双缓冲
if (uboManager_) {
UniformBuffer* globalUBO = uboManager_->getGlobalUBO(frameIndex);
if (globalUBO) {
commandList_->setUniformBuffer(0, globalUBO->getRHIBuffer());
stats_.bufferBinds++;
}
}
// 绑定纹理
if (batch.textureCount > 0) {
for (uint32_t i = 0; i < batch.textureCount; ++i) {
if (batch.textures[i].isValid()) {
commandList_->setTexture(i, batch.textures[i].get());
stats_.textureBinds++;
}
}
}
// 执行批次中的所有命令
for (uint32_t i = 0; i < batch.count; ++i) {
const auto &cmd = batcher_.getCommand(batchIndex, i);
// 绑定顶点缓冲区
if (cmd.vertexBuffer.isValid()) {
commandList_->setVertexBuffer(0, cmd.vertexBuffer.get(), 0);
stats_.bufferBinds++;
} else {
E2D_LOG_WARN("Draw command has no valid vertex buffer!");
}
// 绑定索引缓冲区(如果有)
if (cmd.isIndexed() && cmd.indexBuffer.isValid()) {
commandList_->setIndexBuffer(cmd.indexBuffer.get(), IndexType::UInt16, 0);
stats_.bufferBinds++;
}
// 绑定材质 UBO (binding = 1)
if (cmd.materialUBOSize > 0 && currentMaterialUBO_) {
commandList_->setUniformBuffer(1, currentMaterialUBO_->getRHIBuffer(), cmd.materialUBOOffset, cmd.materialUBOSize);
stats_.bufferBinds++;
}
// 设置模型矩阵
commandList_->setUniform("uModelMatrix", cmd.modelMatrix);
// 绘制
if (cmd.isIndexed()) {
commandList_->drawIndexed(cmd.indexCount, 0, 0, 1, 0);
stats_.drawCalls++;
stats_.triangles += cmd.indexCount / 3;
} else {
commandList_->draw(cmd.vertexCount, 0, 1, 0);
stats_.drawCalls++;
stats_.triangles += cmd.vertexCount / 3;
}
stats_.vertices += cmd.vertexCount;
}
}
} // namespace extra2d